Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Moving object back to parent thread



  • Hello,
    I am writing application communicating with serial devices.
    Currently I am having my custom SerialDeviceManager object which stores open connections to serial devices that I am working with (might be multiple devices). After user choices device(s) that he is interested to communicate with, I am creating SerialDeviceWrapper objects that have some communication logic and move them to separate threads together with QSerialPort objects.
    Simplified code:

    QMap<int, QSerialPort*> devices; // Initialized earlier and stores open connections
    QMap<int, QPair<SerialDeviceWrapper*, QThread*> > active_devices; // Stores initialized threads
    for (auto& dev_id : dev_ids) // dev_ids comes from user choice
    {
         SerialDeviceWrapper *sdw = new SerialDeviceWrapper(devices[dev_id]);
         QThread *device_thread = new QThread();
         sdw->moveToThread(device_thread);
         devices[dev_id]->moveToThread(device_thread);
         connect(device_thread, &QThread::finished, sdw, &QObject::deleteLater);
         connect(device_thread, &QThread::finished, device_thread, &QObject::deleteLater);
         device_thread->start();
         active_devices.insert(dev_id, qMakePair(sdw, device_thread));
    }
    

    Then upon some events I am invoking slots of created SerialDeviceWrapper's:

    // from thread where SerialDeviceManager lives
    // int device_id; uint8_t arg1, arg2 coming from event
    QMetaObject::invokeMethod(active_devices[device_id].first, "SlotName", Qt::QueuedConnection, Q_ARG(const uint8_t, arg1), Q_ARG(const uint8_t, arg2));
    

    Also if user decides that he doesn't need certain serial device anymore, I am stopping SerialDeviceWrapper thread

    // int device_id coming from event
    active_devices[device_id].second->quit();
    active_devices[device_id].second->wait();
    // connected deleteLater will dispose wrapper object and thread object someday
    

    However I do not want to close QSerialPort for this device as it might be reused again by user later.
    My question is how/when should I correctly move QSerialPort object that were moved to Wrapper's QThread back to thread where SerialDeviceManager lives?
    Probably it is possible to do nothing and in case user decides to reuse serial device try to pull QSerialPort object to SerialDeviceManager's thread from itself first (as stopped thread will be deleted and QSerialPort object won't have any thread assigned to it and from documentation "objects with no thread affinity can be "pulled" to the current thread"). But in such case is scenario "User stops serial device communication -> User decides to reuse serial device" but deleteLater for stopped thread is not invoked yet (so QSerialPort still belongs to this thread affinity) is possible?

    Thanks in advance for helping!


  • Lifetime Qt Champion

    Hi,

    You could have a released signal with a pointer to a QSerialPort object as parameter that you connect to your manager so you can put that serial port back to the free pool and the manager's thread.



  • Hi, SGaist,
    Thanks for reply! But at what point of SerialDeviceWrapper's lifecycle shoult I emit released signal? As far as I understand, it should be emitted somewhere after QThread::finished signal to be sure that it will be placed in event loop after deleteLater for thread that QSerialPort was in.


  • Lifetime Qt Champion

    One way could be that rather than calling deleteLater when you thread finishes, you call a slot on the manager to do the cleanup.



  • Thanks for helping, SGaist!

    At the end I changed

    connect(device_thread, &QThread::finished, device_thread, &QObject::deleteLater);
    

    to

    connect(device_thread, &QThread::finished, this, [this, dev_id](){ this->DeleteActiveDevice(dev_id); });
    

    and added slot

    void SerialDeviceManager::DeleteActiveDevice(const int dev_id)
    {
        delete active_devices[dev_id].second;
        active_devices.remove(dev_id);
        // At this point serial port connection will be with no thread affinity
        devices[dev_id]->moveToThread(QThread::currentThread());
    }
    

Log in to reply