void ModbusMaster::stopped() { /* thread is deleted using a connection between thread->finished and thread->deleteLater */ _pThread = NULL; emit threadStopped(); }
bool ThreadBase::terminate(int signal) { if ( isRunning() ) { AX_Thread::kill(_handle, signal); threadStopped(); } return true; }
// Physics thread that controls the motion of all the spheres. void physicsThread(int threadNum) { int localFrame = 0; // Define the normals for each of the walls. static const Vector3 bottom(0, 1, 0); static const Vector3 left(1, 0, 0); static const Vector3 right(-1, 0, 0); static const Vector3 front(0, 0, -1); static const Vector3 back(0, 0, 1); // Initialize the starting values for the wall and sphere, spring and damping values. float wallSpringConst = UPPER_WALL_SPRING_CONST; float sphereSpringConst = UPPER_SPHERE_SPRING_CONST; float wallDampFactor = UPPER_WALL_DAMP_FACTOR; float sphereDampFactor = UPPER_SPHERE_DAMP_FACTOR; // The physics thread itself deals with reseting its frame counter. // It will do this once a second. float secondCheck = 0.0f; #if _USE_MT // If the physics function is being called as a thread, then we don't want it // to finish after one pass. int threadRunning = 0; while(programRunning) { // If the simulation needs to be paused but the thread is running, // stop. if(pauseSimulation && threadRunning) { threadRunning = 0; threadStopped(); } // If the simulation needs to be running but the thread is stopped, // start. else if(!pauseSimulation && !threadRunning) { threadRunning = 1; threadStarted(); } // If the thread isn't running at this time we can't go any further and // we'll just wait and check if the thread has started up next time the // thread is executing. if(!threadRunning) { boost::this_thread::yield(); continue; } #endif localFrame++; LARGE_INTEGER time; QueryPerformanceCounter(&time); // Get the time since this thread last executed and convert into // seconds (from milliseconds). float dt = (float)(((double)time.QuadPart - (double)updateTimes[threadNum].QuadPart) / freq); dt *= 0.001; // Adjust the spring and dampening values based on dt. // This helps when going to lower frame rates and the overlap between // the walls and spheres when using high frame rate values will cause // the spheres to gain energy. wallSpringConst = WALL_SPRING_GRAD * dt + WALL_SPRING_CONST; sphereSpringConst = SPHERE_SPRING_GRAD * dt + SPHERE_SPRING_CONST; wallDampFactor = WALL_DAMP_GRAD * dt + WALL_DAMP_CONST; sphereDampFactor = SPHERE_DAMP_GRAD * dt + SPHERE_DAMP_CONST; // As the gradients for the spring and dampening factors are negative, // we want to clamp the lower bounds of the values. Otherwise at low // framerates the values will be negative. if (wallSpringConst < LOWER_WALL_SPRING_CONST) wallSpringConst = LOWER_WALL_SPRING_CONST; if (sphereSpringConst < LOWER_SPHERE_SPRING_CONST) sphereSpringConst = LOWER_SPHERE_SPRING_CONST; if (wallDampFactor < LOWER_WALL_DAMP_FACTOR) wallDampFactor = LOWER_WALL_DAMP_FACTOR; if (sphereDampFactor < LOWER_SPHERE_DAMP_FACTOR) sphereDampFactor = LOWER_SPHERE_DAMP_FACTOR; // If this is the 1st thread, then we will deal with the user controlled // sphere here. int startSphere = threadNum; if(threadNum == 0) { // Skip calcuating the user sphere like a typical sphere. startSphere += numPhysicsThreads; // As the user controlled sphere only has a position and velocity // that is either zero or constant, we can easily calculate its new // position. if(numSpheres > 0) { spherePositions[0] += sphereData[0].m_velocity * dt; } } for(int i = startSphere; i < numSpheres; i += numPhysicsThreads) { // Calculate the radius of the sphere based on array index. float radius = (float)((i % 3) + 1) * 0.5f; float roomSize = ROOM_SIZE - radius; Vector3 forces(0); Vector3 &spherePosition = spherePositions[i]; SphereData &sphere = sphereData[i]; // Calculate the interim velocity. Vector3 halfVelo = sphere.m_velocity + (0.5f * sphere.m_acc * dt); spherePosition += halfVelo * dt; Vector3 tempVelocity = sphere.m_velocity + (sphere.m_acc * dt); float overlap; // As the % operator is fairly slow we can take advantage here // that we always know what the next value in the array is going // to be and manually determine the mod. int indexCounter = 0; for(int j = 0; j < numSpheres; j++) { // And since we don't want the actual mod value but mod + 1 // we don't worry about resetting to 0. indexCounter++; if(indexCounter > 3) indexCounter = 1; // We don't want a sphere to check if it's collided with itself. if(i == j) continue; SphereData &otherSphere = sphereData[j]; Vector3 toCentre = spherePosition - spherePositions[j]; // An unfortunately slow way of checking if the radius of the // other sphere should be the other spheres actual radius or // the user controlled spheres radius. float otherRadius; if(j == 0) { otherRadius = userSphereSize; } else { otherRadius = (float)(indexCounter) * 0.5f; } float combinedRadius = radius + otherRadius; float lengthSqrd = lengthSquared(toCentre); float combinedRadiusSqrd = combinedRadius * combinedRadius; // A check to see if the spheres have overlapped at all. float overlapSqrd = lengthSqrd - combinedRadiusSqrd; if(overlapSqrd < 0) { #if _SSE overlap = sqrt(lengthSqrd) - combinedRadius; // We want to let the vector normalize itself in SSE // because we can take advantage of the rsqrt instruction // which will be quicker than loading in a float value // and multiplying by the reciprocal. Vector3 tangent = normalize(toCentre); #else // Here we can take advantage that we've already calculated // the actual length value so that we have the overlap and // can use that value again to normalize the tangent. float len = sqrt(lengthSqrd); overlap = len - combinedRadius; Vector3 tangent = toCentre / len; #endif calcForce(forces, sphereSpringConst, sphereDampFactor, overlap, tangent, tempVelocity); } } // Calculate the overlap with the walls using the normal of the wall // and the walls distance from the origin. Should work best for SSE // as it should not require any checking of individual elements of // the vector. overlap = dot3(spherePosition, bottom) + roomSize; if(overlap < 0) { calcForce(forces, wallSpringConst, wallDampFactor, overlap, bottom, tempVelocity); } overlap = dot3(spherePosition, left) + roomSize; if(overlap < 0) { calcForce(forces, wallSpringConst, wallDampFactor, overlap, left, tempVelocity); } overlap = dot3(spherePosition, right) + roomSize; if(overlap < 0) { calcForce(forces, wallSpringConst, wallDampFactor, overlap, right, tempVelocity); } overlap = dot3(spherePosition, front) + roomSize; if(overlap < 0) { calcForce(forces, wallSpringConst, wallDampFactor, overlap, front, tempVelocity); } overlap = dot3(spherePosition, back) + roomSize; if(overlap < 0) { calcForce(forces, wallSpringConst, wallDampFactor, overlap, back, tempVelocity); } // Apply the accumulated forces to the acceleration. sphere.m_acc = forces / (radius * 2.0f); sphere.m_acc += GRAVITY; // Calculate the final velocity value from the interim velocity // and final acceleration. sphere.m_velocity = halfVelo + (0.5f * sphere.m_acc * dt); } // Determin if we need to reset out physics frame counter. secondCheck += dt; if(secondCheck > 1.0f) { physicsFrames[threadNum] = 1; secondCheck -= 1.0f; } else { physicsFrames[threadNum]++; } updateTimes[threadNum] = time; // If we're running the physics function as a thread function then we // need to keep it running, so this is the end of the while(programRunning) loop. #if _USE_MT } #endif }
void display(void) { glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glLoadIdentity(); if(displayError) { orthographicView(); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); txtInstructions.render((screenWidth - txtInstructions.getTextureWidth()) / 2.0f, (screenHeight - txtInstructions.getTextureHeight()) / 2.0f); glFlush(); return; } glPushMatrix(); // Setup the navigation glTranslated(0, 0, -zoom); glRotatef(camRotateX, 1, 0, 0); glRotatef(camRotateY, 0, 1, 0); glTranslated(0, ROOM_SIZE, 0); // Setup drawing for the scene. perspectiveView(); #if _USE_MT // If the render thread needs to wait for new spheres to be added. while(pauseSimulation) { boost::this_thread::yield(); } threadStarted(); #endif renderFrame++; renderFrameCounter++; glEnable(GL_DEPTH_TEST); glColor3f(1.0f, 1.0f, 1.0f); glUseProgram(g_program); glUniform3fv(g_programCameraPositionLocation, 1, g_cameraPosition); glUniform3fv(g_programLightDirectionLocation, 1, g_lightDirection); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, sphereMesh.indexBufferId); // We are using instanced rendering, however we cannot draw all the spheres at once. // So we have a maximum number of objects that can be rendered at once and we'll just // render that many at a time until we have less spheres than MAX_OBJECTS_PER_RENDER // left to render. for(int objectIndex = 0; objectIndex < numSpheres; objectIndex += MAX_OBJECTS_PER_RENDER) { int numObjects = MAX_OBJECTS_PER_RENDER; // Are we going to be rendering more spheres than we should? if (numObjects + objectIndex > numSpheres) { numObjects = numSpheres - objectIndex; } Vector3 *positionIndex = spherePositions + objectIndex; glUniform4fv(g_programObjectPositionLocation, numObjects, (float *)positionIndex); // Tell the shader the size of the user sphere. // Due to the way the shader renders multiple instances, it only knows the id // of the current instance it is rendering, which does not reflect the sphere's actual // id. // So we will set that the size of the userSphere only on the first instanced render // and all later renders it will be zero. glUniform1f(g_programUserSphereLocation, (objectIndex == 0) * userSphereSize); glDrawElementsInstancedEXT(GL_TRIANGLES, sphereMesh.numIndicies, GL_UNSIGNED_INT, 0, numObjects); } glUseProgram(0); #if _USE_MT threadStopped(); #endif glPopMatrix(); // Setup drawing for the interface. orthographicView(); char buff[64]; sprintf(buff, "FPS: %d", framesPerSecond); txtRenderFrames.setText(buff); sprintf(buff, "PPS: %.1f", physicsPerSecond); txtPhysicsFrames.setText(buff); glDisable(GL_DEPTH_TEST); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); float yPos = -2.0f; txtInstructions.render(10, yPos += 12.0f); txtInstructions2.render(10, yPos += 12.0f); txtNumThreads.render(10, yPos += 12.0f); txtNumSpheres.render(10, yPos += 12.0f); txtRenderFrames.render(10, yPos += 12.0f); txtPhysicsFrames.render(10, yPos += 12.0f); glDisable(GL_BLEND); glFlush(); }
void monitor_mountinfo::run() { m_mtoto = this ; connect( m_mtoto,SIGNAL( terminated() ),m_main,SLOT( threadStopped() ) ) ; connect( m_mtoto,SIGNAL( terminated() ),m_mtoto,SLOT( deleteLater() ) ) ; Task::FileHandle manage_fd ; int fd = manage_fd( open( "/proc/self/mountinfo",O_RDONLY ) ) ; if( fd == -1 ){ return ; }else{ m_running = true ; } struct pollfd monitor ; monitor.fd = fd ; monitor.events = POLLPRI ; auto _loop = [&](){ poll( &monitor,1,-1 ) ; return 1 ; } ; auto _unmountProperty = [&]( const QString& volume ){ Task * t = new Task() ; t->setDevice( volume ) ; connect( t,SIGNAL( volumeMiniProperties( volumeEntryProperties * ) ), m_babu,SLOT( volumeMiniProperties( volumeEntryProperties * ) ) ) ; connect( t,SIGNAL( volumeRemoved( QString ) ), m_babu,SLOT( volumeRemoved( QString ) ) ) ; t->start( Task::VolumeMiniProperties ) ; } ; auto _mountProperty = [&]( const QString& volume ){ Task * t = new Task() ; t->setDevice( volume ) ; connect( t,SIGNAL( volumeMiniProperties( volumeEntryProperties * ) ), m_babu,SLOT( volumeMiniProperties( volumeEntryProperties * ) ) ) ; t->start( Task::VolumeMiniProperties ) ; } ; QStringList oldMountList = Task::updateVolumeList() ; QStringList newMountList ; auto _volumeWasUnMounted = [&](){ return oldMountList.size() > newMountList.size() ; } ; auto _volumeWasMounted = [&](){ return oldMountList.size() < newMountList.size() ; } ; auto _unmountedVolume = [&]( const QString& e ){ return !newMountList.contains( e ) ; } ; auto _mountedVolume = [&]( const QString& e ){ return !oldMountList.contains( e ) ; } ; while( _loop() ){ newMountList = Task::updateVolumeList() ; if( _volumeWasUnMounted() ){ for( const auto& it : oldMountList ){ if( _unmountedVolume( it ) ){ _unmountProperty( it ) ; } } }else if( _volumeWasMounted() ){ for( const auto& it : newMountList ){ if( _mountedVolume( it ) ){ _mountProperty( it ) ; } } }else{ /* * mount/unmount just happened but volume count remain the same, * possible reason is because of a bind mount */ } oldMountList = newMountList ; } }
void TrainingThread::mainThreadFunction(){ //Flag that the core is running { boost::mutex::scoped_lock lock( mutex ); threadRunning = true; stopMainThread = false; trainingInProcess = false; startTraining = false; } //Flag that the core is now running emit threadStarted(); bool keepRunning = true; while( keepRunning ){ //Check to see if we should start a new training batch bool runTraining = false; { boost::mutex::scoped_lock lock( mutex ); runTraining = startTraining; if( startTraining ){ startTraining = false; trainingInProcess = true; } } if( runTraining ){ emit pipelineTrainingStarted(); //Start the training, the thread will pause here until the training has finished bool result = trainer.train(); //Flag that the training has stopped { boost::mutex::scoped_lock lock( mutex ); trainingInProcess = false; } //Emit the results if( result ){ emit newInfoMessage( "Pipeline trained" ); }else{ emit newHelpMessage( trainer.getLastErrorMessage() ); } GRT::GestureRecognitionPipeline tempPipeline; { boost::mutex::scoped_lock lock( mutex ); tempPipeline = trainer.getPipeline(); } emit pipelineUpdated( tempPipeline ); emit pipelineTrainingFinished( result ); } //Let the thread sleep so we don't kill the CPU boost::this_thread::sleep( boost::posix_time::milliseconds( DEFAULT_TRAINING_THREAD_SLEEP_TIME ) ); //Check to see if we should stop the thread { boost::mutex::scoped_lock lock( mutex ); if( stopMainThread ){ keepRunning = false; } } } //Flag that the core has stopped { boost::mutex::scoped_lock lock( mutex ); threadRunning = false; stopMainThread = false; trainingInProcess = false; startTraining = false; } //Signal that the core has stopped emit threadStopped(); }
void events::run() { m_running = true ; m_mtoto = this ; connect( m_mtoto,SIGNAL( finished() ),m_main,SLOT( threadStopped() ) ) ; connect( m_mtoto,SIGNAL( finished() ),m_mtoto,SLOT( deleteLater() ) ) ; connect( this,SIGNAL( volumeMiniProperties( volumeEntryProperties * ) ), m_babu,SLOT( autoMountVolume( volumeEntryProperties * ) ) ) ; connect( this,SIGNAL( volumeRemoved( QString ) ), m_babu,SLOT( volumeRemoved( QString ) ) ) ; utility::fileHandle f( inotify_init() ) ; int fd = f.handle() ; if( fd == -1 ){ return this->failedToStart() ; } int dev = inotify_add_watch( fd,"/dev",IN_CREATE|IN_DELETE ) ; int dm = inotify_add_watch( fd,"/dev/mapper",IN_CREATE|IN_DELETE ) ; int md = -1 ; if( utility::pathExists( "/dev/dm" ) ){ md = inotify_add_watch( fd,"/dev/md",IN_DELETE ) ; } auto _allowed_device = []( const char * device ){ /* * dont care about these devices. * /dev/sgX seem to be created when a usb device is plugged in * /dev/dm-X are dm devices we dont care about since we will be dealing with them differently */ bool s = _startsWith( device,"sg" ) || _startsWith( device,"dm-" ) || _contains( device,"dev/tmp" ) || _contains( device,"dev-tmp" ) || _contains( device,".tmp.md." ) || _contains( device,"md/md-device-map" ) ; return s == false ; } ; auto _device_action = [&]( const struct inotify_event * event ){ /* * /dev/md/ folder seem to be deleted when the last entry in it is removed and * created before the first entry is added.To account for this,monitor for the * folder creation to start monitoring its contents. */ if( event->wd == dev && event->mask & IN_CREATE ){ if( _stringsAreEqual( "md",event->name ) ){ md = inotify_add_watch( fd,"/dev/md",IN_DELETE ) ; return false ; } } if( event->wd == dev && event->mask & IN_DELETE ){ if( _stringsAreEqual( "md",event->name ) ){ inotify_rm_watch( md,dev ) ; return false ; } } return true ; } ; const char * currentEvent ; const char * end ; constexpr int BUFF_SIZE = 4096 ; char buffer[ BUFF_SIZE ] ; fd_set rfds ; FD_ZERO( &rfds ) ; int select_fd = fd + 1 ; auto _eventsReceived = [ & ](){ /* * we are blocking on select() and not on read() because QThread->terminate() does not seem to * be able to get out of a blocked read() on certain Qt versions. */ auto _gotEvents = [ & ](){ FD_SET( fd,&rfds ) ; return select( select_fd,&rfds,nullptr,nullptr,nullptr ) > 0 ; } ; if( _gotEvents() ){ auto s = read( fd,buffer,BUFF_SIZE ) ; if( s > 0 ){ end = buffer + s ; currentEvent = buffer ; return true ; } } return false ; } ; auto _processEvent = [ & ]( const struct inotify_event * event ){ if( _device_action( event ) && _allowed_device( event->name ) ){ auto _device = [ & ](){ if( event->wd == dm ){ return zuluMountTask::devices::dm_device ; }else if( event->wd == md ){ return zuluMountTask::devices::md_device ; }else{ return zuluMountTask::devices::device ; } } ; zuluMountTask::event e{ _device(),event->mask == IN_CREATE,event->name } ; Task::exec( [ this,e ](){ auto r = zuluMountTask::deviceProperties( e ) ; if( r.volumeRemoved ){ emit volumeRemoved( r.volumeName ) ; }else{ emit volumeMiniProperties( r.entry ) ; } } ) ; } } ; #define _cast( x ) reinterpret_cast< const struct inotify_event * >( currentEvent ) #define _eventSize sizeof( struct inotify_event ) while( true ){ if( _eventsReceived() ){ while( currentEvent < end ){ auto event = _cast( currentEvent ) ; if( event ){ _processEvent( event ) ; currentEvent += _eventSize + event->len ; }else{ currentEvent += _eventSize ; } } } } }
void auto_mount::run() { /* * Not exactly sure what i am doing here but this kind of thing seem to be necessary to prevent * an occassional crash on exit with an error that reads something like "object deleted while thread is still running" */ m_mtoto = static_cast< QThread * >( this ) ; connect( m_mtoto,SIGNAL( terminated() ),m_main,SLOT( threadStopped() ) ) ; connect( m_mtoto,SIGNAL( terminated() ),m_mtoto,SLOT( deleteLater() ) ) ; connect( m_mtoto,SIGNAL( terminated() ),this,SLOT( deleteLater() ) ) ; #define BUFF_SIZE 4096 char buffer[ BUFF_SIZE ]; m_fdDir = inotify_init() ; if( m_fdDir == -1 ){ qDebug() << "inotify_init() failed to start,automounting is turned off"; m_threadIsRunning = false ; return ; }else{ m_threadIsRunning = true ; } int dev = inotify_add_watch( m_fdDir,"/dev",IN_CREATE|IN_DELETE ) ; int mapper = inotify_add_watch( m_fdDir,"/dev/mapper",IN_CREATE|IN_DELETE ) ; int md = -1 ; QDir d( QString( "/dev/dm" ) ) ; if( d.exists() ){ md = inotify_add_watch( m_fdDir,"/dev/md",IN_DELETE ) ; } struct inotify_event * pevent ; QString device ; const char * f ; const char * z ; int data_read ; int baseSize = sizeof( struct inotify_event ) ; while( 1 ) { data_read = read( m_fdDir,buffer,BUFF_SIZE ) ; z = buffer + data_read ; for( f = buffer ; f < z ; f = f + baseSize + pevent->len ){ pevent = ( struct inotify_event * )f; m_device = f + baseSize ; #define stringPrefixMatch( x,y,z ) strncmp( x,y,z ) == 0 #define stringEqual( x,y ) strcmp( x,y ) == 0 #define stringHasComponent( x,y ) strstr( x,y ) != NULL if( stringPrefixMatch( m_device,"sg",2 ) || stringPrefixMatch( m_device,"dm-",3 ) || stringHasComponent( m_device,".dev/tmp" ) || stringHasComponent( m_device,".tmp.md." ) || stringHasComponent( m_device,"md/md-device-map" ) ){ /* * dont care about these devices. * /dev/sgX seem to be created when a usb device is plugged in * /dev/dm-X are dm devices we dont care about since we will be dealing with them differently */ ; }else{ if( pevent->wd == dev && pevent->mask & IN_CREATE ){ /* * /dev/md path seem to be deleted when the last entry in it is removed and * created before the first entry is added.To account for this,monitor for the * folder created to start monitoring its contents if it get created after we have started */ if( stringEqual( "md",m_device ) ){ md = inotify_add_watch( m_fdDir,"/dev/md",IN_DELETE ) ; continue ; } } if( pevent->wd == dev && pevent->mask & IN_DELETE ){ if( stringEqual( "md",m_device ) ){ inotify_rm_watch( md,dev ); continue ; } } device = QString( m_device ); m_thread_helper = new auto_mount_helper() ; connect( m_thread_helper,SIGNAL( getVolumeSystemInfo( QStringList ) ), m_babu,SLOT( autoMountVolumeSystemInfo( QStringList ) ) ) ; connect( m_thread_helper,SIGNAL( getVolumeInfo( QStringList ) ), m_babu,SLOT( autoMountVolumeInfo( QStringList ) ) ) ; connect( m_thread_helper,SIGNAL( deviceRemoved( QString ) ), m_babu,SLOT( deviceRemoved( QString ) ) ) ; if( pevent->wd == dev ){ m_thread_helper->start( device,auto_mount_helper::dev,pevent->mask ) ; }else if( pevent->wd == mapper ){ m_thread_helper->start( device,auto_mount_helper::dev_mapper,pevent->mask ) ; }else if( pevent->wd == md ){ m_thread_helper->start( device,auto_mount_helper::dev_md,pevent->mask ) ; }else{ ; } } } } }
void ServiceManager::start() { VitaMTP_Init(); loadDefaultSettings(); if(QSettings().value("useMemoryStorage", true).toBool()) { m_db = new QListDB(); } else { m_db = new SQLiteDB(); } // initializing database for the first use refreshDatabase(); // send a signal when the update is finished connect(m_db, SIGNAL(updated(int)), this, SIGNAL(databaseUpdated(int))); thread_count = 0; qDebug("Starting cma threads"); CmaClient *client; QSettings settings; if(!settings.value("disableUSB", false).toBool()) { usb_thread = new QThread(); client = new CmaClient(m_db); usb_thread->setObjectName("usb_thread"); connect(usb_thread, SIGNAL(started()), client, SLOT(connectUsb())); connect(client, SIGNAL(finished()), usb_thread, SLOT(quit()), Qt::DirectConnection); connect(usb_thread, SIGNAL(finished()), usb_thread, SLOT(deleteLater())); connect(usb_thread, SIGNAL(finished()), this, SLOT(threadStopped())); connect(usb_thread, SIGNAL(finished()), client, SLOT(deleteLater())); connect(client, SIGNAL(refreshDatabase()), this, SLOT(refreshDatabase())); client->moveToThread(usb_thread); usb_thread->start(); thread_count++; } if(!settings.value("disableWireless", false).toBool()) { CmaBroadcast *broadcast = new CmaBroadcast(this); wireless_thread = new QThread(); client = new CmaClient(m_db, broadcast); wireless_thread->setObjectName("wireless_thread"); connect(wireless_thread, SIGNAL(started()), client, SLOT(connectWireless())); connect(client, SIGNAL(finished()), wireless_thread, SLOT(quit()), Qt::DirectConnection); connect(wireless_thread, SIGNAL(finished()), wireless_thread, SLOT(deleteLater())); connect(wireless_thread, SIGNAL(finished()), this, SLOT(threadStopped())); connect(wireless_thread, SIGNAL(finished()), client, SLOT(deleteLater())); connect(client, SIGNAL(refreshDatabase()), this, SLOT(refreshDatabase())); client->moveToThread(wireless_thread); wireless_thread->start(); thread_count++; } if(thread_count == 0) { qCritical("You must enable at least USB or Wireless monitoring"); } }