Writing multi-threaded programs using c++-gtk-utils

This library has been written with threading in mind. The documentation indicates which components are thread-safe and which are not: generally any components which need to be thread-safe for useful multi-threaded program design are thread-safe, or have a thread-safe option.

glib, including any glib main loop, is thread-safe provided that, if glib < 2.32 is installed, g_thread_init() has been called. Any multi-threaded program using c++-gtk-utils with glib < 2.32 will in practice want to call g_thread_init(), and prior to glib version 2.24, g_thread_init() had to be called before any other glib function. From glib version 2.24, this requirement is relaxed so that g_thread_init() can be called before any glib call is made to which thread safety is relevant (see the glib documentation for further particulars). From glib version 2.32, g_thread_init() does not need to be called for glib to be thread-safe (g_thread_init() is a no-op retained for API/ABI compatibility reasons).

The X11 backend for gtk and gdk is thread-aware but not of itself thread safe. With the X11 backend, gtk and gdk functions can be called by more than one thread if the gdk global lock is initialised by a call to gdk_threads_init() and calls to gtk and gdk are made within the lock by appropriate use of gdk_threads_enter() and gdk_threads_leave(). https://developer.gnome.org/gdk2/stable/gdk2-Threads.html#gdk2-Threads.description gives futher details. In a program which uses gdk_threads_enter() and gdk_threads_leave() in order to invoke the global lock, any calls to the public functions of Cgu::WinBase, and any creating of Cgu::WinBase or Cgu::MainWidgetBase objects so invoking their constructors or destroying them explicitly or implicitly and so invoking their destructors, and any calls to most public functions of Cgu::Application, must be within the global lock. With that in mind, it should be noted that any callbacks executed by Cgu::Notifier where the Notifier object is emitted() on (or operator()() invoked) by other than the main program thread, and any callbacks executed by Cgu::Callback::post(), Cgu::start_iowatch(), Cgu::start_timeout(), Cgu::start_timeout_seconds(), Cgu::Thread::Future::when(), Cgu::Thread::TaskManager::make_task_when(), Cgu::Thread::TaskManager::make_task_when_full(), Cgu::Thread::TaskManager::make_task_compose(), Cgu::Thread::TaskManager::make_task_packaged_when() or Cgu::Thread::TaskManager::make_task_packaged_compose() are not executed within the global lock (so the callback function should itself invoke gdk_threads_enter()/gdk_threads_leave() where relevant).

However, from gtk-3.6 use of the gdk global lock has been deprecated, and it is unnecessary with c++-gtk-utils because this library provides Cgu::Notifier objects and Cgu::Callback::post(), Cgu::Thread::Future::when(), Cgu::Thread::TaskManager::make_task_when(), Cgu::Thread::TaskManager::make_task_when_full(), Cgu::Thread::TaskManager::make_task_compose(), Cgu::Thread::TaskManager::make_task_packaged_when() and Cgu::Thread::TaskManager::make_task_packaged_compose() functions, which will enable a worker thread to cause a callback object to be executed by the main GUI thread. If all gtk/gdk functions to be invoked in a program by a worker thread are invoked by a callback object executed via one of these objects or functions, the gdk global lock can be ignored, and there is no need to call gdk_threads_init(), gdk_threads_enter() or gdk_threads_leave() in the program.

Approaching program design this way will also generally result in a cleaner design and is much less error-prone. Calls to gtk and gdk functions would always be thread safe because they would always be made in the same thread (the main GUI thread).

The documentation on the Cgu::Thread::Thread class gives further information on the relationship between pthreads (as used by this library), GThreads and C++11/14/17 threads