diff --git a/src/gui/ctrl/ui-dispatcher.hpp b/src/gui/ctrl/ui-dispatcher.hpp index e022cba2b..603bf75cb 100644 --- a/src/gui/ctrl/ui-dispatcher.hpp +++ b/src/gui/ctrl/ui-dispatcher.hpp @@ -36,6 +36,37 @@ ** 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][Gtkmm-tutorial] for multithreaded + ** UI applications. But on top we use our own [dispatcher queue](\ref lib::CallQueue) 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. + ** + ** [Glib-Dispatcher]: https://developer.gnome.org/glibmm/2.42/classGlib_1_1Dispatcher.html + ** [GTKmm-tutorial]: https://developer.gnome.org/gtkmm-tutorial/3.12/sec-using-glib-dispatcher.html.en ** @see NotificationService ** @see CallQueue_test */ @@ -48,22 +79,21 @@ #include "lib/call-queue.hpp" #include -//#include #include namespace gui { namespace ctrl { -// using std::string; using std::move; -// class GlobalCtx; /** * Helper to dispatch code blocks into the UI event thread for execution. * Implementation of the actual dispatch is based on `Glib::Dispatcher` + * @warning any UiDispatcher instance must be created such as to ensure + * it outlives the GTK event loop */ class UiDispatcher : boost::noncopyable diff --git a/src/gui/notification-service.cpp b/src/gui/notification-service.cpp index d611f1ced..90b873943 100644 --- a/src/gui/notification-service.cpp +++ b/src/gui/notification-service.cpp @@ -24,9 +24,25 @@ ** Implementation of notifications and updates within the GUI. ** This is the actual service implementation and runs within the GUI plugin. ** - ** @todo 1/2017 actually forward invocations on the UI-Bus + ** Since GTK is _not threadsafe by design,_ any external invocation passed through + ** this facade service will be dispatched explicitly into the GTK event loop thread. + ** The implementation of this dispatch is based upon `Glib::Dispatcher` and thus + ** requires this service instance to be created from within the thread performing + ** the GTK event loop. Moreover, to avoid segmentation faults on shutdown, the + ** lifespan of this service instance must exceed the running of the event loop, + ** since otherwise the event loop might invoke a lambda bound to the `this` + ** pointer of a NotificationService already decommissioned. The setup of the + ** standard Lumiera UI top-level context ensures this is the case, since the + ** UiManager::performMainLoop() maintains the NotificationService instance + ** and also performs the blocking `gtk_main()` call. Consequently, any + ** invocation added from other threads after leaving the GTK main loop + ** but before closing the GuiNotification facade will just be enqueued, + ** but then dropped on destruction of the UiDispatcher PImpl. + ** ** @todo 1/2017 find a solution for passing diff messages /////////////////////////////////////////////////TICKET #1066 ** + ** @see ui-dispatcher.hpp + ** */ @@ -73,7 +89,7 @@ namespace gui { void NotificationService::displayInfo (string const& text) { - INFO (gui, "@GUI: display '%s' as notification message.", cStr(text)); + INFO (gui, "@GUI: display '%s' as notification message.", cStr(text)); ///////////////////////////////////TICKET #1102 : build a message display box in the UI ////////////////////////TODO actually push the information to the GUI ///////////////////////////////////TICKET #1098 : use a suitable Dispatcher } diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index a2388bc6f..5d76091d5 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -1034,11 +1034,17 @@ - - - - - + + + + + + + + + + + @@ -1048,7 +1054,9 @@ - + + + @@ -1073,9 +1081,6 @@ - - -