From e8639b831952a9b00212fac92fd2f74fbee212dd Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sat, 6 Dec 2008 07:35:37 +0100 Subject: [PATCH] WIP work out further how to handle start/stop of layer separation interfaces --- src/common/singletonfactory.hpp | 4 +- src/common/singletonpolicies.hpp | 2 +- src/common/singletonpreconfigure.hpp | 5 +- src/gui/guinotificationfacade.cpp | 40 +++++---- src/lumiera/instancehandle.hpp | 128 +++++++++++++++++++++++++++ src/lumiera/interface.h | 2 +- src/lumiera/interfaceproxy.cpp | 2 + tests/lumiera/test-interfaces.c | 2 +- wiki/renderengine.html | 10 ++- 9 files changed, 166 insertions(+), 29 deletions(-) create mode 100644 src/lumiera/instancehandle.hpp diff --git a/src/common/singletonfactory.hpp b/src/common/singletonfactory.hpp index ee9548342..8d7b7a4b9 100644 --- a/src/common/singletonfactory.hpp +++ b/src/common/singletonfactory.hpp @@ -94,7 +94,7 @@ namespace lumiera /** @internal helper used to delegate destroying the single instance * to the Create policy, at the same time allowing the Life policy * to control the point in the Application lifecycle when the - * destruction of this instance occures. + * destruction of this instance occurs. */ static void destroy() { @@ -131,7 +131,7 @@ namespace lumiera ///// the object may already have been released when the runtime system calls the ///// destructors of static objects at shutdown. ///// It seems this would either cost us much of the flexibility or get complicated -///// to a point where we could as well implement our own Depenency Injection Manager. +///// to a point where we could as well implement our own Dependency Injection Manager. } // namespace lumiera #endif diff --git a/src/common/singletonpolicies.hpp b/src/common/singletonpolicies.hpp index b3da780d2..ce53e0109 100644 --- a/src/common/singletonpolicies.hpp +++ b/src/common/singletonpolicies.hpp @@ -95,7 +95,7 @@ namespace lumiera * the provided deleter function(s) at application shutdown, * relying on the runtime system calling destructors of static * objects. Because this Policy class can be shared between - * several Singletons, we need to memoize all registered + * several Singletons, we need to memorise all registered * deleter functions for calling them at shutdown. */ static void scheduleDelete (DelFunc kill_the_singleton) diff --git a/src/common/singletonpreconfigure.hpp b/src/common/singletonpreconfigure.hpp index e9b12e3d6..188c67b30 100644 --- a/src/common/singletonpreconfigure.hpp +++ b/src/common/singletonpreconfigure.hpp @@ -61,7 +61,7 @@ namespace lumiera /* ********************************************************************** */ - /* Forward declarations of all Classes we want to specialize the template */ + /* Forward declarations of all Classes we want to specialise the template */ /* ********************************************************************** */ namespace test @@ -87,8 +87,7 @@ namespace backend_interface /* Specialisation Definitions */ /* ************************** */ -namespace lumiera - { +namespace lumiera { using test::MockInjector; diff --git a/src/gui/guinotificationfacade.cpp b/src/gui/guinotificationfacade.cpp index c82d5d3d4..beed5b0a7 100644 --- a/src/gui/guinotificationfacade.cpp +++ b/src/gui/guinotificationfacade.cpp @@ -41,7 +41,7 @@ namespace gui { namespace { // facade implementation details - class GuiNotificationFacade + struct GuiNotificationFacade : public GuiNotification { void @@ -57,37 +57,41 @@ namespace gui { } }; + lumiera::Singleton _facade; - /*=================== define an lumieraorg_guinotification instance ====================== */ + + + + /* ================== define an lumieraorg_guinotification instance ======================= */ LUMIERA_INTERFACE_INSTANCE (lumieraorg_interfacedescriptor, 0 ,lumieraorg_GuiNotificationFacade_descriptor , NULL, NULL, NULL - , LUMIERA_INTERFACE_INLINE (name, LUIDGEN, + , LUMIERA_INTERFACE_INLINE (name, "\044\117\156\365\344\056\362\220\166\350\320\214\115\221\302\177", const char*, (LumieraInterface iface), { return "GuiNotification"; } ) - , LUMIERA_INTERFACE_INLINE (brief, LUIDGEN, + , LUMIERA_INTERFACE_INLINE (brief, "\160\240\102\325\175\145\270\140\350\241\163\303\331\343\253\142", const char*, (LumieraInterface iface), { return "GUI Interface: push state update and notification of events into the GUI"; } ) - , LUMIERA_INTERFACE_INLINE (homepage, LUIDGEN, + , LUMIERA_INTERFACE_INLINE (homepage, "\217\232\066\101\042\116\054\217\070\233\253\241\166\145\234\133", const char*, (LumieraInterface iface), { return "http://www.lumiera.org/develompent.html" ;} ) - , LUMIERA_INTERFACE_INLINE (version, LUIDGEN, + , LUMIERA_INTERFACE_INLINE (version, "\350\365\121\052\037\022\300\021\171\357\017\367\270\071\266\376", const char*, (LumieraInterface iface), { return "0.1~pre"; } ) - , LUMIERA_INTERFACE_INLINE (author, LUIDGEN, + , LUMIERA_INTERFACE_INLINE (author, "\117\007\006\234\153\206\000\344\303\043\027\261\045\320\166\133", const char*, (LumieraInterface iface), { return "Hermann Vosseler"; } ) - , LUMIERA_INTERFACE_INLINE (email, LUIDGEN, + , LUMIERA_INTERFACE_INLINE (email, "\356\243\022\137\345\275\016\034\337\144\031\260\303\050\140\327", const char*, (LumieraInterface iface), { return "Ichthyostega@web.de"; } ) - , LUMIERA_INTERFACE_INLINE (copyright, LUIDGEN, + , LUMIERA_INTERFACE_INLINE (copyright, "\365\220\260\051\267\345\330\046\145\134\331\320\115\157\332\356", const char*, (LumieraInterface iface), { return @@ -95,7 +99,7 @@ namespace gui { " 2008 Hermann Vosseler "; } ) - , LUMIERA_INTERFACE_INLINE (license, LUIDGEN, + , LUMIERA_INTERFACE_INLINE (license, "\155\335\361\364\221\012\115\325\306\046\153\152\002\117\075\077", const char*, (LumieraInterface iface), { return @@ -114,11 +118,11 @@ namespace gui { "Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA"; } ) - , LUMIERA_INTERFACE_INLINE (state, LUIDGEN, + , LUMIERA_INTERFACE_INLINE (state, "\227\170\230\144\310\330\131\175\367\152\327\324\113\275\223\245", int, (LumieraInterface iface), {return LUMIERA_INTERFACE_EXPERIMENTAL; } ) - , LUMIERA_INTERFACE_INLINE (versioncmp, LUIDGEN, + , LUMIERA_INTERFACE_INLINE (versioncmp, "\253\066\335\233\025\161\135\347\377\156\121\135\347\313\130\014", int, (const char* a, const char* b), {return 0;} ////////////////////////////////////////////TODO ) @@ -128,15 +132,15 @@ namespace gui { LUMIERA_INTERFACE_INSTANCE (lumieraorg_GuiNotification, 1 ,lumieraorg_GuiNotificationFacade , LUMIERA_INTERFACE_REF(lumieraorg_interfacedescriptor, 0, lumieraorg_GuiNotificationFacade_descriptor) - , open_facade - , close_facade - , LUMIERA_INTERFACE_INLINE (displayInfo, LUIDGEN, + , NULL /* on open */ + , NULL /* on close */ + , LUMIERA_INTERFACE_INLINE (displayInfo, "\366\075\213\163\207\040\221\233\010\366\174\374\317\126\331\205", void, (const char* text), - { return _facade->displayInfo(text); } + { return _facade().displayInfo(text); } ) - , LUMIERA_INTERFACE_INLINE (triggerGuiShutdown, LUIDGEN, + , LUMIERA_INTERFACE_INLINE (triggerGuiShutdown, "\267\043\244\065\107\314\370\175\063\330\264\257\302\146\326\303", void, (const char* cause), - { return _facade->triggerGuiShutdown(cause); } + { return _facade().triggerGuiShutdown(cause); } ) ); diff --git a/src/lumiera/instancehandle.hpp b/src/lumiera/instancehandle.hpp new file mode 100644 index 000000000..9cbf5921c --- /dev/null +++ b/src/lumiera/instancehandle.hpp @@ -0,0 +1,128 @@ +/* + INSTANCEHANDLE.hpp - automatically handling interface lifecycle + + Copyright (C) Lumiera.org + 2008, Hermann Vosseler + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +/** @file instancehandle.hpp + ** A handle template for automatically dealing with interface and plugin + ** registration and deregistration. By placing an instance of this template, + ** parametrised with the appropriate interface type, the respective interface + ** and instance is loaded and opened through the Lumiera Interface system. + ** It will be closed and unregistered automatically when the handle goes + ** out of scope. Additionally, access via an (existing) interface proxy + ** may be enabled and disabled alongside with the loading and unloading. + ** + ** @see gui::GuiFacade usage example + ** @see interface.h + ** @see interfaceproxy.cpp + */ + + +#ifndef LUMIERA_INSTANCEHANDLE_H +#define LUMIERA_INSTANCEHANDLE_H + + +extern "C" { +#include "lumiera/interface.h" +} + +#include +//#include +#include + + + +namespace lumiera { + + using std::string; +// using boost::scoped_ptr; + + namespace { // implementation details + + LumieraInterface + register_and_open (LumieraInterface* descriptors) + { + if (!descriptors) return NULL; + lumiera_interfaceregistry_bulkregister_interfaces (descriptors, NULL); + LumieraInterface masterI = descriptors[0]; + return lumiera_interface_open (masterI->interface, + masterI->version, + masterI->size, + masterI->name); + } + + } // (End) impl details + + + + /** + * Handle tracking the registration of an interface, deregistering it on deletion. + * Depending on which flavour of the ctor is used, either (bulk) registration of interfaces + * or plugin loading is triggered. The interface type is defined by type parameter. + * @todo when provided with the type of an facade interface class, care for enabling/disabling + * access through the facade proxy singleton when opening/closing the registration. + */ + template< class I ///< fully mangled name of the interface type + > + class InstanceHandle + : private boost::noncopyable + { + LumieraInterface* desc_; + I* instance_; + + public: + /** Set up an InstanceHandle representing a plugin. + * Should be placed at the client side. + * @param iName unmangled name of the interface + * @param version major version + * @param minminor minimum acceptable minor version number + * @param impName unmangled name of the instance (implementation) + */ + InstanceHandle (string const& iName, uint version, size_t minminor, string const& impName) + : descriptors_(0), + instance_(static_cast + (lumiera_interface_open (iName, version, minminor, impName))) + { } + + /** Set up an InstanceHandle managing the + * registration and deregistration of interface(s). + * Should be placed at the service providing side. + * @param descriptors zero terminated array of interface descriptors, + * usually available through lumiera_plugin_interfaces() + */ + InstanceHandle (LumieraInterface* descriptors) + : desc_(descriptors), + instance_(static_cast (register_and_open (desc_))) + { } + + ~InstanceHandle () + { + lumiera_interface_close ((LumieraInterface)instance_); + if (desc_) + lumiera_interfaceregistry_bulkremove_interfaces (desc_); + } + + + }; + + +} // namespace lumiera + +#endif diff --git a/src/lumiera/interface.h b/src/lumiera/interface.h index fb4aa93e7..83d1a4e6b 100644 --- a/src/lumiera/interface.h +++ b/src/lumiera/interface.h @@ -316,7 +316,7 @@ LUMIERA_INTERFACE_INSTANCE (lumieraorg__plugin, 0, lumiera_interface_handle->close (&(handle)->interface_header_) -#else /* compile as buildin */ +#else /* compile as builtin */ #define LUMIERA_PLUGIN_INTERFACEHANDLE static LUMIERA_INTERFACE_HANDLE(lumieraorg_interface, 0) lumiera_interface_handle #define LUMIERA_PLUGIN_STORE_INTERFACEHANDLE(name) lumiera_interface_handle = LUMIERA_INTERFACE_CAST (lumieraorg_interface, 0) name diff --git a/src/lumiera/interfaceproxy.cpp b/src/lumiera/interfaceproxy.cpp index 03cb3cf5b..c6e96df8d 100644 --- a/src/lumiera/interfaceproxy.cpp +++ b/src/lumiera/interfaceproxy.cpp @@ -78,6 +78,8 @@ namespace gui { /** storage for the facade proxy factory used by client code to invoke through the interface */ lumiera::SingletonSub GuiNotification::facade (typeinfo_proxyInstance_to_create); + ///////////////////////////////////////TODO: this solution is not correct, because it doesn't detect when the interface is shut down! + } // namespace gui diff --git a/tests/lumiera/test-interfaces.c b/tests/lumiera/test-interfaces.c index ee42bb9ec..d85cf99dd 100644 --- a/tests/lumiera/test-interfaces.c +++ b/tests/lumiera/test-interfaces.c @@ -499,7 +499,7 @@ TEST ("plugin_examplepluginc") lumiera_interfaceregistry_init (); lumiera_plugin_discover (lumiera_plugin_load, lumiera_plugin_register); - TODO ("macro to derrive minminor version from a slot"); + TODO ("macro to derive minminor version from a slot"); LUMIERA_INTERFACE_HANDLE(lumieraorg_testhello, 0) german = LUMIERA_INTERFACE_OPEN (lumieraorg_testhello, 0, 0, lumieraorg_hello_german); diff --git a/wiki/renderengine.html b/wiki/renderengine.html index 70adf0cfb..806303db6 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -1378,12 +1378,12 @@ Thus, the Proc-Layer exposes (one or several) facade interfaces for the GUI to u
Considering how to interface to and integrate with the GUI Layer. Running the GUI is //optional,// but it requires to be [[started up|GuiStart]], installing the necessary LayerSeparationInterfaces.
-
+
Starting up the GUI is optional and is considered part of the Application start/stop and lifecycle.
 * main and AppState activate the lifecyle methods on the ~GuiSubsysDescriptor
 * loading a GuiStarterPlugin creates an instance of the notification interface within the InterfaceSystem
 * its implementation (within GuiStarterPlugin) is linked against the GuiNotificationFacade, i.e. the actual implementation. This link is hard wired.
-* the process of loading this GuiStarterPlugin creates an interface ref on the calling side, which is made available through the usual singleton factory embedded within the GuiNotificationFacade interface.
+* the process of loading this GuiStarterPlugin creates an interface ref on the calling side, which is made available through the usual singleton factory embedded within the GuiNotificationFacade interface. {{red{TODO current solution doesn't handle shutdown correctly}}}
 * activating this singleton factory yields the (single) instance of the forwarding proxy, which calls through this interface ref.
 
@@ -1801,7 +1801,7 @@ From experiences with other middle scale projects, I prefer having the test code [img[Example: Interfaces/Namespaces of the ~Session-Subsystems|uml/fig130053.png]]
-
+
Lumiera uses a 3-layered architecture. Separation between layers is crucial. Any communication between the layers regarding the normal operation of the application should //at least be initiated// through ''Layer abstraction interfaces''. This is a low-impact version of layering, because, aside from this triggering, direct cooperation of parts within the single Lumiera process is allowed, under the condition that is is implemented using additional abstractions (interfaces with implementation level granularity). We stick to the policy of //disallowing direct coupling of implementations located in different layers.//
 
 To implement such a structure, each layer separation interface actually is comprised of several parts:
@@ -1809,6 +1809,10 @@ To implement such a structure, each layer separation interface actually is compr
 * a facade interface defining the respective abstractions in terms of the implementation language (C or C++)
 * a implementation object on the service proiding side which is directly addressed by the implementation instantiated within the InterfaceSystem
 * a proxy object on the client side, which usually is given inline alongside with the CLI interface definition.
+
+!opening and closing
+Handling the lifecycle can be tricky, because client- and service-side need to carry out the opening and closing operations in sync and observing a specific call order to ensure calls get blocked already on the client side unless the whole interface compound is really up and running. To add to this complexity, plugins and built-in interfaces are handled differently regarding the question who is in charge of the lifecycle: interfaces are installed on the service side, whereas the loading of plugins is triggered from client side by requesting the plugin from the loader.
+Anyway, interfaces are resources which best should be managed automatically. At least within the C++ part of the application we can ensure this by using a handle class. This way the handling of plugins and interfaces can be unified; the only remaining difference is on what side (client or service provider side) the handle is instantiated, with the appropriate ctor parameters.