During the last months, a lot of gradual changes to policies and guidelines had been finalised and documented. Moreover, recent clarifications regarding memory management in the render engine will lead to a clearer definition of the »Vault« Layer's role, with some ''engine code'' expected to move down. On this occasion, I reworked relevant sections in the documentation to confirm the role that now falls to central parts of the overall architecture. Notably I decided to rename those parts related to the application backbone as »Vessel« — to underpin the more active and defining role they assumed.
154 lines
9.2 KiB
Text
154 lines
9.2 KiB
Text
Application : Start-up and Subsystems
|
|
=====================================
|
|
:Date: 2009
|
|
:Author: Ichthyo
|
|
|
|
//MENU: label Start-up
|
|
|
|
.the purpose of »Application-main«
|
|
Lumiera is envisaged as a heavyweight yet coherent »**Application**« -- which implies
|
|
it is not so much seen as ``a platform'', framework or operating system. And, in line
|
|
with this understanding, we place no emphasis on possible configuration changes and state
|
|
transitions within the running application _as a whole._ The ability to react ``life'' to
|
|
some global setting changes can be limited to relevant special cases. And, while it is
|
|
indeed possible to close and load a new Session from a running application instance, in
|
|
most parts of the code base we assume the application just to be ``up and running'' -- all
|
|
required services are assumed to be available and interfaces to be accessible. If some of
|
|
these assumptions break, an exception is raised and the application unwinds.
|
|
Without doubt, these decisions represent a forcible simplification, and might be limiting
|
|
at times, yet serve us well to cut down complexity in large parts of the code base --
|
|
however, there needs to be _one dedicated realm_ to deal with all these specific concerns
|
|
related to configuration, interdependency and lifecyle.
|
|
|
|
|
|
The Vessel
|
|
----------
|
|
So we treat all these concerns within a small and self contained structure, called the
|
|
»**Vessel**«, and clearly set apart from all the other layers, services and subsystems.
|
|
This dedicated _Application Realm_ is organised around the ``Application main object''.footnote:[
|
|
This is the singleton `vessel::Voyage`, which is triggered by the `main` function
|
|
of the Lumiera Application. The sourcecode is kept in a separate folder 'src/vessel'
|
|
and linked into the shared library 'liblumieravessel.so']
|
|
Its purpose is to bootstrap and tear-down the application in a controlled way,
|
|
which includes to provide the backbone services, evaluate the start-up parameters
|
|
and to connect and start the necessary core components. The beginning and end of
|
|
this vessel's voyage therefore marks the *Lifecycle* of the application, as a
|
|
succession of ``lifecycle phases'' -- where almost any activity happens within
|
|
the _operational phase,_ when everything is considered ``up and running''.
|
|
|
|
Subsystems
|
|
~~~~~~~~~~
|
|
However, before becoming operational, the application must be bootstrapped, and at the end,
|
|
all parts need to be unwound cleanly. To organise this process, we identify a limited number
|
|
of *Subsystems* within the Application, which are self-sufficient and operate independently,
|
|
at least to some degree.
|
|
Each link:{ldoc}/design/architecture/Subsystems.html[Subsystem] is self contained
|
|
and groups some other parts and services, which typically work together and may
|
|
be mutually dependent. These subsystems represent a grouping, applied for the purpose
|
|
of starting and stopping the application in a regular way; they rather do not
|
|
correspond 1:1 to a layer, an interface, a class or a plugin. As a matter of fact,
|
|
they are _rather irrelevant_ outside the mentioned »Application realm«. A subsystem
|
|
may depend on other subsystems, which comprises a clear startup- and shutdown-ordering.
|
|
However, once the application is in normal operational mode, the subsystems turn
|
|
into a passive, guarding and managing role; the activities relevant for the
|
|
application's purpose rather rely on components, interfaces, services, all
|
|
aggregated into the three Layers »Stage«, »Steam« and »Vault«.
|
|
|
|
__We expect the following subsystems to be built eventually:__ +
|
|
Engine, Session, PlayOut, GUI, Script runner, Renderfarm node.
|
|
|
|
Organisation of Subsystems
|
|
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
Not all subsystems need to be started for any use of the application. A script-driven
|
|
use, or a renderfarm node does not need a GUI. So there is an overall global operation
|
|
mode of the application, controlled through the launching options, and determined during
|
|
the startup phase. It is the responsibility of the _Application main object_ to
|
|
pull up required functionality, which in turn might result in pulling up
|
|
further subsystems as dependencies.
|
|
|
|
Subsystems are defined by implementing the interface `vessel::Subsys`, which acts
|
|
as façade to conduct the lifecycle, find out about dependencies and shut down
|
|
the subsystem in the end. So this interface, together with the _Subsystem Runner,_
|
|
define a lifecycle protocol; each subsystem is free to implement this as it
|
|
sees fit. Typically, this façade will load plugins, register and provide further
|
|
_business interfaces,_ and especially set up the _Layer separation interfaces_
|
|
which canalise any communication going on between the layers.
|
|
|
|
The *GUI Façade* is special, while in compliance with this protocol. The actual
|
|
UI is loaded from a plug-in at runtime,footnote:[This corresponds to the vision
|
|
to allow for different Lumiera UI's -- maybe to support different working styles
|
|
or target audiences. If such is actually feasible remains to be clarified as of
|
|
2020; even while decoupled on a technical level, the session still needs to make
|
|
a lot of assumptions regarding the UI's capabilities and needs.]
|
|
and so the implementation of this façade needs to reside in the application core
|
|
realm; it will start a `GuiRunner` to load and activate the GUI plug-in, which
|
|
then in turn has to open the public _GUI Notification_ façade. The latter is
|
|
one of the _Layer separation interfaces_ and comprises the actual way for the
|
|
lower layers to activate and interact with the user interface.
|
|
|
|
Parallelism
|
|
~~~~~~~~~~~
|
|
Actually this scheme builds on the assumption that starting each subsystem will
|
|
not block the overall start/main/shutdown thread. Any subsystem is supposed
|
|
to spawn its own control/event threads if necessary. The Lumiera application
|
|
works fundamentally asynchronous. The user interface operates single threaded,
|
|
in accordance to long established best practices of UI programming. However,
|
|
any user interaction is translated into commands, sent down into the session
|
|
and handled there one by one. The result of processing such commands will be
|
|
pushed back up into the UI later and detached from the immediate interaction.
|
|
Likewise, the re-rendering caused by changes in the session is carried out
|
|
within the engine independently, relying on worker jobs and a thread pool.
|
|
|
|
|
|
Initialisation and Lifecycle
|
|
----------------------------
|
|
After some discussion,footnote:[
|
|
See the link:{rfc}/GlobalInitialization.html[GlobalInitialisation] RfC
|
|
from spring 2008. In the beginning, we all agreed to ``keep matters simple''
|
|
and build an `init()` function within one central piece of code everyone knows
|
|
and hooks into. However, while the outline of the application emerged, there
|
|
was a tension between the concern about _over-engineering_ versus the concern
|
|
about _tangled and unmanageable complexity._ At some point, an alternative
|
|
implementation based on lifecycle callbacks was elaborated, which then turned
|
|
into the solution described here. At that point, Lumiera ceased to be the
|
|
typical UI based application started-up by GTK-main, and the existing GTK
|
|
code was retrofitted to be launched from within a plug-in.
|
|
]
|
|
the design leaned toward loosely coupled parts and a formal lifecycle; which
|
|
saves us from investigating and coding up the various interdependencies
|
|
explicitly. Rather, all parts of the application have to comply to
|
|
link:{ldoc}/design/architecture/Subsystems.html#lifecycle[Lifecycle Phases],
|
|
and each part has to care for its own state transitions, invoked through
|
|
_lifecycle callbacks._ We can distinguish two distinct models how to deal
|
|
with lifecycle, and both are equally acceptable:
|
|
|
|
- Assuming that operations happen in response to some client's request,
|
|
this activation should go through a _service interface._ Interfaces
|
|
can be opened and closed in Lumiera, and this is accomplished by
|
|
hooking them up below some subsystem.
|
|
- However, some parts carry out continuous activities, and in that case
|
|
a _lifecycle hook_ should be registered, to limit activities to the
|
|
appropriate lifecycle phase.
|
|
|
|
Application Start
|
|
~~~~~~~~~~~~~~~~~
|
|
* some fundamental language-level facilities will be prepared during
|
|
_static initialisation._ At some point, execution enters `main(argc,arvv)`.
|
|
* `Voyage::init()` brings up the plugin loader and opens the config-interface.
|
|
* ...followed by triggering *ON_GLOBAL_INIT*
|
|
* the main thread then pulls up the subsystems (`Voyage::maybeStart(subsystem)`),
|
|
according to the command line options.
|
|
* within each subsystem, façade interfaces will be opened through the
|
|
interface/plug-in system.
|
|
* At this point, the GUI plug-in is loaded and launched, the windows created,
|
|
the UI event loop starts and the application becomes live.
|
|
* shutdown or failure of _any given subsystem_ initiates the shutdown sequence
|
|
by requesting all other running subsystems to terminate. In the typical case,
|
|
the UI subsystem will trigger this shutdown sequence, in response to closing
|
|
the main window.
|
|
* there is an *ON_GLOBAL_SHUTDOWN* event, which can be used for normal cleanup;
|
|
In case of an emergency exit, the *ON_EMERGENCY_EXIT* event is triggered alternatively.
|
|
* the Voyage destructor tears down the core systems (config-interface and pluginloader).
|
|
* we establish a policy to _prohibit any non-local and non-trivial activities_ during the
|
|
tear-down phase after leaving the `main()` function.
|
|
|