Table of Contents
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.
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
.
[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.