From 06a61773fafe35937b49afd67ef17b65156f3cdf Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Fri, 20 Jan 2017 01:54:49 +0100 Subject: [PATCH] Nexus/CoreService: consider handling of bus connections. Now I've realised that there are two degrees of connectedness. It is very much possible to have a "free standing" BusTerm, which only allows to send uplink messages. In fact, this is how CoreService is implemented, and probably it should also the way how to connect the GuiNotification service... --- src/gui/ctrl/bus-term.hpp | 24 +++-- src/gui/ctrl/core-service.hpp | 47 ++++++--- src/gui/ctrl/nexus.hpp | 6 ++ src/gui/model/tangible.hpp | 2 +- wiki/renderengine.html | 18 +++- wiki/thinkPad.ichthyo.mm | 193 +++++++++++++++++++++++----------- 6 files changed, 205 insertions(+), 85 deletions(-) diff --git a/src/gui/ctrl/bus-term.hpp b/src/gui/ctrl/bus-term.hpp index c65ab3acb..377c4f927 100644 --- a/src/gui/ctrl/bus-term.hpp +++ b/src/gui/ctrl/bus-term.hpp @@ -34,14 +34,23 @@ ** which it will be wired. Moreover, each BusTerm bears a distinct ** [identity](\ref ::endpointID_), which is used as _implicit subject_ ** for emanating messages, or as explicit destination for routing. - ** The whole [UI-Bus](\ref BusController) is built to perform within the + ** The whole [UI-Bus](\ref ui-bus.hpp) is built to perform within the ** UI event thread and thus is _not threadsafe_. For that reason, ** the automatic detachment built into each BusTerm's dtor is ** sufficient to ensure sane connectivity. ** - ** @todo as of 11/2015 this is complete WIP-WIP-WIP + ** @note BusTerm *disconnects itself automatically* on destruction. + ** However, it is *not attached automatically*. It _does require_ + ** a reference to the bus on construction, which by default places + ** the BusTerm instance into a _semi connected_ state: the BusTerm + ** is able to send messages to the bus, but the Nexus (hub) does + ** not know the BusTerm by ID and thus is not able to direct + ** messages towards this BusTerm. Contrast this to a Tangible, + ** which is constructed in a way to ensure it is always has a + ** bidirectional communication link to the Nexus. ** ** @see [BusTerm_test] + ** @see Tangible ** */ @@ -51,8 +60,6 @@ #include "lib/error.hpp" -//#include "lib/symbol.hpp" -//#include "lib/util.hpp" #include "lib/idi/entry-id.hpp" #include "lib/diff/gen-node.hpp" @@ -69,8 +76,6 @@ namespace ctrl{ class MutationMessage; -// using lib::HashVal; -// using util::isnil; using lib::idi::EntryID; using lib::diff::GenNode; using std::string; @@ -127,7 +132,10 @@ namespace ctrl{ /** may be moved, but not copied, * due to the embedded identity */ - BusTerm(BusTerm&&) = default; + BusTerm(BusTerm&&) = default; + BusTerm(BusTerm const&) = delete; + + BusTerm& operator= (BusTerm const&) = delete; protected: /** @@ -150,7 +158,7 @@ namespace ctrl{ }; - + /** record state mark from this subject */ inline void diff --git a/src/gui/ctrl/core-service.hpp b/src/gui/ctrl/core-service.hpp index d4530b060..0b0135de8 100644 --- a/src/gui/ctrl/core-service.hpp +++ b/src/gui/ctrl/core-service.hpp @@ -39,6 +39,37 @@ ** both CoreService and Nexus are mutually interdependent from an operational ** perspective, since they exchange messages in both directions. ** + ** ## Bus connection and topology + ** The CoreService plays a central role within the UI, since it represents + ** _»the application core«_ from the UI layer's viewpoint. But it is not + ** the bus manager or central router, a role fulfilled by ctrl::Nexus, + ** the central UI-Bus hub. Every node which has been added into the + ** routing table in Nexus, can be addressed as a _first class citizen,_ + ** that is, we're able to direct messages towards such an element, knowing + ** only it's ID. But there is a twist: all connections to the Bus are made + ** from [bus terminals](ctrl::BusTerm), and each _node_, i.e. each + ** [tangible model element](model::Tangible) has a BusTerm member and + ** thus inherits the ability to talk to the bus. But only when _actively_ + ** connected to the bus, a full link and entry in the routing table is + ** established. The constructor of model::Tangible indeed makes such + ** a connection right away, while any "free standing" BusTerm just + ** knows how to talk to the Bus _upstream,_ without establishing + ** a full link to receive also _downstream_ messages. + ** + ** And _the fine point to note is_ that CoreService just incorporates + ** a free standing BusTerm, without registering it with the Nexus. + ** Doing so would be pointless, since CoreService in fact is not a + ** regular Tangible, rather it fulfils a very special purpose within + ** the UI. Most of the UI-Bus messages would not make much sense when + ** directed towards the CoreService. Rather, CoreService _acts as upstream_ + ** for the Nexus, and thus gains the ability to respond to those few special + ** messages, which can not be handled in a generic way on the Nexus: + ** - *act* handles command invocation within the Session core, and + ** is treated by [forwarding](command-handler.hpp) it over the + ** SessionCommand facade to the [Proc-Dispatcher](proc-dispatcher.hpp) + ** - *note* observes and captures presentation state note messages, which + ** are to be handled by a central presentation state manager (TODO 1/17). + ** ** @see AbstractTangible_test ** @see BusTerm_test ** @@ -80,15 +111,6 @@ namespace ctrl{ Nexus uiBusBackbone_; NotificationService activateNotificationService_; - /** - * This service establishes some hard wired connections to the UI-Bus. - * Because baseclass BusTerm automatically disconnects at destruction, - * the member NotificationService and this object (CoreService) are - * still connected to the nexus, when the sanity check in our dtor runs. - * But all other UI elements outside of the scope of this should already - * be disconnected at that point... - */ - enum { NUMBER_OF_CONNECTED_DIRECT_MEMBERS = 2 };//!< NUMBER_OF_CONNECTED_DIRECT_MEMBERS virtual void act (GenNode const& command) override @@ -114,13 +136,6 @@ namespace ctrl{ { INFO (gui, "UI-Backbone operative."); } - - ~CoreService() - { - if (uiBusBackbone_.size() > NUMBER_OF_CONNECTED_DIRECT_MEMBERS) - ERROR (gui, "Some UI components are still connected to the backbone."); - } - }; diff --git a/src/gui/ctrl/nexus.hpp b/src/gui/ctrl/nexus.hpp index 8e1c8d26c..74eaabb10 100644 --- a/src/gui/ctrl/nexus.hpp +++ b/src/gui/ctrl/nexus.hpp @@ -182,6 +182,12 @@ namespace ctrl{ Nexus (BusTerm& uplink_to_CoreService, ID identity =lib::idi::EntryID()) : BusTerm(identity, uplink_to_CoreService) { } + + ~Nexus() + { + if (0 < size()) + ERROR (gui, "Some UI components are still connected to the backbone."); + } }; diff --git a/src/gui/model/tangible.hpp b/src/gui/model/tangible.hpp index e4a358029..61573d007 100644 --- a/src/gui/model/tangible.hpp +++ b/src/gui/model/tangible.hpp @@ -173,7 +173,7 @@ namespace model { Tangible(ID identity, ctrl::BusTerm& nexus) - : uiBus_(nexus.attach(identity, *this)) + : uiBus_{nexus.attach(identity, *this)} { } public: diff --git a/wiki/renderengine.html b/wiki/renderengine.html index 42b992071..d538e5e36 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -1721,6 +1721,12 @@ This is an very important external Interface, because it links together all thre +
+
''special service connected to the UI-Bus to handle all messages touching core concerns''
+Especially this service handles the {{{act}}} messages to deal with commands [[operating on the Session|CommandHandling]]. And it deals with "uplink" {{{note}}} messages to record ongoing PresentationState changes. For all the other UI-Element nodes connected to the bus, CoreService is just assumed to be there, yet it is not known by ID, nor can it be addressed directly, like regular ~UI-Elements can. 
+
+On an operational level, CoreService additionally serves as lifecycle manager for the whole UI backbone, insofar it maintains the central bus hub, the Nexus. CoreService is instantiated (as a PImpl) when the UI subsystem starts up, and it is destroyed when leaving the UI main loop. At this point, the automatic registration and deregistration mechanism for bus terminals and ~UI-Elements must have cleared all connections in the central routing table (within Nexus).
+
The question is where to put all the state-like information [[associated with the current session|SessionOverview]]. Because this is certainly "global", but may depend on the session or need to be configured differently when loading another session. At the moment (9/07) Ichthyo considers the following solution:
 * represent all configuration as [[Asset]]s
@@ -9026,7 +9032,7 @@ For now, as of 6/10, we use specialised QueryResolver instances explicitly and d
 &rarr; QueryRegistration
 
-
+
Abstraction used in the Backbone of Lumiera's GTK User Interface
 The UI-Bus is a ''Mediator'' -- impersonating the role of the //Model// and the //Controler// in the [[MVC-Pattern|http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller]] in common UI architecture.
 
@@ -9036,6 +9042,16 @@ The ~MVC-Pattern as such is fine, and probably the best we know for construction
 This way, we separate between immediate local control of UI state and the more global, generic [[interaction control|InteractionControl]] and [[command binding|GuiCommandBinding]].
 And we arrive at a backbone like structure, which can be wired, programmed and reasoned about while abstracting from the local concerns and the state management related to the UI toolkit set in use. Any element //of more than local relevance// -- be it a controller or be it a widget, representing some entity from the model -- will be attached to the UI-Bus by virtue of a ''bus terminal'', establishing a bi-directional connection. This enables any UI element to invoke commands and persist presentation state (by sending //"state mark" mesages//), and in turn this allows the session and engine core to "cast" state updates towards the UI, without precisely knowing where and how to reach the relevant presentation entities.
 
+!!!topology
+The UI-Bus has a simple star shaped topology without hierarchical nesting and forwarding. It is not necessarily connected this way, since protocol and contracts do not assume any topology explicitly -- and the topology may be subject to change without further notice. We'll only assume there is a central hub somewhere in the bus, which maintains a routing table. Actually this hub represents the centre of the star and is known as ''Nexus''. Whenever an UI element connects to the bus, a routing entry is added, which allows the element to be directly invoked to receive messages. For that reason, every element connected this way must be at a fixed memory location.
+
+!!!degree of connectedness
+A connection point to the UI-Bus is called a ''Bus Terminal'' and implemented by class {{{BusTerm}}}. In fact, this class is the foundation of the ~UI-Bus network, with the Nexus being a specialisation. The //contract// involved with this connection requires the connected entity to respond to several messages, which effectively also means that this connected entity is a tangible UI-Element ({{{gui::model::Tangible}}} implements the necessary functions). But this pertains only to fully bidirectional bus connections, where the connected entity can receive messages addressed by it's ID. Beyond that, it is very much possible to operate a "free standing" bus terminal, which allows just to send "uplink" messages into the bus, without the need to add this terminal itself to the central routing table.
+
+!!!core services
+As far as the UI is concerned, »the application core« appears just as a node somehow connected to the bus. In fact there is a special backbone entity known as CoreService to fulfil that role. Even more, this service manages the lifecycle of the UI-Bus as a whole, insofar it incorporates the Nexus as a member, and is responsible for exposing the external interfaces of the GUI. And the CoreService is responsible for actually handling the {{{act}}} and {{{note}}} messages at one central location.
+
+
 !Bus interactions
 The UI-Bus has a star shaped topology, with a central "bus master" hub, which maintains a routing table. Attachment and detachment of elements can be managed automatically, since all of the UI-Bus operations perform within the UI event thread. We distinguish between up-link messages, directed towards some central service (presentation state management or command invocation) and down-link messages, directed towards individual elements. The interactions at the bus are closely interrelated with the elementary UI-Element operations.
 ;act
diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm
index 0acd20cc7..487898855 100644
--- a/wiki/thinkPad.ichthyo.mm
+++ b/wiki/thinkPad.ichthyo.mm
@@ -128,6 +128,40 @@
 
 
 
+
+
+  
+    
+  
+  
+    

+ CoreService hat keine volle Bus-Connection +

+ + +
+ + + + + +

+ ...das hab ich mir jetzt explizit so überlegt und es ist sinnvoll. +

+

+ Nur ein Tangible kann eine volle Bus-Connection haben, und das heißt, +

+

+ es kann downlink-Nachrichten bekommen. Dagegen hat CoreService lediglich ein "freistehendes" +

+

+ BusTerm, das damit Nachrichten an den Nexus schicken kann. +

+ + +
+ +
@@ -187,7 +221,8 @@ - + + @@ -209,14 +244,63 @@ - + + + + + + + + +

+ gemeint ist: keine volle bidirektionale Connection, +

+

+ denn CoreService ist kein Tangible. Das macht Sinn so, habe darüber nachgedacht. +

+

+ +

+

+ Anmerkung: ein "frestehendes" BusTerm ist valide und zugelassen, es hat halt nur eine uplink-Connection. +

+ + +
+
+ + + + + + +

+ nur ein Tangible kann downlink-Nachrichten sinnvoll empfangen; +

+

+ es muß dazu auch jede Menge Methoden implementieren. +

+ + +
+
+
- + + + + + + + + + +
@@ -257,10 +341,23 @@ - + - - + + + + + + + + + + + + + + + @@ -275,8 +372,8 @@ Speicherzugriffsfehler

- -
+ +
@@ -295,38 +392,12 @@ - - + - - - - - - - - -

- das scheint einen virtuellen dtor aufzurufen, der den dtor von NotificationService aktiviert -

-

- ? -

- - -
-
- - - - - - - - + + - @@ -347,19 +418,21 @@ Und noch schlimmer: im Debugger gibts keinen

- -
+
- + + + - + + @@ -370,8 +443,7 @@ die Closure eines Lambdas hängt am Kontext

- -
+
@@ -403,9 +475,7 @@ terminiert und dann tatsächlich den Fuktor aufrufen möchte

- - -
+
@@ -419,26 +489,31 @@ Debug: nur ein Element connected

- - + + + + + + + +

+ das scheint einen virtuellen dtor aufzurufen, der den dtor von NotificationService aktiviert +

+

+ ? +

+ +
-
-
-
- - - + + - - - - - +