planning session lifecycle implementation

This commit is contained in:
Fischlurch 2010-10-23 05:58:14 +02:00
parent ad452a9dd4
commit 796c4488a5
6 changed files with 176 additions and 15 deletions

View file

@ -22,11 +22,15 @@
/** @file appstate.hpp
** Registering and managing some application-global services.
** This can be considered the "main" object of the Lumiera application
** Besides encapsulating the logic for starting up the fundamental parts
** of the application, there is a mechanism for registering and firing off
** application lifecycle event callbacks.
** of the application, there is a mechanism for registering \em subsystems
** to be brought up and shut down in order. AppState will issue the global
** application lifecycle events (where other parts may have registered
** callbacks) and provides the top-level catch-all error handling.
**
** @see LifecycleHook
** @see Subsys
** @see main.cpp
** @see logging.h
*/

View file

@ -166,6 +166,14 @@ namespace mobject {
*/
virtual void clear () =0;
/** shut down the current session cleanly.
* Includes discarding of all assets and unloading any
* config rules and additional state. Doesn't save anything.
* Typically invoked on shutdown of the session subsystem.
* @note next access will pull up a empty default session
*/
virtual void close () =0;
/** reset all session config and
* start with a pristine default session.
*/

View file

@ -0,0 +1,101 @@
/*
LIFECYCLE-ADVISOR.hpp - outline of the session lifecycle
Copyright (C) Lumiera.org
2010, Hermann Vosseler <Ichthyostega@web.de>
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 lifecycle-advisor.hpp
** Implementation facility providing an operation skeleton of session lifecycle.
** This header is intended to be included into the session manager implementation;
** it should not be used by client code otherwise. The purpose of the LifecycleAdvisor
** is to to get a consolidated view on the whole lifecycle. Reading this source file
** should convey a complete picture about what is going on with respect to the
** session lifecycle. Besides that, no actual implementation code is to be found
** here; any implementation is delegated to the relevant session facilities.
**
** The idea of a LifecycleAdvisor is inspired by GUI frameworks, especially
** Spring RichClient. Typically, such frameworks provides a means for flexible
** configuration of the application lifecycle. This isn't a goal here, as there
** is only one Lumiera application and the session lifecycle can be considered
** hard wired, with the exception of some extension points, which are implemented
** as "lifecycle events".
**
** @see SessManager
** @see LifecycleHook
** @see lumiera::AppState
** @see session.hpp
*/
#ifndef MOBJECT_SESSION_LIFECYCLE_ADVISOR_H
#define MOBJECT_SESSION_LIFECYCLE_ADVISOR_H
#include "lib/error.hpp"
#include "include/lifecycle.h"
#include "proc/mobject/session.hpp"
#include <boost/noncopyable.hpp>
namespace mobject {
namespace session {
using lumiera::LifecycleHook;
/**
* Skeleton operations conducting the session lifecycle sequences.
* Any details of the operations are delegated to the current session
* and associated services.
* @warning this object is assumed to be used as a single instance
* in a controlled and safe (single threaded) environment
*/
class LifecycleAdvisor
: boost::noncopyable
{
public:
/** operation sequence to pull up the session
*
*/
void
pullUp()
{
LifecycleHook::trigger (ON_SESSION_START);
LifecycleHook::trigger (ON_SESSION_INIT);
LifecycleHook::trigger (ON_SESSION_READY);
}
/** operation sequence for cleanly shutting down the session
*
*/
void
shutDown()
{
LifecycleHook::trigger (ON_SESSION_END);
}
};
}} // namespace mobject::session
#endif

View file

@ -39,7 +39,7 @@
#include "proc/mobject/session.hpp"
#include "proc/mobject/session/sess-manager-impl.hpp"
#include "proc/mobject/session/defsmanager.hpp"
//#include "proc/mobject/session/defsregistry.hpp"
#include "proc/mobject/session/lifecycle-advisor.hpp"
#include "lib/error.hpp"
using boost::scoped_ptr;
@ -82,17 +82,26 @@ namespace session {
/** Initially (at static init time), only the single system-wide
* Session manger instance is created. It can be used to load an
* existing session; otherwise an empty default Session an a
* Defaults manager (Config Query system) is created at first
* existing session; otherwise an empty default Session, together
* with the core facilities (PlacementIndex, AssetManager, Query
* subsystem and the Defaults manager) is created on first
* \link #operator-> access \endlink to the session object.
*/
SessManagerImpl::SessManagerImpl () throw()
: pImpl_ (0)
, lifecycle_(new LifecycleAdvisor)
{
Session::initFlag = true; //////////////////////////////////////// TICKET #518 instead of this hack, implement basic-init of the session manager for real
}
SessManagerImpl::~SessManagerImpl ()
{
TODO ("verify sane lifecycle");
Session::initFlag = false;
}
bool
SessManagerImpl::isUp ()
@ -100,8 +109,8 @@ namespace session {
return bool(pImpl_);
}
/** @note no transactional behaviour.
* may succeed partial.
/** @note no transactional behaviour. may succeed partially.
* @todo clarify relation to command processing/undo /////////// TICKET #697
*/
void
SessManagerImpl::clear ()
@ -110,6 +119,17 @@ namespace session {
}
/** Shut down the current session together with all associated services.
* @todo avoid blocking when aborting render processes ///////////// TICKET #201
* @todo well defined transactional behaviour ///////////////////// TICKET #698
*/
void
SessManagerImpl::close ()
{
UNIMPLEMENTED("clean session shutdown");
}
/** @note this operation is atomic and either succeeds or
* fails completely, in which case the current session
* remains unaltered.

View file

@ -30,6 +30,7 @@
namespace mobject {
namespace session {
class LifecycleAdvisor;
/**
@ -38,15 +39,17 @@ namespace session {
*/
class SessManagerImpl : public SessManager
{
scoped_ptr<SessionImplAPI> pImpl_;
scoped_ptr<SessionImplAPI> pImpl_;
scoped_ptr<LifecycleAdvisor> lifecycle_;
SessManagerImpl() throw();
friend class lib::singleton::StaticCreate<SessManagerImpl>;
virtual ~SessManagerImpl() {}
~SessManagerImpl() ;
/* ==== SessManager API ==== */
virtual void clear () ;
virtual void close () ;
virtual void reset () ;
virtual void load () ;
virtual void save () ;

View file

@ -2438,6 +2438,30 @@ The general idea is, that each facade interface actually provides access to a sp
|//Lumiera's major interfaces//|c
</pre>
</div>
<div title="LifecycleAdvisor" modifier="Ichthyostega" modified="201010230311" created="201010220319" tags="def SessionLogic impl" changecount="7">
<pre>An implementation facility used by the session manager implementation to ensure a consistent lifecycle. A template-method-like skeleton of operations can be invoked by the session manager to go through the various lifecycle stages, while the actual implementation functionality is delegated to facilities within the session. The assumption is for the lifecycle advisor to be executed within a controlled environment, a single instance and single threaded.
---------------------
!Implementation notes
The goal on the implementation level is to get a consolidated view on the whole lifecycle.
Reading the source code should convey a complete picture about what is going on with respect to the session lifecycle.
!!entrance points
;close
: the shutdown sequence cleanly unwinds any contents and returns into //uninitialised state.//
;reset
: is compriesd of two phases: shutdown and startup of an empty new session
: shutdown will be skipped automatically when uninitialised
: startup has to inject default session content
;load
: similar to the startup sequence of __reset__, but injects serialised content
Note that, while causing a short //freeze period,// __saving__ and __(re-)building__ aren't considered lifecycle operations
!!operation sequences
The ''pull up'' sequence performes basic initialsiation of the session facilities and then, after the pImpl-switch, executes the various loading and startup phases. Any previous session switched away is assumed to unwind automatically, nothing is done especially to disconnnect such an existing session, we simply require the session subsystem to be in a pristine state initially.
The ''shut down'' sequence does exactly that: halt processing and rendering, disconnect an existing session, if any, get back into initial state. It doesn't care for unwinding session contents, which is assumed to happen automatically when references to previous session contents go out of scope.
</pre>
</div>
<div title="LoadingMedia" modifier="Ichthyostega" modified="200806030203" created="200709220005" tags="design spec" changecount="4">
@ -4565,7 +4589,7 @@ The session and the models rely on dependent objects beeing kept updated and con
&amp;rarr; see [[details here...|ModelDependencies]]
</pre>
</div>
<div title="SessionInterface" modifier="Ichthyostega" modified="201006190502" created="200904242108" tags="SessionLogic GuiIntegration design draft discuss" changecount="49">
<div title="SessionInterface" modifier="Ichthyostega" modified="201010210035" created="200904242108" tags="SessionLogic GuiIntegration design draft discuss" changecount="52">
<pre>&quot;Session Interface&quot;, when used in a more general sense, denotes a compound of several interfaces and facilities, together forming the primary access point to the user visible contents and state of the editing project.
* the API of the session class
* the accompanying management interface (SessionManager API)
@ -4603,7 +4627,7 @@ To the contrary, the ''generic'' API is related to a //current location (state),
* to destroy
!!exploring session contents
Typically, the list of timelines serves as starting point for exploring the model. Basically, any kind of object could be attached everywhere, but both the GUI and the Builder rely on assumptions regarding the [[overall model structure|HighLevelModel]] &amp;mdash; silently ignoring content not in line. This corresponds to the //dedicated API functions// on specific kinds of objects, which allow to retrieve content according to this assumed structure conveniently and with strong typing. From the timeline and the sequence linked to it you'll get the root track, and from there the sub-tracks and the clips located on them, which in turn may have attachments (effects, transitions, labels).
Typically, the list of timelines serves as starting point for exploring the model. Basically, any kind of object could be attached everywhere, but both the GUI and the Builder rely on assumptions regarding the [[overall model structure|HighLevelModel]] &amp;mdash; silently ignoring content not in line. This corresponds to the //dedicated API functions// on specific kinds of objects, which allow to retrieve content according to this assumed structure conveniently and with strong typing. From the timeline and the associated sequence you'll get the root track, and from there the sub-tracks and the clips located on them, which in turn may have attachments (effects, transitions, labels).
On the other hand, arbitrary structures can be retrieved using the //generic API:// Contents can be discovered on the QueryFocus, which automatically follows the //point of mutation,// but can be moved to any point using the {{{QueryFocus::attach(obj)}}} function.
!!queries and defaults
@ -4612,7 +4636,7 @@ Queries can retrieve the immediate children, or the complete contents of a scope
{{red{WIP ... just emerging}}}
!!discovery and mutations
The discovery functions available on these ~APIs are wired such as to return suitably typed MObjectRef instances always. These are small value objects and can be used to invoke operations (both query and mutating) on the underlying object within the session. Raw placement references aren't exposed on these outward interfaces.
The discovery functions available on these ~APIs are wired such as to return suitably typed MObjectRef instances always. These are small value objects and can be used to invoke operations (both query and mutating) on the underlying object within the session. Raw placement (language)references aren't exposed on these outward interfaces.
While this protects against accessing dangling references, it can't prevent clients from invoking any mutating operation directly on these references. It would be conceivable, by using proxies, to create and invoke commands automatically. But we rather don't want to go this route, because
* Lumiera is an application focussed on very specific usage, not a general purpose library or framework
@ -4641,7 +4665,7 @@ Currently, I'm planning to modify MObjectRef to return only a const ref to the u
&lt;&lt;&lt;
</pre>
</div>
<div title="SessionLifecycle" modifier="Ichthyostega" modified="201001070440" created="200911070329" tags="SessionLogic spec" changecount="23">
<div title="SessionLifecycle" modifier="Ichthyostega" modified="201010220313" created="200911070329" tags="SessionLogic spec" changecount="25">
<pre>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'', 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
@ -4651,6 +4675,8 @@ Currently, I'm planning to modify MObjectRef to return only a const ref to the u
!Role of the session manager
The SessionManager is responsible for conducting the session lifecycle. Accessible through the static interface {{{Session::current}}}, it exposes the actual session as a ~PImpl. Both session manager and session are indeed interfaces, backed by implementation classes belonging to ~Proc-Layer's internals. Loading, saving, resetting and closing are the primary public operations of the session manager, each causing the respective lifecycle event.
Beyond that, client code usually doesn't interact much with the lifecycle, which mostly is a pattern of events to happen in a well-defined sequence. So the //implementation// of the session management operations has to comply to this lifecycle, and does so by relying on a self-contained implementation service, the LifecycleAdvisor. But (contrary to an application framework) the lifecycle of the Lumiera session is rather fixed, the only possibility for configuration or extension being the [[lifecycle hooks|LifecycleEvent]], where other parts of the system (and even plug-ins) may install some callback methods.
!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 &quot;properties&quot; 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]].
@ -4659,8 +4685,7 @@ Each of these facilities relies on a separate access point to session services,
Currently, the understanding is for some global mechanism to hold any command execution, script running and further object access by the GUI //prior//&amp;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.
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. At various stages within the lifecycle, specific LifecycleEvent hooks are activated, which serve as an extension point for other parts of the system to install callback functions to execute additional behaviour at the right moment.
!!!building (or loading) a session
# as a preparation step, a new implementation instance is created, alongside with any supporting facilities (especially the PlacementIndex)