From 79d5e49a749c1b6f7d9ef993241dd83b8a179f51 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Mon, 9 Nov 2009 05:21:59 +0100 Subject: [PATCH] defined basic session lifecycle and service access. Closes Ticket #400 --- src/proc/mobject/session.hpp | 1 + src/proc/mobject/session/session-impl.hpp | 1 + src/proc/mobject/session/session-services.cpp | 34 ++++++ src/proc/mobject/session/session-services.hpp | 91 +++++++++++--- src/proc/mobject/session/session.cpp | 13 +- tests/40components.tests | 6 +- tests/43session.tests | 13 +- .../session/session-service-access-test.cpp | 112 +++++++++++------- wiki/renderengine.html | 30 ++++- 9 files changed, 234 insertions(+), 67 deletions(-) create mode 100644 src/proc/mobject/session/session-services.cpp diff --git a/src/proc/mobject/session.hpp b/src/proc/mobject/session.hpp index 5e46e97a0..e15eb78a1 100644 --- a/src/proc/mobject/session.hpp +++ b/src/proc/mobject/session.hpp @@ -114,6 +114,7 @@ namespace mobject { extern const char* ON_SESSION_START; ///< triggered before loading any content into a newly created session extern const char* ON_SESSION_INIT; ///< triggered when initialising a new session, after adding content + extern const char* ON_SESSION_READY; ///< triggered after session is completely functional and all APIs are open. extern const char* ON_SESSION_END; ///< triggered before discarding an existing session diff --git a/src/proc/mobject/session/session-impl.hpp b/src/proc/mobject/session/session-impl.hpp index 7b36f6887..5740a4424 100644 --- a/src/proc/mobject/session/session-impl.hpp +++ b/src/proc/mobject/session/session-impl.hpp @@ -89,6 +89,7 @@ namespace session { /** * Session manager implementation class holding the * actual smart pointer to the current Session impl. + * @todo couldn't this be pushed down into session.cpp? */ class SessManagerImpl : public SessManager { diff --git a/src/proc/mobject/session/session-services.cpp b/src/proc/mobject/session/session-services.cpp new file mode 100644 index 000000000..624666eb7 --- /dev/null +++ b/src/proc/mobject/session/session-services.cpp @@ -0,0 +1,34 @@ +/* + SessionServices - accessing Proc-Layer internal session implementation services + + 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. + +* *****************************************************/ + + +#include "proc/mobject/session/session-services.hpp" +#include "proc/mobject/session/session-impl.hpp" + +namespace mobject { +namespace session { + + /** TODO */ + + + +}} // namespace mobject::session diff --git a/src/proc/mobject/session/session-services.hpp b/src/proc/mobject/session/session-services.hpp index 1c944094e..a71e5664a 100644 --- a/src/proc/mobject/session/session-services.hpp +++ b/src/proc/mobject/session/session-services.hpp @@ -23,8 +23,52 @@ /** @file session-services.hpp ** A mechanism for exposing and accessing implementation level - ** services of the session. - ** TODO TODO + ** services of the session. While any client code should always + ** use the public Session API, some implementation level facilities + ** within Proc-Layer need to cooperate with a wider SessionImpl API. + ** On the other hand, we don't want to create coupling between the + ** mentioned Proc internals and the session implementation. Another + ** concern addressed by this mechanism is to assure consistency + ** across all those implementation APIs. New APIs can be added + ** just by extending a template definition and will automatically + ** participate in the session management mechanisms, because any + ** access is routed through the top level Session PImpl. + ** + ** \par structure of session implementation-level services + ** + ** Assumed any part of the Proc implementation needs to cooperate + ** with the session implementation; the necessary link has to be + ** abstracted into an implementation level API. Typically, this + ** API provides an static access function, which is to be implemented + ** "somewhere else", so the Proc implementation isn't required to + ** include anything of the session implementation level + ** + ** In order to actually provide such a service, an specialisation of + ** the ServiceAccessPoint template has to be defined, which may mix in + ** the service API and implement it directly on top of SessionImpl. + ** Note, mixing in the API isn't required -- alternatively the API might + ** be completely bridged through the mentioned static access functions + ** (i.e. such would be kind of an generic API, relying on convention + ** rather than on a vtable) + ** + ** When the SessManagerImpl creates the concrete session object, + ** it doesn't use the bare SessionImpl class; rather, an inheritance + ** chain is formed, starting with SessionImpl and stacking any of the + ** configured ServiceAccessPoint instantiations on top of it. Thus, + ** the public session access gets the concrete implementation of the + ** Session API (through the vtable), while any service level access + ** can use the corresponding service API directly. Service APIs have + ** to care to avoid name clashes though. + ** + ** The implementation of all the service API access functions is + ** bundled within session-service.cpp -- where the full compound + ** of SessionImpl and the ServiceAccessPoint specialisations has + ** to be visible. + ** + ** @see session-service-access-test.cpp simplified inline demo definition of this setup + ** @see session.hpp public session API + ** @see session-impl.hpp definition of ServiceAccessPoint specialisations + ** @see session-impl.cpp session implementation internals ** */ @@ -35,24 +79,25 @@ #include "proc/mobject/session.hpp" #include "lib/meta/generator.hpp" -//#include -//#include - namespace mobject { namespace session { -//using std::vector; -//using boost::scoped_ptr; -//using std::tr1::shared_ptr; using lumiera::typelist::InstantiateChained; using lumiera::typelist::InheritFrom; using lumiera::typelist::NullType; - + /** + * Access point to a single implementation-level API. + * For each concrete service to provide, an specialisation + * of this template is assumed to exist which inherits from + * IMPL; it will be used in an inheritance-chain on top of + * SessionImpl and thus can access the latter (= session + * implementation API) just through its parent class IMPL + */ template struct ServiceAccessPoint; @@ -62,16 +107,32 @@ namespace session { * to provide by the Session. An instance of this template * is created on top of SessionImpl, configured such as * to inherit from all the concrete services to be - * exposed for use by Proc-Lyer's internals. + * exposed for use by Proc-Lyer's internals. + * + * @param APIS sequence of API types to implement + * @param FRONT type of the frontend used for access + * @param SESS the basic session implementation */ - template< typename IMPS + template< typename APIS , class FRONT - , class BA =NullType + , class SESS > - class SessionServicesX - : public InstantiateChained + class TSessionServices + : public InstantiateChained< typename APIS::List // for each of the API types... + , ServiceAccessPoint // instantiate a service implementation + , SESS // and stack all these on top of SessionImpl + > { - static FRONT& entrance_; + public: + static FRONT& current; ///< intended to be hard-wired to SessManagerImpl singleton + + /** access an service by explicit downcast. + * @warning this function is dangerous; never store the + * returned reference, as the referred object + * might go away due to session close/reset/load + */ + template + API& get() { return *this; } }; diff --git a/src/proc/mobject/session/session.cpp b/src/proc/mobject/session/session.cpp index 317c8ef3b..d0b6a6b2e 100644 --- a/src/proc/mobject/session/session.cpp +++ b/src/proc/mobject/session/session.cpp @@ -53,7 +53,7 @@ namespace mobject { * * Consequently, if you want to talk to the session manager, * you use dot-notation, while you access the session object - * via arrow notaion (e.g. \code Session::current->getFixture() ) + * via arrow notation (e.g. \code Session::current->getFixture() ) */ SessManager& Session::current = Singleton()(); @@ -79,6 +79,17 @@ namespace mobject { */ const char* ON_SESSION_INIT ("ON_SESSION_INIT"); + /** \par + * LifecycleHook, to perform post loading tasks, requiring an already completely usable + * and configured session to be in place. When activated, the session is completely restored + * according to the standard or persisted definition and any access interfaces are already + * opened and enabled. Scripts and the GUI might even be accessing the session in parallel. + * Subsystems intending to perform additional processing should register here, if requiring + * fully functional client side APIs. Examples would be statistics gathering, validation + * or auto-correction of the session's contents. + */ + const char* ON_SESSION_READY ("ON_SESSION_READY"); + /** \par * LifecycleHook, to perform any state saving, deregistration or de-activation necessary * before bringing down an existing session. When invoked, the session is still fully valid diff --git a/tests/40components.tests b/tests/40components.tests index ccaa911c8..adc44631e 100644 --- a/tests/40components.tests +++ b/tests/40components.tests @@ -561,9 +561,9 @@ out: ctor DoIt > out: ctor DoIt > out: ctor DoIt > out: ctor DoIt > -out: Block< 2>::eat\(..\) -out: Block< 5>::eat\(..\) -out: Block<13>::eat\(..\) +out: devouring__Block< 2>__ +out: devouring__Block< 5>__ +out: devouring__Block<13>__ out: gulp! out: dtor DoIt > out: dtor DoIt > diff --git a/tests/43session.tests b/tests/43session.tests index 5c9b256b0..fd3f0b3db 100644 --- a/tests/43session.tests +++ b/tests/43session.tests @@ -99,7 +99,18 @@ PLANNED "SessionManager_test" SessionManager_test < #include #include #include -//using boost::format; - using lib::Singleton; - using boost::lexical_cast; - using std::ostream; - using std::string; - using std::cout; - using std::endl; - namespace mobject { namespace session { namespace test { + using lib::Singleton; + using boost::lexical_cast; + using std::ostream; + using std::string; + using std::cout; + using std::endl; + namespace { // what follows is a simulated (simplified) version // of the complete Session + SessionManager setup..... -// using boost::noncopyable; -// using session::SessionServices; using lumiera::typelist::Types; + using lumiera::typelist::InstantiateChained; - /* === Interface level === */ + /* === Interface level === */ //----------------corresponding-to-session.hpp struct TSessManager; typedef TSessManager& PSess; @@ -78,7 +73,9 @@ namespace test { }; - /* === Service level API === */ + + + /* === Service level API === */ //----------------internal-API-definition-headers struct InternalAPI_1 { @@ -95,7 +92,7 @@ namespace test { - /* === Implementation level === */ + /* === Implementation level === */ //----------------corresponding-to-session-impl.hpp struct TSessionImpl : TSession { @@ -116,10 +113,10 @@ namespace test { template - struct ServiceAccessPoint; + struct TServiceAccessPoint; template - struct ServiceAccessPoint + struct TServiceAccessPoint : IMPL , InternalAPI_1 { @@ -131,7 +128,7 @@ namespace test { }; template - struct ServiceAccessPoint + struct TServiceAccessPoint : IMPL { void @@ -145,8 +142,8 @@ namespace test { , class FRONT , class SESS > - class SessionServices - : public InstantiateChained + class TSessionServices + : public InstantiateChained { public: @@ -159,16 +156,19 @@ namespace test { return *this; } }; - - - /* === storage and basic configuration === */ + + + + + + /* === storage and basic session manager configuration === */ struct TSessManagerImpl; - typedef SessionServices< Types - , TSessManagerImpl - , TSessionImpl - > SessionImplAPI; + typedef TSessionServices< Types + , TSessManagerImpl + , TSessionImpl + > SessionImplAPI; @@ -203,13 +203,18 @@ namespace test { TSessManager& TSession::current = Singleton()(); //note: already during static initialisation - + template<> TSessManagerImpl& SessionImplAPI::current = static_cast (TSession::current); - - - /* === Implementation of service access === */ + + + + + + + + /* === Implementation of service access === */ //----------------corresponding-to-session-services.cpp InternalAPI_1& InternalAPI_1::access() @@ -224,9 +229,10 @@ namespace test { } - - /* === Implementation of Session internals === */ + + + /* === Implementation of Session internals === */ //----------------corresponding-to-session-impl.cpp inline ostream& operator<< (ostream& os, TSessionImpl const& simpl) @@ -267,14 +273,18 @@ namespace test { + + + + + /******************************************************************************* - * Verify the access mechanism used by Proc-Layer internals for - * accessing implementation level APIs of the session. + * Verify the access mechanism both to the pubic session API and + * to implementation level APIs used by Proc-Layer internals. + * + * Actually, this test uses a simulated setup of the real session, + * complete with interfaces, implementation and session manager frontend. * - * Actually, this test uses setup of the real session, - * complete with interfaces, implementation and a - * session manager frontend. - * * @see session-impl.hpp the real thing * @see SessionServices; */ @@ -289,6 +299,10 @@ namespace test { } + /** @test accessing an non-existing session + * causes creation of a new TSessionImpl instance. + * After that, the public API function gets invoked. + */ void access_defaultSession () { @@ -297,6 +311,10 @@ namespace test { } + /** @test invoking the management API to close the session. + * The next public API invocation will create + * a new TSessionImpl instance. + */ void make_newSession () { @@ -305,17 +323,23 @@ namespace test { } + /** example of an one-liner, as it might be used + * internally by implementation code within Proc-Layer */ + uint magic() { return InternalAPI_1::access().getMagic(); } + + /** @test accessing implementation-level APIs */ void invoke_implServices () { - cout << "current Session-Impl-ID = " << InternalAPI_1::access().getMagic() << endl; + cout << "current Session-Impl-ID = " << magic() << endl; InternalAPI_2::invokeImplementationService(); + cout << "now resetting this session." << endl; TSession::current.reset(); + InternalAPI_2::invokeImplementationService(); // invocation creates new session as side effect + cout << "current Session-Impl-ID = " << magic() << endl; } - - }; diff --git a/wiki/renderengine.html b/wiki/renderengine.html index 843f37345..9f66340c8 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -3950,9 +3950,9 @@ Currently as of 5/09, this is an ongoing [[implementation and planning effort|Pl {{red{WIP ... just emerging}}} -
+
The current [[Session]] is the root of any state found within Proc-Layer. Thus, events defining the session's lifecycle influence and synchronise the cooperative behaviour of the entities within the model, the ProcDispatcher, [[Fixture]] and any facility below.
-* when ''starting'', by default an empty session is created, which puts any related facility into a defined initial state.
+* when ''starting'', on first access an empty session is created, which puts any related facility into a defined initial state.
 * when ''closing'' the session, any dependent facilities are disabled, disconnected, halted or closed
 * ''loading'' an existing session &mdash; after closing the previous session &mdash; sets up an empty (default) session an populates it with de-serialised content.
 * when encountering a ''mutation point'', [[command processing|ProcDispatcher]] is temporarily halted to trigger off an BuildProcess.
@@ -3963,10 +3963,34 @@ The SessionManager is responsible for conducting the session lifecycle. Accessib
 !Synchronising access to session's implementation facilities
 Some other parts and subsystems within the ~Proc-Layer need specialised access to implementation facilities within the session. Informations about some conditions and configurations might be retrieved through [[querrying the session|Query]], and especially default configurations for many objects are [[bound to the session|DefaultsImplementation]]. The [[discovery of session contents|SessionStructureQuery]] relies on an [[index facility|PlacementIndex]] embedded within the session implementation. Moreover, some "properties" of the [[media objects|MObject]] are actually due to the respective object being [[placed|Placement]] in some way into the session; consequently, there might be an dependency on the actual [[location as visible to the placement|PlacementScope]], which in turn is constituted by [[querying the index|QueryFocus]].
 
-Each of these facilities relies on a separate access point to session services, corresponding to distinct service interfaces. But &mdash; on the implementation side &mdash; all these services are provided by a (compound) implementation object. This approach allows to switch the actual implementation of all these services instantaneous by swapping the ~PImpl maintained by the session manager.
+Each of these facilities relies on a separate access point to session services, corresponding to distinct service interfaces. But &mdash; on the implementation side &mdash; all these services are provided by a (compound) SessionServices implementation object. This approach allows to switch the actual implementation of all these services simply by swapping the ~PImpl maintained by the session manager. A new implementation level service can thus be added to the ~SessionImpl just by hooking it into the ~SessionServices compound object. But note, this mechanism as such is ''not thread safe'', unless the //implementation// of the invoked functions is synchronised in some way to prevent switching to a new session implementation while another thread is still executing session implementation code.
+
+Currently, the understanding is for some global mechanism to hold any command execution, script running and further object access by the GUI //prior//&nbsp; to invoking any of the session management operations (loading, closing, resetting). An alternative would be to change the top-level access to the session ~PImpl to go through an accessor value object, to acquire some lock automatically before any access can happen. C++ ensures the lifespan of any temporaries to surpass evaluation of the enclosing expression, which would be sufficient to prevent another thread to pull away the session during that timespan. Of course, any value returned from such an session API call isn't covered by this protection. Usually, objects are handed out as MObjectRef, which in turn means to resolve them (automatically) on dereferentiation by another session API access. But while it seems we could get locking to work automatically this way, still such a technique seems risky and involved; a plain flat lock at top level seems to be more appropriate.
 
 !Interface and lifecycle hooks
 {{red{draft as of 11/09}}}
+As detailed above, {{{Session::current}}} exposes the management / lifecycle API, and at the same time can be dereferenced into the primary [[session API|SessionInterface]]. An default configured ~SessionImpl instance will be built automatically, in case no session implementation instance exists on executing this dereferentiation.
+
+!!!building (or loading) a session
+# as a preparation step, a new implementation instance is created, alongside with any supporting facilities (especially the PlacementIndex)
+# the basic default configuration is loaded into this new session instance
+# when the new session is (technically) complete and usable, the switch on the ~PImpl happens
+# the {{{ON_SESSION_START}}} LifecycleEvent is emitted
+# content is loaded into the session, including hard wired content and any de-serialised data from persistent storage
+# the {{{ON_SESSION_INIT}}} event is emitted
+# additional initialisation, wiring and registration takes place; basically anything to make the session fully functional
+# the session LayerSeparationInterface is opened and any further top-level blocking is released
+# the {{{ON_SESSION_READY}}} event is emitted
+
+!!!closing the session
+# top-level facilities accessing the session (GUI, command processing, scripts) are blocked and the LayerSeparationInterface is closed
+# any render processes are ensured to be terminated
+# the {{{ON_SESSION_END}}} event is emitted
+# the command processing log is tagged
+# the command queue(s) are emptied, discarding any commands not yet executed
+# the PlacementIndex is cleared, effectively releasing any object "instances"
+# the [[asset registry|AssetManager]] is cleared, thereby releasing any remaining external resource references
+# destruction of session implementation instances