Lumiera  0.pre.03
»edit your freedom«
ui-dispatcher.hpp File Reference

Go to the source code of this file.

Description

Allow dispatch of self-contained code blocks (lambdas) into the main UI event thread.

GTK is not threadsafe by design – thus it is mandatory to dispatch any asynchronous invocations from external facilities in a controlled way into the main event loop. Unfortunately, this becomes a tricky undertaking when these external invocations need to pass argument data. This helper serves to accommodate such problems, relying on the automatic (heap based) argument storage of std::function. Client code provides the actual invocation in the form of completely closed lambdas.

Warning
these lambdas will be stored in a synchronised queue and invoked out of the original call stack. It is the client's responsibility to ensure that all bindings in the closure are either by value, or by smart-ptr, or alternatively to ensure the lifecycle of any referred entity exceeds the lifespan of the UI-Loop. Since the shutdown-order of Lumiera's subsystems is not deterministic, this rules out passing references to anything tied to some subsystem lifecycle. Referring to a static singleton is acceptable though.

implementation considerations

Basically the implementation relies on the standard mechanism for multithreaded UI applications. But on top we use our own dispatcher queue to allow passing arbitrary argument data, based on the argument storage of std::function. Which in the end effectively involves two disjoint thread collaboration mechanisms:

  • the caller creates a closure of the operation to be invoked, binding all arguments by value
  • this closure is wrapped into a std::function instance
  • which in turn is added into the dispatcher queue. Depending on the implementation, this might incur explicit locking
  • after successfully enqueuing the closure, the GTK event thread is signalled through the Glib-Dispatcher, which actually messages through an OS-pipe (kernel based IO)
  • the Dispatcher need to be created within the UI event thread (which is the case, since all of Lumiera's UI top-level context is created in the thread dedicated to run GTK)
  • relying on internal GLib / GIO magic'', the dispatcher hooks into the respective GLib main context'' to ensure this signalling is picked up from the event thread, which...
  • ...finally leads to invocation of the Dispatcher's signal from within the event loop

This hybrid approach is rather simple to establish, but creates additional complexities at runtime. More specifically, we have to pay the penalty of chaining the overhead and the inherent limitations of two thread signalling mechanisms (in our dispatcher queue and within kernel based IO). It would be conceivable to implement all of the hand-over mechanism solely within our framework, yet unfortunately there seems to be no easily accessible and thus ``official'' way to hook into the event processing, at least without digging deep into GLib internals.

Note
as detailed in the documentation for Glib-Dispatcher, all instances built within a given receiver thread (here the UI event loop thread) will share a single pipe for signalling. Under heavy load this queue might fill up and block the sender on dispatch.
See also
NotificationService
CallQueue_test

Definition in file ui-dispatcher.hpp.

#include "stage/gtk-base.hpp"
#include "lib/call-queue.hpp"
#include "lib/format-string.hpp"
#include "lib/nocopy.hpp"
#include <utility>

Classes

class  UiDispatcher
 Helper to dispatch code blocks into the UI event thread for execution. More...
 

Functions

string generateErrorResponse (const char *problem="unexpected problem")
 

Namespaces

 stage
 Lumiera GTK UI implementation root.
 
 stage::ctrl
 Backbone of the Lumiera GTK UI.