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 | |
Notifier (const Notifier &)=delete | |
Notifier & | operator= (const Notifier &)=delete |
bool | in_main_thread () noexcept |
void | emit () |
void | operator() () |
Callback::SafeFunctor | connect (const Callback::SafeFunctor &f) |
Callback::SafeFunctor | connect (const Callback::SafeFunctor &f, Releaser &r) |
template<class F , class = typename std::enable_if<!std::is_convertible<typename std::remove_reference<F>::type, const Callback::SafeFunctor>::value>::type> | |
Callback::SafeFunctor | connect (F &&f) |
template<class F , class = typename std::enable_if<!std::is_convertible<typename std::remove_reference<F>::type, const Callback::SafeFunctor>::value>::type> | |
Callback::SafeFunctor | connect (F &&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. Callable objects (such as lambda expressions or the return value of std::bind) or Callback::SafeFunctor objects may be connected to the Notifier (referred to be below as "connected callables"), which are 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 callables 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 connected callables will execute (which is the thread in which the default glib program event loop executes), executing them 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 Notifier object is invoked by the same thread as that in which the connected callables will execute, this is detected and they 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 the connected callable concerned 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 callables 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 2.0.15, 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 connected callables, a check is made for a case where between the signalling thread invoking a Notifier object and the main program event loop executing the callables, 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 callables 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 callables have been executed. Likewise, a Releaser object cannot offer protection if the remote object whose non-static method is represented or called into by a connected callable is destroyed by another thread while the main program loop is in the middle of executing the callable. When the main loop begins executing a callable, the remote object must either wholly exist (in which case the callable will be invoked) or have been destroyed (in which case the callable 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 connected callable, the AsyncQueue class can be employed.
Usage
This is an example:
Callback::SafeFunctor objects may be connected to a notifier, and the connect() method may be directly initialized with the result of Callback::make(), Callback::make_ref() or Callback::lambda() and implicit conversion will take place. Here is an example using Callback::make_ref(), with a class object my_obj of type MyClass, with a method void MyClass::my_method(int, const Something&):
|
delete |
This class cannot be copied. The copy constructor is deleted.
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 and std::hash<T*>::operator()() does not throw (as it would not on any sane implementation). 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. |
|
inline |
Connects a callable object, such as formed by a lambda expression or the result of std::bind.
f | The callable object to connect. It must be fully bound (that is, take no arguments when called). |
std::bad_alloc | The method might throw std::bad_alloc if memory is exhausted and the system throws in that case. If might also throw if the copy or move constructor of the callable object throws. |
Since 2.1.0
|
inline |
Connects a callable object, such as formed by a lambda expression or the result of std::bind.
f | The callable object to connect. It must be fully bound (that is, take no arguments when called). |
r | A Releaser object for automatic disconnection of the callable object if an object whose method it represents or calls into is destroyed. |
std::bad_alloc | The method might throw std::bad_alloc if memory is exhausted and the system throws in that case. If might also throw if the copy or move constructor of the callable object throws. |
Since 2.1.0
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). |
|
inlinenoexcept |
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). |
This class cannot be copied. The assignment operator is deleted.
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. |