Table of Contents
glibmm provides the normal set of thread launching functions, mutexes, condition variables and scoped locking classes required for writing multi-threaded programs using C++.
However, care is required when writing programs based on gtkmm using
multiple threads of execution, arising from the fact that
libsigc++, and in particular
sigc::trackable
, are not thread-safe. That's
because none of the complex interactions that occur behind the scenes
when using libsigc++ are protected by a
mutex or other means of synchronization.
[1]
This requires a number of rules to be observed when writing
multi-threaded programs using gtkmm. These are set out below, but
one point to note is that extra care is required when deriving classes
from sigc::trackable
, because the effects are
unintuitive (see particularly points 4 and 5 below).
Use Glib::Dispatcher
to invoke gtkmm functions
from worker threads (this is dealt with in more detail in the next
section).
A sigc::signal
object should be regarded as
owned by the thread which created it. Only that thread should connect
a sigc::slot
object to the signal object, and
only that thread should emit()
or call
operator()()
on the signal, or null any
connected sigc::slot
object. It follows
(amongst other things) that any signal object provided by a gtkmm
widget should only be operated on in the main GUI thread and any
object deriving from sigc::trackable
having its
non-static methods referenced by slots connected to the signal object
should only be destroyed in that thread.
Any sigc::connection
object should be regarded
as owned by the thread in which the method returning the
sigc::connection
object was called. Only that
thread should call sigc::connection
methods on
the object.
A sigc::slot
object created by a call to
sigc::mem_fun()
which references a method of a
class deriving from sigc::trackable
should
never be copied to another thread, nor destroyed by a different thread
than the one which created it. (One consequence of this is that
Glib::Threads::Thread::create()
should not be
called with a slot argument created by a call to
sigc::mem_fun()
which represents a method of such
a class. It is however safe to pass
Glib::Threads::Thread::create()
a function
object representing such a method by using, say,
boost::bind()
or, in C++11,
std::bind()
or a C++11 lambda expression.)
If a particular class object derives from
sigc::trackable
, only one thread should create
sigc::slot
objects representing any of the
class's non-static methods by calling
sigc::mem_fun()
. The first thread to create such
a slot should be regarded as owning the relevant object for the
purpose of creating further slots referencing any
of its non-static methods using that function, or nulling those slots
by disconnecting them or destroying the trackable object.
Although glib is itself thread-safe, any
glibmm wrappers which use
libsigc++ will not be. So for example, only
the thread in which a main loop runs should call
Glib::SignalIdle::connect()
,
Glib::SignalIO::connect()
,
Glib::SignalTimeout::connect()
,
Glib::SignalTimeout::connect_seconds
for that main loop, or manipulate any
sigc::connection
object returned by them.
The connect*_once() variants,
Glib::SignalIdle::connect_once()
,
Glib::SignalTimeout::connect_once()
,
Glib::SignalTimeout::connect_seconds_once()
,
are thread-safe for any case where the slot is not created by a call to
sigc::mem_fun()
which represents a method of a class
deriving from sigc::trackable
. This is similar to
Glib::Threads::Thread::create()
as mentioned in point 4.
[1]
These interactions arise from the fact that, amongst other things, a
class inheriting from sigc::trackable
will, via
that inheritance, have a std::list
object
keeping track of slots created by calls to
sigc::mem_fun()
representing any of its
non-static methods (more particularly it keeps a list of callbacks
which will null the connected slots on its destruction). Each
sigc::slot
object also keeps, via
sigc::slot_rep
, its own
sigc::trackable
object to track any
sigc::connection
objects which it needs to
inform about its demise, and also has a function to deregister itself
from any sigc::trackable
on disconnection or
destruction. sigc::signal
objects also keep
lists of slots, which will be updated by a call to their
connect()
method or calls to any
sigc::connection
object relating to such a
connection.