[Development] Should QFileSystemWatcher be thread-safe? (Qt 5.8.0)

Konrad Rosenbaum konrad at silmor.de
Thu Oct 5 12:59:08 CEST 2017


On Mon, October 2, 2017 11:54, René J. V. Bertin wrote:
> Konrad Rosenbaum wrote:
>> Whenever a QueuedConnection triggers the sending object generates an
>> event
>> and delivers it to the event queue of the thread that owns the target
>> slot's
>
> So the sending object knows that the nature of each connection to it?

The code behind the signal knows about it and how to deliver itself.

>>> Can I assume
>>> there's some kind of fifo that ensures signals are delivered in order
>>> of
>>
>> Never assume order.
>
> Ok. From your other reactions I infer there is something like a
> mostly-fifo with
> a mutex that protects pushing and popping :)

More or less: yes. The input is thread safe and FIFO (or as FIFO as you
can get in a multi-threaded environment), the output has priorities and
filters.

>> deterministic or in order. Threads execute in a (seemingly) random order
>> and
>> can interrupt or race each other.
>
> And one cannot even rely on mutexes to "fix" or prevent that?

If you use Mutexes right you can rely on them to serialize the code that
blocks on the same Mutex, i.e. only one blocking code block is executed at
the same time. You cannot predict which thread gets the Mutex first or
last. Since Mutexes are a special case of semaphores, the same is true for
semaphores  (again: if you use them right).

The OS can interrupt your thread at any time and give the CPU to another
thread or process that is ready to run (a thread waiting for a blocked
Mutex is not ready). It may also interrupt your thread and run a signal
handler in the same thread! On the other hand if your machine has multiple
CPU cores the OS may let several threads run happily at the same time on
different cores. Depending on hardware timings and bus congestion these
threads may race each other.

>> important or less important than others - e.g. the deleteLater event is
>> normally only handled at the top level of the event loop to prevent
>> crashes
>> caused by less then absolutely ingenious programmers using
>> QueuedConnection,
>> deleteLater and QEventLoop (that fact saved my bacon a few times).
>
> Sounds like the situation that ObjC/Apple addressed by using refcounting
> instead
> of direct deletion.

If by "problem" you mean programmer stupidity (or if you prefer: naturally
limited cognitive capacity): yes. ;-)

> Anyway, order isn't crucial in the particular application I'm working on
> here.
> The only thing that matters is that signals are delivered in an acceptable
> time-
> frame after having been sent. And that should be the case (my initial
> reason for
> getting involved with this was to keep the main event loop from being
> blocked).

If by "acceptable" you mean "fast enough for a human being", then yes -
QueuedConnection will do this for you reliably enough.

With Qt there are some simple rules:

If there is the slightest chance that a QObject is used/called across
threads - use deleteLater instead of delete.

For calls across threads use QueuedConnection explicitly. (In most cases
Qt detects this automatically, but there are some special cases usually
involving moveToThread().)

Do not feed pointers or references into signal-slot-connections. Avoid
sharing pointers/references between threads.

Do not make any assumptions about the order of signals or slots. Try to
avoid the assumption that a signal only returns after the slot has been
executed (or that it returns immediately) - you'll mess with connection
settings soon enough after you forgot that you rely on it. Regard signals
as fire-and-forget-but-maybe-take-some-time-with-unknown-effects.

The combination of these will ensure that your application behaves in an
acceptable manner.

QueuedConnection and deleteLater use the same event queue, so Qt is able
to detect if an object disappears while delivering a signal.

If you absolutely, positively have to share pointers between threads:
a) try to refactor, if that fails:
b) make 100% positively sure that the object lives longer than the
sharing; use QPointer (makes debugging easier); write twice as many lines
of comments as you write code so you cannot forget.

If you have to block on signals: try to use multiple signals instead of a
hard block; document!



   Konrad




More information about the Development mailing list