more eol-style
git-svn-id: svn://svn.icculus.org/gtkradiant/GtkRadiant/branches/ZeroRadiant.ab@187 8a3a26a2-13c4-0310-b231-cf6edde360e5
This commit is contained in:
@@ -1,190 +1,190 @@
|
||||
synapse code design documentation
|
||||
=================================
|
||||
|
||||
Objective:
|
||||
----------
|
||||
|
||||
Provide a simple cross platform layer to use dynamically loaded code
|
||||
inside a core application. Portability intended to win32 / linux / MacOS (?)
|
||||
|
||||
Main features are:
|
||||
|
||||
- designed for single process only, no remote clients, no asynchronous processes
|
||||
- a client/server architecture, based on configuration files: a main binary,
|
||||
loading a set of shared objects
|
||||
|
||||
Constraints:
|
||||
------------
|
||||
|
||||
- large existing plugin code in Radiant!
|
||||
must be compatible with minimal changes, specially for plugins (i.e. clients)
|
||||
|
||||
- make things as much transparent as possible
|
||||
(ideally, no real difference between a static linkage and dynamic linkage,
|
||||
cf usage of #define macros to wrap a function call onto a code pointer)
|
||||
|
||||
Features:
|
||||
---------
|
||||
|
||||
Gather as much generic code as possible in a static .lib with minimal dependencies
|
||||
(only dependency should be configuration files parser)
|
||||
|
||||
NOTE: current effective dependency is STL / glib / xml
|
||||
|
||||
Main executable implemented as a server, all others as clients. What has to
|
||||
be done for a server / what has to be done for a client needs to be documented.
|
||||
Provide as much scripts and tools and guidelines as needed (scripted generation of
|
||||
some .h files?)
|
||||
|
||||
Proposed implementation:
|
||||
------------------------
|
||||
|
||||
- have linux/ and win32/ subdirectories with OS-specific implementations
|
||||
(such as dynamically loading shared objects, and doing the initial query?)
|
||||
|
||||
- reduce the API of a client to the minimum: one exported function?
|
||||
provide a squeleton to make new clients easily?
|
||||
|
||||
Server use case:
|
||||
1) build information about location of the modules (from code and config files)
|
||||
2) load all modules and query information about their APIs
|
||||
NOTE: could read the APIs from some XML description files instead of
|
||||
querying it from the modules?
|
||||
3) build information about the required function tables
|
||||
i.e.: setup a list with the function tables to be filled in, and what they
|
||||
need to be filled in with.
|
||||
4) resolve the function table
|
||||
NOTE: is this iterative? will some plugins request more APIs as they get filled
|
||||
up?
|
||||
NOTE: do we have optional tables?
|
||||
5) unload unreferences modules
|
||||
NOTE: we don't expect to be unloading a LOT of modules, otherwise we would
|
||||
setup a solution that allows exploring of the APIs a given module provides
|
||||
from a file description. Or you could 'cache' that (md5-checksum the file, and
|
||||
maintain an XML list).
|
||||
|
||||
Client use case:
|
||||
1) dynamically loaded
|
||||
2) prompted for the interfaces it provides
|
||||
2) prompted for the interfaces it requires
|
||||
3) either unloaded, or told what interfaces have been filled in
|
||||
|
||||
The client module exports an Synapse_EnumerateInterfaces entry point
|
||||
This returns an ISynapseClient, which lists what the plugin provides, and what it requires
|
||||
|
||||
The APIs:
|
||||
|
||||
An interface is a function table, GUID / major string / minor string
|
||||
GUID is a shortcut to reference a major string (i.e. the human readable thing)
|
||||
the GUID / major string is unique for a given interface
|
||||
minor string is used to reference a particular version of an API
|
||||
(for instance when talking about image loading functionality, tga and jpg etc.)
|
||||
|
||||
The GUID scheme is handy because it provides easy tests. They are not strictly
|
||||
necessary but we will probably want to keep them. Should we extend to GUIDs
|
||||
for minor too?
|
||||
|
||||
Roadmap:
|
||||
--------
|
||||
|
||||
Need to convert the core (as server) and the required modules. Will have
|
||||
clearer view of what's to be done along the way.
|
||||
|
||||
Implementation design:
|
||||
----------------------
|
||||
|
||||
There is a client and server side to synapse. Typically server is in Radiant or q3map,
|
||||
client is in any module. For implementation, we have one server class and one client class.
|
||||
It would be possible to have two seperate libraries, synapse-client and synapse-server. But
|
||||
that only brings down the statically linked stuff to make things more complicated build-sysem
|
||||
wise.
|
||||
|
||||
Initial implementation has been using isynapse.h and synapse.h, to provide a pure virtual
|
||||
base class for server and client. But that doesn't bring any major functionality, it's easier
|
||||
if both sides see the full API of the client and server classes.
|
||||
|
||||
A side problem is the diagnostic printing functionality. For easy debugging we require that
|
||||
the synapse code can have access to a Sys_Printf or similar function at all times (that is for
|
||||
client and server implementation). On client we will pipe through the main API to the server
|
||||
as soon as we can in most cases. Using Sys_Printf would bring us to a dead end situation, since
|
||||
when synapse is used as the server, the main code implements it's own Sys_Printf stuff.
|
||||
Instead we introduce a local Syn_Printf implementation, which can be overriden in the server
|
||||
to point to the appropriate print functions.
|
||||
|
||||
Runtime config:
|
||||
---------------
|
||||
|
||||
Something that has not been looked upon a lot yet, runtime configuration. What interfaces
|
||||
are loaded etc. Ideally, from an XML config file. A client explicitely requests the
|
||||
server to load all the interfaces it requires (in this case, the client is radiant or
|
||||
q3map).
|
||||
|
||||
Plugins are somewhat out of the 'required interfaces' frame, since they are loaded
|
||||
whenever they are found. It is possible however that some plugins would not want to be
|
||||
loaded in if the game doesn't match etc. in case they would need to access the global
|
||||
config?
|
||||
|
||||
In most cases a given API is only required once for editor functionality. (VFS for
|
||||
instance), so our #define strategy for easy mapping of the functions should still work.
|
||||
|
||||
Version checks, reference counting:
|
||||
------------------------------------
|
||||
|
||||
Need version checking at several levels. A version string (major/minor) on the main API
|
||||
entry point (straight in the exported function to save as much as possible for
|
||||
compatibility). For individual APIs, we have been feeding the struct size into the first
|
||||
int of the struct so far, and it has worked very well.
|
||||
|
||||
Reference counting: we introduced class based APIs to solve the ref counting issues,
|
||||
which are not easy to solve on C function tables. That problem would arise in plugin
|
||||
situations where we might want to 'reload' or 'unload' some plugins. The server could
|
||||
keep track of the ref count.
|
||||
|
||||
Caching?
|
||||
--------
|
||||
|
||||
We are going to load every shared object we find and query it for it's interfaces. Then
|
||||
we will unload the stuff we don't want. This is going to slow down the startup process.
|
||||
We could extract the API information in a cache to avoid the loading step.
|
||||
|
||||
Interfaces with multiple minors against I* objects?
|
||||
---------------------------------------------------
|
||||
|
||||
Looking at the iimage.h API, why not having instead something that enumerates C++ objects
|
||||
directly? Mainly because we want to be able to spread several minors accross multiple modules
|
||||
and still use them together. And straight laid out function tables in C structs are only
|
||||
one indirection when the table is static.
|
||||
|
||||
This raises a broader topic, instead of requesting APIs, we could request objects directly.
|
||||
Would that be of any use?
|
||||
|
||||
Loading interfaces / resolving interdependencies strategy
|
||||
---------------------------------------------------------
|
||||
|
||||
Some notes about how we load the modules and resolve interdependencies:
|
||||
|
||||
We want to avoid requesting a module for an API it provides before all the APIs it requires
|
||||
have been filled in (mostly stability concerns, a module may be doing whatever internally
|
||||
when we request something from it). The exception being the module we are trying to resolve
|
||||
for (since we need a start point for resolution). But in all likelyness we resolve for radiant
|
||||
or q3map for instance.
|
||||
|
||||
With this approach, it is possible that some situations could not be resolved, for instance:
|
||||
Radiant
|
||||
requires A
|
||||
provides B
|
||||
module 1
|
||||
requires C
|
||||
provides A
|
||||
module 2
|
||||
requires A
|
||||
provides C
|
||||
if we start by resolving Radiant, we will get stuck
|
||||
if we are ready to ask module to provide the API even though the required is not meant, it would work
|
||||
but that kind of situation is very unlikely, so sticking to safer strategy
|
||||
|
||||
Configuration
|
||||
-------------
|
||||
|
||||
the config info needs to go down to the clients too
|
||||
synapse code design documentation
|
||||
=================================
|
||||
|
||||
Objective:
|
||||
----------
|
||||
|
||||
Provide a simple cross platform layer to use dynamically loaded code
|
||||
inside a core application. Portability intended to win32 / linux / MacOS (?)
|
||||
|
||||
Main features are:
|
||||
|
||||
- designed for single process only, no remote clients, no asynchronous processes
|
||||
- a client/server architecture, based on configuration files: a main binary,
|
||||
loading a set of shared objects
|
||||
|
||||
Constraints:
|
||||
------------
|
||||
|
||||
- large existing plugin code in Radiant!
|
||||
must be compatible with minimal changes, specially for plugins (i.e. clients)
|
||||
|
||||
- make things as much transparent as possible
|
||||
(ideally, no real difference between a static linkage and dynamic linkage,
|
||||
cf usage of #define macros to wrap a function call onto a code pointer)
|
||||
|
||||
Features:
|
||||
---------
|
||||
|
||||
Gather as much generic code as possible in a static .lib with minimal dependencies
|
||||
(only dependency should be configuration files parser)
|
||||
|
||||
NOTE: current effective dependency is STL / glib / xml
|
||||
|
||||
Main executable implemented as a server, all others as clients. What has to
|
||||
be done for a server / what has to be done for a client needs to be documented.
|
||||
Provide as much scripts and tools and guidelines as needed (scripted generation of
|
||||
some .h files?)
|
||||
|
||||
Proposed implementation:
|
||||
------------------------
|
||||
|
||||
- have linux/ and win32/ subdirectories with OS-specific implementations
|
||||
(such as dynamically loading shared objects, and doing the initial query?)
|
||||
|
||||
- reduce the API of a client to the minimum: one exported function?
|
||||
provide a squeleton to make new clients easily?
|
||||
|
||||
Server use case:
|
||||
1) build information about location of the modules (from code and config files)
|
||||
2) load all modules and query information about their APIs
|
||||
NOTE: could read the APIs from some XML description files instead of
|
||||
querying it from the modules?
|
||||
3) build information about the required function tables
|
||||
i.e.: setup a list with the function tables to be filled in, and what they
|
||||
need to be filled in with.
|
||||
4) resolve the function table
|
||||
NOTE: is this iterative? will some plugins request more APIs as they get filled
|
||||
up?
|
||||
NOTE: do we have optional tables?
|
||||
5) unload unreferences modules
|
||||
NOTE: we don't expect to be unloading a LOT of modules, otherwise we would
|
||||
setup a solution that allows exploring of the APIs a given module provides
|
||||
from a file description. Or you could 'cache' that (md5-checksum the file, and
|
||||
maintain an XML list).
|
||||
|
||||
Client use case:
|
||||
1) dynamically loaded
|
||||
2) prompted for the interfaces it provides
|
||||
2) prompted for the interfaces it requires
|
||||
3) either unloaded, or told what interfaces have been filled in
|
||||
|
||||
The client module exports an Synapse_EnumerateInterfaces entry point
|
||||
This returns an ISynapseClient, which lists what the plugin provides, and what it requires
|
||||
|
||||
The APIs:
|
||||
|
||||
An interface is a function table, GUID / major string / minor string
|
||||
GUID is a shortcut to reference a major string (i.e. the human readable thing)
|
||||
the GUID / major string is unique for a given interface
|
||||
minor string is used to reference a particular version of an API
|
||||
(for instance when talking about image loading functionality, tga and jpg etc.)
|
||||
|
||||
The GUID scheme is handy because it provides easy tests. They are not strictly
|
||||
necessary but we will probably want to keep them. Should we extend to GUIDs
|
||||
for minor too?
|
||||
|
||||
Roadmap:
|
||||
--------
|
||||
|
||||
Need to convert the core (as server) and the required modules. Will have
|
||||
clearer view of what's to be done along the way.
|
||||
|
||||
Implementation design:
|
||||
----------------------
|
||||
|
||||
There is a client and server side to synapse. Typically server is in Radiant or q3map,
|
||||
client is in any module. For implementation, we have one server class and one client class.
|
||||
It would be possible to have two seperate libraries, synapse-client and synapse-server. But
|
||||
that only brings down the statically linked stuff to make things more complicated build-sysem
|
||||
wise.
|
||||
|
||||
Initial implementation has been using isynapse.h and synapse.h, to provide a pure virtual
|
||||
base class for server and client. But that doesn't bring any major functionality, it's easier
|
||||
if both sides see the full API of the client and server classes.
|
||||
|
||||
A side problem is the diagnostic printing functionality. For easy debugging we require that
|
||||
the synapse code can have access to a Sys_Printf or similar function at all times (that is for
|
||||
client and server implementation). On client we will pipe through the main API to the server
|
||||
as soon as we can in most cases. Using Sys_Printf would bring us to a dead end situation, since
|
||||
when synapse is used as the server, the main code implements it's own Sys_Printf stuff.
|
||||
Instead we introduce a local Syn_Printf implementation, which can be overriden in the server
|
||||
to point to the appropriate print functions.
|
||||
|
||||
Runtime config:
|
||||
---------------
|
||||
|
||||
Something that has not been looked upon a lot yet, runtime configuration. What interfaces
|
||||
are loaded etc. Ideally, from an XML config file. A client explicitely requests the
|
||||
server to load all the interfaces it requires (in this case, the client is radiant or
|
||||
q3map).
|
||||
|
||||
Plugins are somewhat out of the 'required interfaces' frame, since they are loaded
|
||||
whenever they are found. It is possible however that some plugins would not want to be
|
||||
loaded in if the game doesn't match etc. in case they would need to access the global
|
||||
config?
|
||||
|
||||
In most cases a given API is only required once for editor functionality. (VFS for
|
||||
instance), so our #define strategy for easy mapping of the functions should still work.
|
||||
|
||||
Version checks, reference counting:
|
||||
------------------------------------
|
||||
|
||||
Need version checking at several levels. A version string (major/minor) on the main API
|
||||
entry point (straight in the exported function to save as much as possible for
|
||||
compatibility). For individual APIs, we have been feeding the struct size into the first
|
||||
int of the struct so far, and it has worked very well.
|
||||
|
||||
Reference counting: we introduced class based APIs to solve the ref counting issues,
|
||||
which are not easy to solve on C function tables. That problem would arise in plugin
|
||||
situations where we might want to 'reload' or 'unload' some plugins. The server could
|
||||
keep track of the ref count.
|
||||
|
||||
Caching?
|
||||
--------
|
||||
|
||||
We are going to load every shared object we find and query it for it's interfaces. Then
|
||||
we will unload the stuff we don't want. This is going to slow down the startup process.
|
||||
We could extract the API information in a cache to avoid the loading step.
|
||||
|
||||
Interfaces with multiple minors against I* objects?
|
||||
---------------------------------------------------
|
||||
|
||||
Looking at the iimage.h API, why not having instead something that enumerates C++ objects
|
||||
directly? Mainly because we want to be able to spread several minors accross multiple modules
|
||||
and still use them together. And straight laid out function tables in C structs are only
|
||||
one indirection when the table is static.
|
||||
|
||||
This raises a broader topic, instead of requesting APIs, we could request objects directly.
|
||||
Would that be of any use?
|
||||
|
||||
Loading interfaces / resolving interdependencies strategy
|
||||
---------------------------------------------------------
|
||||
|
||||
Some notes about how we load the modules and resolve interdependencies:
|
||||
|
||||
We want to avoid requesting a module for an API it provides before all the APIs it requires
|
||||
have been filled in (mostly stability concerns, a module may be doing whatever internally
|
||||
when we request something from it). The exception being the module we are trying to resolve
|
||||
for (since we need a start point for resolution). But in all likelyness we resolve for radiant
|
||||
or q3map for instance.
|
||||
|
||||
With this approach, it is possible that some situations could not be resolved, for instance:
|
||||
Radiant
|
||||
requires A
|
||||
provides B
|
||||
module 1
|
||||
requires C
|
||||
provides A
|
||||
module 2
|
||||
requires A
|
||||
provides C
|
||||
if we start by resolving Radiant, we will get stuck
|
||||
if we are ready to ask module to provide the API even though the required is not meant, it would work
|
||||
but that kind of situation is very unlikely, so sticking to safer strategy
|
||||
|
||||
Configuration
|
||||
-------------
|
||||
|
||||
the config info needs to go down to the clients too
|
||||
for instance, mapxml loaded for q3map or radiant, doesn't rely on the same major?
|
||||
@@ -1,59 +1,59 @@
|
||||
XML config files for customized synapse initialization at runtime
|
||||
-----------------------------------------------------------------
|
||||
|
||||
Objective:
|
||||
----------
|
||||
|
||||
We have to assign the minors of the APIs to function tables
|
||||
(and possibly to the API managers) at runtime from some config files.
|
||||
|
||||
For instance in Q3 or RTCW mode, we will want to fill in
|
||||
g_FileSystemTable and g_ShadersTable with the "quake3" minor. Whereas
|
||||
those tables will be filled in with a different minor for HL mode.
|
||||
|
||||
This affects SYN_REQUIRE for all the clients of the system, so that
|
||||
config will need to be global and passed around to the clients.
|
||||
|
||||
Implementation:
|
||||
---------------
|
||||
|
||||
an XML hierarchy to describe the APIs:
|
||||
<client name="CORE">
|
||||
<api name="vfs">
|
||||
quake3
|
||||
</api>
|
||||
<api name="shaders">
|
||||
quake3
|
||||
</api>
|
||||
..
|
||||
</client>
|
||||
<client name="SHADERS">
|
||||
|
||||
</client>
|
||||
|
||||
Each client will have to be identified by a specific name, if a name in the
|
||||
config is not found in the client list, the init should fail. A client can
|
||||
still be hardcoded and not appear in this list though.
|
||||
(a GetName() function to synapse client)
|
||||
|
||||
SYN_REQUIRE_ANY support will work for strict API lists. Just the same way
|
||||
we do the simple SYN_REQUIRE
|
||||
|
||||
Discussion:
|
||||
-----------
|
||||
|
||||
We only deal with SYN_REQUIRE. It is possible that at some point we will want
|
||||
to customize the SYN_PROVIDE too. For instance depending on the game mode, a
|
||||
same module could provide two different minors. Couldn't provide the two minors
|
||||
at the same time though, only one.
|
||||
|
||||
Implementation:
|
||||
---------------
|
||||
|
||||
Default config file will be synapse.config in the gametools path. We can override
|
||||
this later with a custom line in the .game file. Should Synapse be able to operate
|
||||
without this config file though, as it is looked up by the main program and handed
|
||||
over to synapse before init? Possibly .. we'll just pass a NULL config node ptr
|
||||
|
||||
Add the config file path to CSynpaseServer::Initialize, pass the loaded XML file to
|
||||
XML config files for customized synapse initialization at runtime
|
||||
-----------------------------------------------------------------
|
||||
|
||||
Objective:
|
||||
----------
|
||||
|
||||
We have to assign the minors of the APIs to function tables
|
||||
(and possibly to the API managers) at runtime from some config files.
|
||||
|
||||
For instance in Q3 or RTCW mode, we will want to fill in
|
||||
g_FileSystemTable and g_ShadersTable with the "quake3" minor. Whereas
|
||||
those tables will be filled in with a different minor for HL mode.
|
||||
|
||||
This affects SYN_REQUIRE for all the clients of the system, so that
|
||||
config will need to be global and passed around to the clients.
|
||||
|
||||
Implementation:
|
||||
---------------
|
||||
|
||||
an XML hierarchy to describe the APIs:
|
||||
<client name="CORE">
|
||||
<api name="vfs">
|
||||
quake3
|
||||
</api>
|
||||
<api name="shaders">
|
||||
quake3
|
||||
</api>
|
||||
..
|
||||
</client>
|
||||
<client name="SHADERS">
|
||||
|
||||
</client>
|
||||
|
||||
Each client will have to be identified by a specific name, if a name in the
|
||||
config is not found in the client list, the init should fail. A client can
|
||||
still be hardcoded and not appear in this list though.
|
||||
(a GetName() function to synapse client)
|
||||
|
||||
SYN_REQUIRE_ANY support will work for strict API lists. Just the same way
|
||||
we do the simple SYN_REQUIRE
|
||||
|
||||
Discussion:
|
||||
-----------
|
||||
|
||||
We only deal with SYN_REQUIRE. It is possible that at some point we will want
|
||||
to customize the SYN_PROVIDE too. For instance depending on the game mode, a
|
||||
same module could provide two different minors. Couldn't provide the two minors
|
||||
at the same time though, only one.
|
||||
|
||||
Implementation:
|
||||
---------------
|
||||
|
||||
Default config file will be synapse.config in the gametools path. We can override
|
||||
this later with a custom line in the .game file. Should Synapse be able to operate
|
||||
without this config file though, as it is looked up by the main program and handed
|
||||
over to synapse before init? Possibly .. we'll just pass a NULL config node ptr
|
||||
|
||||
Add the config file path to CSynpaseServer::Initialize, pass the loaded XML file to
|
||||
the clients. Do we need to wrap in an object with some convenience functions?
|
||||
@@ -1,85 +1,85 @@
|
||||
Release / Reload of modules
|
||||
---------------------------
|
||||
|
||||
The 'not active' modules are unloaded after startup
|
||||
Plugins should be allowed to be unloaded and reloaded on the fly
|
||||
Modules too, when possible?
|
||||
|
||||
Don't want to 'force' all plugins to have unload capabilities
|
||||
Just has to be something specified at compile time wether or not they can unload 'cleanly'
|
||||
|
||||
Dependency implications. When you release a module, you need to remove a number of interfaces.
|
||||
If those interfaces are being used, can you explicitely ask for them to be unloaded?
|
||||
|
||||
Also, problem with plugins breaking the startup process:
|
||||
http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=441
|
||||
|
||||
The most important is to provide a clean shutdown method
|
||||
What's the != between unload and shutdown?
|
||||
|
||||
Means that the server proceeds to the shutdown, and nothing else is supposed to be making
|
||||
calls through APIs post shutdown.
|
||||
|
||||
Should be done in 3 steps:
|
||||
#1 prepare shutdown, all APIs are active, just release and save all the stuff you want
|
||||
#2 tell the modules to shutdown, i.e. release the APIs they point to? (at this point they can't call through anymore)
|
||||
#3 force all things to be unloaded, warn about reference count problems
|
||||
|
||||
What is different when we unload a module, and we want to keep the editor up?
|
||||
All the interfaces obtained from this plugin need to be released
|
||||
If some pure virtual classes have been obtained from this plugin, we need a mecanism to have them removed
|
||||
Do we need a first path to check if the unload procedure is going to be allowed?
|
||||
|
||||
For instance, a plugin that provides custom entities rendering etc.
|
||||
Need to unload first, then need to reload (scan the map again, rebuild)
|
||||
|
||||
Summing up, when doing a reload we need to keep track of the modules and let them know after the
|
||||
module has been reloaded, so that the links can be rebuilt. When doing unload we need to do a
|
||||
'check' pass prior to anything to know if the release is possible. Because it does not depend
|
||||
on the module we unload, it depends on the other clients that use it.
|
||||
|
||||
Objectives:
|
||||
-----------
|
||||
|
||||
- 'release check' of a module
|
||||
walk through the interfaces this module has provided, and make sure the release will be possible
|
||||
- 'release' of a module
|
||||
actually drop all the interfaces. this should be done only after a 'release check'
|
||||
if something fails here, we are in an unstable state and need to abort
|
||||
- 'client shutdown' unused modules after initial startup
|
||||
actual DLL unload / complete shutdown of the client interface
|
||||
this comes after 'release check' and 'release'
|
||||
- 'refresh' modules
|
||||
'release check', 'release', 'shutdown' and then, reload and build the links again
|
||||
- 'core shutdown'
|
||||
'release' and force 'shutdown' of all clients
|
||||
even if we encounter some interfaces that we are not able to release cleanly
|
||||
force things and shutdown all clients
|
||||
then the core process is ready to exit..
|
||||
|
||||
Constraints:
|
||||
------------
|
||||
|
||||
- refresh and shutdown are sharing some code
|
||||
- the 'release' part of a module refresh may not be always possible (that's what 'release check' is there for)
|
||||
- 'core shutdown' has to be forced to happen, in the safest way possible obviously
|
||||
|
||||
Implementation:
|
||||
---------------
|
||||
|
||||
- 'client shutdown' comes first
|
||||
this is used after initial startup, since we don't have to do a prior 'release' on those
|
||||
this will be used in the 'core shutdown' and 'refresh' too
|
||||
- then 'core shutdown' procedure?
|
||||
that's the most urgent thing we need
|
||||
but 'release check' and 'release' have to be written in and used during 'core shutdown'
|
||||
- 'refresh' takes an essential part of the design, but that's not something we need to have written right now?
|
||||
(it mostly relies on 'release check' 'release' 'client shutdown', and then reload and rebuild the links)
|
||||
|
||||
'client shutdown':
|
||||
this is server side code though, you tell the server 'shutdown this client'
|
||||
we don't have to exchange anything with the client while we do that
|
||||
(written initially for unload of unused modules after startup)
|
||||
|
||||
'core shutdown':
|
||||
is a shutdown of all clients
|
||||
Release / Reload of modules
|
||||
---------------------------
|
||||
|
||||
The 'not active' modules are unloaded after startup
|
||||
Plugins should be allowed to be unloaded and reloaded on the fly
|
||||
Modules too, when possible?
|
||||
|
||||
Don't want to 'force' all plugins to have unload capabilities
|
||||
Just has to be something specified at compile time wether or not they can unload 'cleanly'
|
||||
|
||||
Dependency implications. When you release a module, you need to remove a number of interfaces.
|
||||
If those interfaces are being used, can you explicitely ask for them to be unloaded?
|
||||
|
||||
Also, problem with plugins breaking the startup process:
|
||||
http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=441
|
||||
|
||||
The most important is to provide a clean shutdown method
|
||||
What's the != between unload and shutdown?
|
||||
|
||||
Means that the server proceeds to the shutdown, and nothing else is supposed to be making
|
||||
calls through APIs post shutdown.
|
||||
|
||||
Should be done in 3 steps:
|
||||
#1 prepare shutdown, all APIs are active, just release and save all the stuff you want
|
||||
#2 tell the modules to shutdown, i.e. release the APIs they point to? (at this point they can't call through anymore)
|
||||
#3 force all things to be unloaded, warn about reference count problems
|
||||
|
||||
What is different when we unload a module, and we want to keep the editor up?
|
||||
All the interfaces obtained from this plugin need to be released
|
||||
If some pure virtual classes have been obtained from this plugin, we need a mecanism to have them removed
|
||||
Do we need a first path to check if the unload procedure is going to be allowed?
|
||||
|
||||
For instance, a plugin that provides custom entities rendering etc.
|
||||
Need to unload first, then need to reload (scan the map again, rebuild)
|
||||
|
||||
Summing up, when doing a reload we need to keep track of the modules and let them know after the
|
||||
module has been reloaded, so that the links can be rebuilt. When doing unload we need to do a
|
||||
'check' pass prior to anything to know if the release is possible. Because it does not depend
|
||||
on the module we unload, it depends on the other clients that use it.
|
||||
|
||||
Objectives:
|
||||
-----------
|
||||
|
||||
- 'release check' of a module
|
||||
walk through the interfaces this module has provided, and make sure the release will be possible
|
||||
- 'release' of a module
|
||||
actually drop all the interfaces. this should be done only after a 'release check'
|
||||
if something fails here, we are in an unstable state and need to abort
|
||||
- 'client shutdown' unused modules after initial startup
|
||||
actual DLL unload / complete shutdown of the client interface
|
||||
this comes after 'release check' and 'release'
|
||||
- 'refresh' modules
|
||||
'release check', 'release', 'shutdown' and then, reload and build the links again
|
||||
- 'core shutdown'
|
||||
'release' and force 'shutdown' of all clients
|
||||
even if we encounter some interfaces that we are not able to release cleanly
|
||||
force things and shutdown all clients
|
||||
then the core process is ready to exit..
|
||||
|
||||
Constraints:
|
||||
------------
|
||||
|
||||
- refresh and shutdown are sharing some code
|
||||
- the 'release' part of a module refresh may not be always possible (that's what 'release check' is there for)
|
||||
- 'core shutdown' has to be forced to happen, in the safest way possible obviously
|
||||
|
||||
Implementation:
|
||||
---------------
|
||||
|
||||
- 'client shutdown' comes first
|
||||
this is used after initial startup, since we don't have to do a prior 'release' on those
|
||||
this will be used in the 'core shutdown' and 'refresh' too
|
||||
- then 'core shutdown' procedure?
|
||||
that's the most urgent thing we need
|
||||
but 'release check' and 'release' have to be written in and used during 'core shutdown'
|
||||
- 'refresh' takes an essential part of the design, but that's not something we need to have written right now?
|
||||
(it mostly relies on 'release check' 'release' 'client shutdown', and then reload and rebuild the links)
|
||||
|
||||
'client shutdown':
|
||||
this is server side code though, you tell the server 'shutdown this client'
|
||||
we don't have to exchange anything with the client while we do that
|
||||
(written initially for unload of unused modules after startup)
|
||||
|
||||
'core shutdown':
|
||||
is a shutdown of all clients
|
||||
|
||||
Reference in New Issue
Block a user