c++-gtk-utils
|
Provides thread-safe signalling between a worker thread and the main program thread. More...
#include <c++-gtk-utils/notifier.h>
Public Member Functions | |
bool | in_main_thread () |
void | emit () |
void | operator() () |
Callback::SafeFunctor | connect (const Callback::SafeFunctor &f) |
Callback::SafeFunctor | connect (const Callback::SafeFunctor &f, Releaser &r) |
void | disconnect (const Callback::SafeFunctor &f) |
void | block (const Callback::SafeFunctor &f) |
void | unblock (const Callback::SafeFunctor &f) |
Notifier () | |
~Notifier () | |
Static Public Member Functions | |
static void | init () |
Provides thread-safe signalling between a worker thread and the main program thread.
The Notifier class provides thread-safe signalling between two threads. It does this through a pipe, to which an GSource (iowatch) object is attached to connect it to the glib program event loop. A functor is connected to the notifier, which is called in the receiving thread via the program event loop when operator()() (or emit()) is called on the Notifier object by the signalling thread. It therefore behaves like a SafeEmitter object, except that connected functors execute in the glib program event loop thread rather than in the thread which calls operator()()/emit().
It is an alternative to the Callback::post() function in callback.h, and the documentation on callback.h contains a description of relevant trade-offs between the two.
If the signalling thread is the same thread as that in which the functor connected to it will execute (which is the thread in which the default glib program event loop executes), executing it via the pipe would risk a deadlock - if the pipe fills up, the thread would block on write and never be able to read from the pipe to empty it. Accordingly, if the object is invoked by the same thread as that in which the functor will execute, this is detected and the functor will be invoked directly, rather than via the pipe. Therefore, actions so invoked may be out of order with those invoked by the other threads.
If a Releaser object is passed as the second argument of Notifier::connect(), then a connected functor will automatically be disconnected if the object which has the Releaser object as a member is destroyed.
The main use of Notifier objects is for a worker thread to signal an event to the main thread in which GTK+ is executing, which implies that GTK+ should also be executing in the default glib program event loop (GMainContext) (as will almost always be the case), which is the one with which the program first starts. Before a Notifier object is first used, it is a requirement that Notifier::init() (a static member function) be called in the thread in which the default glib event loop executes, and any connected functors will execute in that thread. Notifier::init() only needs to be called once at program start-up - it doesn't need to be called separately for each Notifier object, and can be called before any Notifier objects have been constructed. If it has not been called before the construction of the first Notifier object has taken place, it will occur automatically on that first construction. That means that if the first Notifier object is not constructed in the main (event loop) thread of the program, then Notifier::init() must be called explicitly before that first object is constructed. In addition, if glib < 2.32 is installed, before Notifier::init() is called (or the first Notifier object created) g_thread_init(0) should have been called: as a result a Notifier object cannot be a global (non-local) static object with glib < 2.32 (glib >= 2.32 does not require g_thread_init() to be called to be thread safe). Note also that global static Notifier objects are not safe prior to version 1.2.28, even with glib >= 2.32.
It is a good idea that Notifier::init() should have been called (or the first Notifier object constructed) before the main program thread creates any new threads. Then the state of initialisation effected by Notifier::init() will automatically be visible between threads.
When executing a functor connected to a Notifier object, a check is made for a case where between the signalling thread invoking a Notifier object and the main program event loop calling that functor, the Notifier object ceases to exist. However there can still be a race condition if the lifetime of the Notifier object is determined outside the thread of execution of the main program event loop and a Notifier object is destroyed by that other thread between the time the check is made and the functor executed. Normally Notifier objects are constructed and destroyed in the main program thread, but where that is not the case the user will need to take this into account and if need be provide appropriate synchronisation to secure the lifetime of the Notifier object until after the functor has been called. Likewise, a Releaser object cannot offer protection if the remote object whose non-static method is represented by a connected functor is destroyed by another thread while the main program loop is in the middle of executing the functor. When the main loop begins invoking the execution of the callback, the remote object must either wholly exist (in which case the callback will be invoked) or have been destroyed (in which case the callback will be ignored), and not be in some transient half-state governed by another thread.
Apart from that, the Notifier object is thread-safe and any of its methods may be invoked in any thread. (It is as thread-safe as a SafeEmitter object, as described in emitter.h, which contains further details on thread safety.)
To pass variable data to a functor executed by the Notifier object, the AsyncQueue class can be employed.
Cgu::Notifier::Notifier | ( | ) |
The constructor is thread safe provided init() has previously been called before the main program thread creates any new threads.
std::bad_alloc | The constructor might throw std::bad_alloc if memory is exhausted and the system throws in that case. |
PipeError | PipeError can be thrown if this is the first Notifier object to be constructed and Notifier::init() has not previously been called. |
Cgu::Notifier::~Notifier | ( | ) |
The destructor does not throw provided the destructors of any bound arguments do not throw. It is thread safe (but see the comments in the introductory remarks above about race conditions where the lifetime of a Notifier object is determined by a thread other than the main program thread).
void Cgu::Notifier::block | ( | const Callback::SafeFunctor & | f | ) |
Blocks a connected functor from executing in the main program thread when emit() or operator()() is called until unblock() is called. This method does not throw (assuming that merely iterating through a list does not throw, as it would not on any sane implementation). It is thread safe.
f | The functor to block. |
Callback::SafeFunctor Cgu::Notifier::connect | ( | const Callback::SafeFunctor & | f | ) |
Connects a functor. It is thread safe.
f | The functor to connect. |
std::bad_alloc | The method might throw std::bad_alloc if memory is exhausted and the system throws in that case. |
Callback::SafeFunctor Cgu::Notifier::connect | ( | const Callback::SafeFunctor & | f, |
Releaser & | r | ||
) |
Connects a functor. It is thread safe.
f | The functor to connect. |
r | A Releaser object for automatic disconnection of the functor if the object whose method it represents is destroyed. |
std::bad_alloc | The method might throw std::bad_alloc if memory is exhausted and the system throws in that case. |
void Cgu::Notifier::disconnect | ( | const Callback::SafeFunctor & | f | ) |
Disconnects a functor previously connected. This does not throw provided that the destructors of any bound arguments do not throw (as they should not do), and assuming that merely iterating through a list does not throw (as it would not on any sane implementation). It is thread safe.
f | The functor to disconnect. |
void Cgu::Notifier::emit | ( | ) |
This will cause the connected functors to be executed in the main program thread. It is thread safe (but see the comments in the introductory remarks above about race conditions where the lifetime of a Notifier object is determined by a thread other than the main program thread, and about protection by a Releaser object where a connected remote object is destroyed in mid-emission by another thread).
std::bad_alloc | The method might throw std::bad_alloc if memory is exhausted and the system throws in that case, and this method is called in the thread in which the functors will execute (the main program thread). In addition, it will throw if the function or class methods represented by the functors throw (or if the assignment operator of a bound argument throws) and the call is made in that thread. If called in a different thread it will not throw (in that case, an exception thrown by a connected functor will be consumed to protect the glib main loop and the iowatch dispatcher will issue a g_critical() warning). |
|
inline |
A utility which tells the caller whether it is in the thread in which the callback will execute (the main program thread). It will not throw. It is thread safe.
|
static |
Initialises the program for the use of Notifier objects. It only needs to be called once at program start-up (it doesn't need to be called separately for each Notifier object), and can be called before any Notifier objects have been constructed. It should be called in the thread in which the default main glib event loop executes (the main program thread) before that thread creates any new threads.
|
inline |
This will cause the connected functors to be executed in the main program thread. It is thread safe (but see the comments in the introductory remarks above about race conditions where the lifetime of a Notifier object is determined by a thread other than the main program thread, and about protection by a Releaser object where a connected remote object is destroyed in mid-emission by another thread).
std::bad_alloc | The method might throw std::bad_alloc if memory is exhausted and the system throws in that case, and this method is called in the thread in which the functors will execute (the main program thread). In addition, it will throw if the function or class methods represented by the functors throw (or if the assignment operator of a bound argument throws) and the call is made in that thread. If called in a different thread it will not throw (in that case, an exception thrown by a connected functor will be consumed to protect the glib main loop and the iowatch dispatcher will issue a g_critical() warning). |
void Cgu::Notifier::unblock | ( | const Callback::SafeFunctor & | f | ) |
Unblocks a previously blocked functor. This method does not throw (assuming that merely iterating through a list does not throw, as it would not on any sane implementation). It is thread safe.
f | The functor to unblock. |