Qt Signals And Slots Thread Safe

Qt::DirectConnection forces Qt to invoke the receiving slot/signal directly, and it doesn't care about threads, so it's not thread safe. Qt::QueuedConnection forces Qt to 'delay' invocation of the receiving signal/slot by posting an event in the event queue of the thread the receiving object resides in. The signal slot connection (with a few rare exceptions) is safe across thread boundaries. So Just making it is usually enough to ensure race conditions don't occur. Does QT has builtin functionality to make sure it is not erased until nobody has a reference to it? No, but this is outside of Qt's scope. Detailed Description. The QThread class provides a platform-independent way to manage threads. A QThread object manages one thread of control within the program. QThreads begin executing in run. By default, run starts the event loop by calling exec and runs a Qt event loop inside the thread. You can use worker objects by moving them to the thread using QObject::moveToThread.

For any C++ developer who's used Qt, we've grown to love the Signals/Slots idiom it presents for creating clean Observer code. However, it relied on the Qt Moc pre-compiler tool, which meant any project that wanted to use this feature had to use follow along with the Qt idiom, which really made Qt applications look potentially foreign despite being written in C++. In addition to this the implementation wasn't type-safe (a.k.a. no compile-time type checking), and doesn't generalize to any callable target (you have to extend QtObject and declare a slot using Qt's syntax, can only return void).

Since then, there have been multiple implementations of Signals/Slots. Some notable ones are listed below:

  • Boost Signals. Not thread safe, performance wasn't great, now deprecated in favor of Boost Signals2. Licensed under the Boost Liscense.
  • Boost Signals2. Thread safe upgrade of Boost Signals. Others have complained about its performance, but my tests seem to show it's at least decent.. Licensed under the Boost Liscense.
  • Libsigc++. Supposedly decently quick, but not thread safe. I think this is also a near-fully featured implementation like Boost Signals/Signals2. Also licensed under LGPL, making use somewhat restricted.
  • libtscb. A thread safe fairly quick quick implementation. However, it skimps on features (I think it offers similar features to Qt's implementation). Also licensed under LGPL.
  • C++11-based Implementation. This is actually another blog which sought to implement Signals/Slots using new features brought by C++11, namely variadic templates, std::function, and possibly more. This is CC0 licensed (public domain). Probably one of the fastest implementations I have seen, but is not thread-safe.

I was wondering if I could implement a more feature-full implementation like Boost Signals2 bet with better performance like libtscb. I'll be using some C++11 features like the last implementation, notably atomics, std::function, and variadic templates. I'm also using Boost shared_ptr/weak_ptr, Boost mutex, and Boost shared_ptr atomics. These libraries are being used because currently the compiler I'm using doesn't have the standard variants implemented (Mingw w64 gcc-4.8.1).

For the most part, I was able to implement (or actually, can see a straight-forward implementation) nearly all of the features provided by Boost Signals2. There are some semantic changes (notably the interface for combiners is different), and I was unable to capture the O(log(n)) performance of inserting group-ordered slots. My implementation likely will have O(n) group insertion.

Some basic observations I've noticed about how Signals/Slots are usually used:

  • Connecting/disconnecting slots is usually not that time critical.
  • Most signals either have no slots, or very few slots connected.
  • Signals may be invoked from multiple threads, and usually can be evaluated asynchronously. It seems like slots usually can be evaluated asynchronously, but they could be strongly ordered, too. In any case, only forward iteration is required for emitting a signal.
  • Emitting should be as fast as possible.

Thinking about how to best reach these goals, I decided to use an implementation which guarantees a wait-free singly linked list for read operations. The back-end implementation is still a doubly linked list, and only one writer is allowed at a time.

Memory management is taken care of using shared_ptr/weak_ptr. I wanted to see if I could implement using only standard C++11, and theoretically I could have, but unfortunately I don't have access to a compiler with all the necessary features. Fortunately, I'm only using the Boost equivalents in a 'standards-compliant' manner, so as time goes on changing these to a pure C++11 implementation is a find/replace operation.

basic slot class structure

I'm utilizing a trick for transforming multiple inheritance into single inheritance. There's not really much good reason for doing so other than code bloat issues with Visual Studio. Currently my implementation uses standard C++11 features not supported by any release of Visual Studio (2012 or older), but in the future I want to work towards C++03 compatibility using various Boost libraries (since I'm already using a few for this purpose).

Qt Signals And Slots Tutorial

This is not the full code, but a near-bare bones implementation which at least shows the usage and some implementation details.

Because I am limiting myself to single inheritance chains only, I have to carefully consider what order classes get inherited (especially for classes which could be instantiated). Eventually, I ended up with the following:

  • slot_base -> slot_wrapper
  • slot -> callable -> slot_base -> slot_wrapper
  • grouped_slot -> slot -> groupable -> callable -> slot_base -> slot_wrapper
  • extended_slot -> callable -> slot_base -> slot_wrapper
  • grouped_extended_slot -> extended_slot -> groupable -> callable -> slot_base -> slot_wrapper

Basic Signal Structure

Update: While I was implementing this I had assumed that atomic shared_ptr was lock-free. After looking into the current implementations, they currently use spinlocks on each shared_ptr. In other words, not lock-free. However, in theory my implementation could take advantage of true lock-free atomic shared_ptr's in the future.

The basic structure of the Signal class is a doubly linked list, but there internally the linked list ensures that atomically there is always a valid singly linked list from any node to the tail. I am unaware of any generic lock-free doubly linked list implementation which has all of the features I need, so the trade off I'm making is only one writer is allowed at a time. Memory management is handled entirely by shared_ptr's internal reference counting mechanisms. There might be some more efficient atomic reference counting method I could implement, but for now shared_ptr is fast enough.

In order to make signal emission lock-free, I decided to use head and tail nodes. I also place a shared_ptr to a given node which is where grouped slot insertion can begin searching from, but this only points to an existing node. I don't know if I can implement some sort of interleaved binary tree/singly linked list to allow better grouped slot insertion, but for now I'm going to assume that the somewhat crummy group insertion performance is acceptable since signal emission is the primary optimization goal.

Basic Algorithm for List Write Operations

  1. Do non-list dependant operations (mainly allocating memory for nodes and build connection object)
  2. Acquire unique write lock.
  3. Perform all necessary list modification operations. Operations which will change the implicit singly linked list must be atomic, otherwise they do not.
  4. Release lock.

Here's an example implementation for push_front (connect new slot at front of list):

Modified Iterator

One problem I encountered while working on the combiner call implementation was how to efficiently pass iterators to the combiner. The solution I came up with means the iterator object encapsulated all state internally, in particular if it is the end node. This means that no separate end iterator is passed. I think the problem could be resolved using a similar single inheritance chain like I did for the slots classes, though I haven't tried this out yet.

I am using a std::tuple to store parameters for later execution, and unpacking them using the technique Johannes Schaub presented here.

Signal Emit Algorithm

Implementing the signal emit operation is fairly simple, the only thing to keep in mind is that operations which try to move to the next node should be atomic. I've omitted a sample Combiner implementation because there's not much different about it (other than keeping in mind an iterator currently encapsulates the end state).

For my testing, I'm going to benchmark Qt Signals/Slots, Boost Signals2, and this implementation. The slot being benchmarked:

The hardware running the test is an Intel i5-m430 laptop running Windows 7 x64. For Qt test I'm using VS2012 x64, and for the other tests I'm using Mingw-w64 gcc 4.8.1. Basically, I didn't want to re-compile Qt with mingw since I already had it built for VS2012. For the record I'm testing with *Qt5 and Boost *1.54.0.

Safety
Note*: well, actually I'm using trunk repository builds, but I'm pretty sure there's no difference between the two (it would be silly for Qt to significantly muck around with the Signals/Slots implementation, and I haven't observed any changes to Boost Signals2).

I'm going to time how long it takes to emit signals with various number of number of slots (yes, I am averaging over many signal emits). The std::function calls is repeated calls to a std::function wrapping this to handler, more or less used to determine how much time is spent doing actual slot work.

Test CaseNo Slots (ns)1 Slot (ns)2 Slots (ns)4 Slots (ns)8 Slots (ns)16 Slots (ns)32 Slots (ns)64 Slots (ns)static overhead (ns)dynamic overhead (ns/slot)
std::function calls03.57.515296011623203.6
Qt Signals/Slots1475115177312581112322024033.8
Boost Signals275137183253398705128025007537
My Implementation275788150272525101019802730

So what can we tell from these results? Well, unfortunately it's difficult to know exactly how Qt compares to the others because of the different compilers. However, it is probably safe to say Qt has some short-circuit mechanism for handling empty signals. I would be curious to see how well these implementations would fare under high contention/parallel loads. I suspect my implementation would handle multiple emits with a single writer quite easily because this is what the implementation targets. I don't know how Qt or Boost Signals2 are implemented under the hood, but I suspect both of them use some sort of locking mechanism on emits.

I don't know exactly how bad Boost Signals2 performed in the past, but as far as I can tell it is probably 'acceptable' most of the time. However, it does perform poorly when there are no slots connected. I'm going to keep working on this implementation and will eventually release it when it's done (right now it basically works, but many features are unimplemented). The cost is not as good as is possible with non-thread safe implementations, but there are nice gains. I may even try submitting it for inclusion in Boost (either as signals3, or use parts to improve signals2).

Meeting C++

published at 20.08.2015 15:28 by Jens Weller

This is the 7th blog post in my series about writing applications with C++ using Qt and boost. This time it is about how to notify one part of our application that something has happened somewhere else. I will start with Qt, as it brings with signals and slots a mechanism to do exactly that. But, as I have the goal not to use Qt mainly in the UI Layer, I will also look on how to notify other parts of the application, when things are changing. The last episode was about QWidgets and data.

The video for this episode:

Signals and Events in Qt

But lets start with Qt. Qt offers two different systems for our needs, Qt signal/slot and QEvents. While Qt signal/slot is the moc driven signaling system of Qt (which you can connect to via QObject::connect), there is a second Event interface informing you about certain system-like events, such as QMouseEvent, QKeyEvent or QFocusEvent. Usually you have to overwrite a method to receive such events, or use an event filter, like I showed in my last post for QFocusEvents. Some classes translate QEvents to signals, such as the TreeView, which has a signal for displaying context menus. But as this blog post is more on signaling then system events...

Qt has had its own signaling mechanism for a long time now, so when you use Qt, you also will use QSignals. Qt also uses its own keywords for this: signals, slots and emit. There is an option to turn this of, and use the macros Q_SIGNAL/S,Q_SLOT/S and Q_EMIT instead: CONFIG += no_keywords. This allows to use 3rd party libraries which use these terms, e.g. boost::signal. Qt signal/slot implementation is thread safe, so that you can use it to send messages between different QThreads, this is especially important, as anything UI related should run in the main thread of Qt, anything that could block your UI should not run in this thread, so running jobs in a QThreadPool and emitting the finished result as a signal is a common pattern. Maybe I will touch this in a later post...

For now, lets see the basics of using signals and slots in Qt. This is the code from my MainWindow class constructor, connecting several signals to slots:

So, the traditional, moc driven connect method is QObject* derived sender, the SIGNAL macro defining the signal to connect to, followed by the QObject* derived receiver, then SLOT(...) is the last argument, naming the slot to connect to. There is a fifth defaultet parameter: the ConnectionType. The last line contains the new, lambda based connection option, where you again have the sender and its slot, this time as a method-pointer, and then followed by a lambda acting as the receiving slot.

This syntax can lead to a rare error, when ever a signal is overloaded, like QComboBox::currentIndexChanged, which is available with an int or QString parameter. Then you'll need an ugly static_cast to tell the compiler which version you'd like:

In this case I didn't even needed the argument from the slot. It is fairly easy to use your own signals and slots, all you need is a QObject derived class, which is processed by the moc. Mostly of course you already have classes derived from QObject indirectly, which then use signals and slots, like the page panel class:

So, slots and signals are normal member functions, declared after the qt-specific keyword signals/slots. When you want to emit a signal, its enough to just write 'emit my_signal();', and all observers on this signal will get notified. Slots are often used to react to certain events in the UI, like the currentIndexChanged signal in this case. In the widget editor of QtCreator you get an overview of available signals when right clicking and selecting 'go to slot...', this will create a slot for this signal in your QWidget derived class.

There is also the option to map certain widgets to certain values when a signal fires, this is done via QSignalMapper. I use this in a different program to have one widget for editing flag like settings, where each flag is a bit in a settings value:

The constructor only takes a QStringList for the option names, and an int for how many columns of check boxes the current use case should have. The QSignalMapper is a member variable, and each QCheckBox connects its clicked signal to the map() slot of QSignalMapper. With setMapping the connection between the sender and the value is set up. QSignalMapper offers int, QObject*, QWidget* and QString as mapping values. QVariant or a generic interface is not provided by Qt. In the clicked slot I simply toggle the bit for the corresponding flag.

When working in Qt, most of it types provide support for signals and slots through deriving from QObject, which offers connect/disconnect methods to manage your slot connections. This brings again the disadvantages of QObject and the moc, as templates can't be used in this context, all classes using signal/slot must be concrete classes. Deriving your classes from templates (CRTP e.g.) can help here to mix in a generic layer.

Qt Signal Slot Example

While Qt is fairly well prepared to manage its own messaging needs, what alternatives exist, that could be used in the non Qt related code? The C++ standard offers currently only std::function, which can be used to implement a callback mechanism. But this has its limitations, of a 1:1 or 1:many connection this is a viable option. I use it to notify my MainWindow class that a node in the tree has changed its name. Also its useful to implement classes which execute a callback in a certain context, like EventFilter in the last blog post in this series. But std::function is not an implementation of the observer pattern, and implementing your own with it would be reinventing the wheel. Boost has had for a long time a signal library, which now is available as version 2: boost::signals2.

Using boost::signals2

Honestly, if I could avoid using signals2, I would, as it has one certain disadvantage: build times increase. So far my project is kind of small, has only a few classes, which most of are less then 100 loc. Adding boost::signals2 to a class makes it hard to build a project quickly for debugging or just seeing if the work of the past hour still compiles.

The need for signals2 came in my application, when I began to understand, that there are some events, which go from the Qt layer into the boost/standard C++ layer, and then need to travel back into the Qt layer. Each Page has a shared_ptr to a layout object, which is part of a LayoutItem holding the list of layouts for a document. There is one LayoutPanel to edit, create and delete layouts in LayoutItem, and each PagePanel has a QComboBox, so that the user can select the layout for the page. Now, when a user creates/renames a layout, each PagePanel needs to be notified, but when it gets deleted, also page needs to change. This could be implemented in the Qt layer, each Qt class involved has access to the boost/C++ layer, and can make the necessary changes. But then, this important business logic of removing a layout will only work through the UI. When I use boost::signals2, it can be done in the boost/standard C++ layer.

boost::signals2 has a signal template, which has the signature as the argument, this signal type also then has the typedef for the slot type, signal::connect returns a connection object:

When ever an object subscribes to the layout signals, it must to so for all three, the vector should invoke RVO. Currently, PagePanel is the only subscriber, it simply connects to the signals using boost::bind:

One detail here is, that I do use scoped_connection, which will call disconnect() on its destruction, while the default boost::signals2::connection class does not. scoped_connection can be moved, but not copied. But once it is in the vector, it will stay there. Also, you should forward declare the connection classes, so that you don't have to include the boost/signals2.hpp headers, this prevents leaking into other sources.

But boost::signals2 can do far more. I have no use for code that depends on the order of slots called, but you can specify this with signal::contect(int group, slot):

Qt Signal And Slots

In some context it is interesting to handle the return value of a signal, for this boost::signal2 offers a combiner, which is the second template parameter to signal: signal<float(float,float), aggregate_combiner<std::vector<float> > >. This combiner then also overwrites the return value of the signal, which is now std::vector instead of float. Another feature is that you can block a connection with shared_connection_block.

Qt Signal Thread

boost::signal2 is currently header only, thread safe and offers a few more customization points, for example you can change the mutex, but also the signature type, which currently is boost::function.

Alternatives to boost::signals2

If you know very well what you are doing, you could use boost::signal instead of its new version, signals2. This might improve your compile times, but boost::signals is not any more maintained. Also, while signals2 is header-only, signals is not. The thread safety is a key feature of signals2, which at some time sooner or later will come into play in your code base. I don't want to introduce a 3rd party library into my project just to have signaling/observer pattern, but you should know, that there are a few alternatives (I googled that too):

  • libsigslot
    • has open bugs from 2003 - 2011, memory leaks and other issues. But seems to do the job.
  • libsigc++
    • a standard C++ implementation, inspired by Qt, you (might) have to derive your objects from a base class. Virtual function calls are the base of this library it seems, at least for method slots, which the call has to be derived from sigc::trackable.
    • gtkmm and glibmm seem to use this for their signaling needs.
    • the 5 open bugs seem to be feature requests mostly (and nil is a keyword in Object-C, well...)
    • the library has been rewritten using modern C++ idioms (claims the site)
  • This codeproject article from 2005 gives some insights, but C++11 changes some of them I think.
  • slimsig
    • seems to be a header only alternative to boost::signals2
    • 2 open bugs, no change in one year
  • boost::synapse
    • this library is proposed for boost, but has not yet been reviewed.
    • I think it could be a more lightweight alternative to signals2
    • Currently its not threadsafe.

The only disadvantage of boost::signal2 is really its impact on compile and link time, which can be reduced through pimple and other isolation techniques, so that a recompilation is only triggered when really needed. One idea which came in my mind during this blog post is a std_signal2 header, which replaces the boost types (function, mutex etc.) with the corresponding std types. I'm not sure how this would work out, but boost::signals2 seems to be pretty well build to do this, a lot of template parameters have default values which then configure the library, and are hidden from the day to day usage.

Join the Meeting C++ patreon community!
This and other posts on Meeting C++ are enabled by my supporters on patreon!

Copyright Meetingcpp GmbH 2020 ImprintPiwik Opt outPrivacy Policy