I create an object of a class that has a QTimer that runs cyclically.
The client of the program can request the removal of the task that the object is doing, which will delete the object.
When using object->deleteLate(); the object is never deleted, but with delete object; it works.
Is this due to the timer field in the object running cyclically?
The documentation for ~QObject says:
All signals to and from the object are automatically disconnected, and any pending posted events for the object are removed from the event queue. However, it is often safer to use deleteLater() rather than deleting a QObject subclass directly.
Emphasis is mine. In a note later in the same document
Warning: Deleting a QObject while pending events are waiting to be delivered can cause a crash. You must not delete the QObject directly if it exists in a different thread than the one currently executing. Use deleteLater() instead, which will cause the event loop to delete the object after all pending events have been delivered to it.
Emphasis is mine. This means your cyclic timer, upon re-adding itself, would cause the object to always have a pending event.
If you want to use deleteLater(), then your cyclic timer should detect whether a deleteLater() is in progress, and then cancel itself if so. Then, your deletion will occur after any other pending event has been cleared.
Related
I believe the following code is safe, but I have not been able to confirm it:
void someMethod(Process *process) {
emit signalWithProcess(process);
process->deleteLater();
}
I'm not sure if process still exists when the signals are called and auto or queued connections are used.
For direct connections, the code above is correct because the slots are called synchronously. AFAIK, for slots on the same thread as the object, it is also correct because deleteLater() posts a new event to the object's event loop. However, the signal events are posted earlier so they run earlier.
However, for queued connections on different threads I do not understand how it works and whether the code above is safe. My gut-feeling says yes, but I have not been able to verify it.
For example, the process object above belongs to thread A and there's a queued connection to another object on thread B. When someMethod() runs, it adds the queued signals to thread B's event loop and adds the deleteLater event to thread A's event loop.
Unless there is some way to block thread A from deleting the object while the slot is run in thread B, this seems not to be safe. Unfortunately, I have not found anything related to that in the source code or the documentation.
Similar question (but without deleteLater): Is it safe to emit signal passing QObject pointer as parameter right before the passed object is going to be destroyed?
Consider that object A is bound to thread T1 and a signal sig1 with an argument of QList < QVariantMap > is fired from thread T2.
The signal is queued on A's event loop, but before handling, A's thread (T1) is quit. The QList< QVariantMap > argument of sig1 is obviously allocated on the stack, I'm guessing that it should clean up itself.
But when? Perhaps when the QThread object itself is destroyed? Does anyone have specific knowledge in this area? In my system, this thread will not simply run once and end at shutdown. It may potentially be started, run for hours, and then quit and be started again later on.
The argument will not be deleted if the thread has been terminated in a correct way like
QThread t;
t.exit();
t.wait();
Now you assert that
The QList< QVariantMap > argument of sig1 is obviously allocated on
the stack
It is not that obvious. Because the receiving thread need to use the data in a deferred execution, emit(myVariantList) will eventually lead to a copy of myVariantList. You don't know where the copy is going to be allocated.
Now to allow posting events with eventual data , ie the classic
Mywidget m;
m.show(); <-- this post at least one event
app.exec();
Those posted events need to be saved just in case of an eventual execution of the event loop. I believe that if the object holding the event loop is not destroyed or the user doesn't explicitly ask for events to be removed, they stay for the lifetime of the QObject.
I searched this site and QT documentation, but could not find and direct answer for the following question:
Lets say I have a worker class with only one slot:
void Worker::testSlot(){
//access data and do some calculation
}
Now if this slot is connected to signal from other classes running on other thread, and if queued connection is used, is it necessary to use lock (QMutexLocker) before accessing data in worker? I think it is not needed since the testSlot() is executed in one thread always (the thread in which worker is moved), and thus it is synchronized. Even if two signals were emitted from different thread at the same time, there is no way to suspend executing the slot in half-way for the first signal and start for second signal. But I am not sure about this.
You're 100% correct.
The key bit of information is that emission of a signal connected to an object in a different thread via a queued or automatic connection results in posting a QMetaCallEvent to the target object. It doesn't directly result in any calls at all.
The event loop running in the thread where the target object resides has toy deliver the event to the object - you can verify that by properly overriding the event method and outputting a debug message when the event has the MetaCall type. Remember to call the base class's method in your reimplementation. Since the event loop runs synchronously, it executes the calls serially. Thus no additional serialization-of-access means are necessary. It doesn't matter what thread the meta call event was posted from - the thread per se is not used for the posting, and the event queue will look the same whether a number of events was posted from one thread, or multiple threads.
It is the QObject::event method that handles the QMetaCallEvent and executes the call. The call may be to a slot, an invokable method, a constructor/destructor, or a functor that is to execute in a given object's thread context.
I know that deleting a QObject from a slot processing could crash the app because it could have other queued events.
So, instead of use "delete obj" i will use obj->deleteLater(). As far as I know the obj waits to process all queued events and then "delete obj".
QObject::~QObject ()
All signals to and from the object are automatically disconnected, and any pending posted events for the object are removed from the event queue. However, it is often safer to use deleteLater() rather than deleting a QObject subclass directly.
But, what's about other signal/slot processes? I mean, if you have connected signals/slot from objects which live in different threads as Qt::QueuedConnection or Qt::BlockingQueuedConnection. Are they dequeued to be processed?
Thanks in advance.
deleteLater will remove object from thread which object belongs to. Qt::QueuedConnection and Qt::BlockingQueuedConnection (also Qt::AutoConnection) are work in this way that slot is invoked from event loop of thread which object with slot belongs to.
So if you add to this quote you posted this means that any pending Qt::*QueuedConnection will be automatically dismissed when object gets destroyed. This means that you are safe when using those connections and deleteLater. Problem may appear when you will use Qt::DirectConnection with different thread, or when you try to destroy object directly (delete pointer;) from wrong thread.
As per Qt Documents
The object will be deleted when control returns to the event loop. If
the event loop is not running when this function is called (e.g.
deleteLater() is called on an object before QCoreApplication::exec()),
the object will be deleted once the event loop is started. If
deleteLater() is called after the main event loop has stopped, the
object will not be deleted. Since Qt 4.8, if deleteLater() is called
on an object that lives in a thread with no running event loop, the
object will be destroyed when the thread finishes.
Note that entering and leaving a new event loop (e.g., by opening a
modal dialog) will not perform the deferred deletion; for the object
to be deleted, the control must return to the event loop from which
deleteLater() was called.
Note: It is safe to call this function more than once; when the first
deferred deletion event is delivered, any pending events for the
object are removed from the event queue.
In Qt::QueuedConnection type deleteLater() will wait to finish thread.but In Qt::DirectConnection when anyone delete object directly, it may crash the application.
In that case you need to check thread is running or not before deleting.
I'm looking for a solution to schedule the deletion of an object across threads. The docs about how deleteLater behave are not entirely clear. Can I call this function in a thread which is not the object's owner?
For example, Object X is owned by Thread A, and in Thread B I would like to have Object X deleted. As the object may be inside event processing at the moment (in Thread A) I can't safely delete it until it gets back to the message loop. If I call deleteLater from Thread B however the docs seem to indicate it will delete as soon as Thread B gets back to the message loop.
Currently I take the approach of having a signal emitted in Thread A which is attached to a slot which calls deleteLater. I'm wondering if there is perhaps an easier way to do this -- if indeed I can just call deleteLater from any thread.
Looking at the Qt 4 code and Qt 5 code, deleteLater() just invokes QCoreApplication::postEvent() which is explicitly declared thread-safe. So, it should be fine to just call it directly. As the event queue is processed in the object's owner thread, deletion will happen in thread A.
Only Qt 5 QObject documentation explicitly lists deleteLater() as thread safe. If you want to completely rely on documented behaviour in Qt 4, use postEvent().
While deleteLater() is not safe itself, you can invoke it in object's threadA with meta call:
metaObject()->invokeMethod(object, "deleteLater", Qt::QueuedConnection);
Then, it will be safe.
deleteLater() only means that the object will be deleted after all signal/slots int the current event loop (i.e. ThreadB) have been treated.
So, if no other slots need ObjectX in ThreadB, it is equivalent to a plain delete.
Whether you can delete the object or not and how it will be handled in ThreadA is up to your app logic.
If ObjectX is the main object of the thread, sending the quit() signal to ThreadA is the way to go.