MythTV  master
QObject is dangerous for your health

QObject derived classes can be quite useful, they can send and receive signals, get keyboard events, translate strings into another language and use QTimers. More...

QObject derived classes can be quite useful, they can send and receive signals, get keyboard events, translate strings into another language and use QTimers.

But they also can't be deleted easily, they are not thread-safe, can't participate as equals in multiple inheritence, can not be used at all in virtual inheritence.

Deleting QObjects

The problem with deleting QObjects has to do with signals, and events. These can arrive from any thread, and often arrive from the Qt event thread even if you have not explicitly requested them.

If you have not explicitly connected any signals or slots, and the only events you might get are dispatched with qApp->postEvent(), not qApp->sendEvent(), then it is safe to delete the QObject in the Qt event thread. If your QObject is a GUI object you've passed given to Qt for it to delete all is good. If your object is only deleted in response to a posted event or a signal from the Qt event thread (say from a QTimer()) then you are OK as well.

If your class may be deleted outside a Qt thread, but it does not explicitly connect any signals or slots, and the only events it might get are dispatched with qApp->postEvent(), then you can safely use deleteLater() to queue your object up for deletion in the event thread.

MythTV is a very much a multithreaded program so you may need to have a QObject that may get events from another thread if you send signals but do not get any signals and or events from outside the Qt event thread, then you can use deleteLater() or delete depending on whether you are deleteing the QObject in the event thread, see above. But if you are not in the Qt event thread then you need to call disconnect() in the thread that is sending the signals, before calling deleteLater(). This prevents your object from sending events after you consider it deleted (and begin to delete other QObjects that may still be getting signals from your object.)

What about if you are getting events via qApp->sendEvent() or a signal from another thread? In this case things get complicated, so we highly discourage this in MythTV and prefer that you use callbacks if at all possible. But in say you need to do this, then you need to disconnect all the signals being sent to your QObject. But disconnect() itself is not atomic. That is you may still get a signal after you disconnect it. What you need to is disconnect the signal from your slot in the thread that is sending the signal. This prevents the signal from being emitted while you are disconnecting. Doing this gracefully is left as an excersize for the reader.

Ok, so the above is not entirely complete, for instance you could wrap every signal emit in an instance lock and reimplement a disconnect that uses that instance lock. But if you've figured this or another solution out already all you need to know is that almost all Qt objects are reenterant but not thread-safe, and that QObjects recieve events and signals from the Qt event thread and from other threads if you use qApp->sendEvent() or send signals to it from another thread.

QObject thread-safety

The only thread-safe classes in Qt are QThread and the mutex classes. when you call any non-static QObject method you must have a lock on it or make sure only thread ever calls that function.

QObject itself is pretty safe because when you assign one QObject to another you make a copy. Qt's container classes, including QString do not make a copy but rather pass a reference counted pointer to the data when you assign one to another. This reference counter is not protected by a mutex so you cannot use regular assignment when passing a QString or other Qt container from one thread to another.

In order to use these classes safely you must use QDeepCopy when passing them from one thread to another. This is thankfully quite easy to use.

QString original = "hello world";
QString unsafe = original;
QString safe = QDeepCopy<QString>(original);

In this case safe and original can be used by two separate threads, while unsafe can only be used in the same thread as originalStr.

The QDeepCopy template will work on the most popular Qt containers, for the ones it doesn't support you will have to copy manually.

QObject inheritence

You cannot inherit more than one QObject derived class. This is because of how signals and slots are implemented. What happens is that each slot is transformed into an integer that is used to find the right function pointer quickly when a signal is sent. But if you have more than one QObject in the inheritence tree the integers will alias so that signals intended for the second slot in parent class B might get sent to the second slot in parent class A instead. Badness ensues.

For the similar reason you cannot inherit a QObject derived class virtually, in this case the badness is a little more complicated. The result is the same however, signals arrive at the wrong slot. Usually, what happens is that the developer tries to inherit two QObject derived classes but gets linking errors, so they make one or more of the classes virtual, or inherit a QObject non-virtually but then also inherit one or more QObject derived classes virtually. The linker doesn't complain, but strange unexplainable behaviour ensues when the application is run and signals start showing up on the wrong slots.