draft the creation and lifecycle facilities
This commit is contained in:
parent
78c7036678
commit
ed7f975748
3 changed files with 162 additions and 16 deletions
|
|
@ -123,15 +123,14 @@ namespace lib {
|
|||
}
|
||||
|
||||
~DelStash ()
|
||||
{
|
||||
try { killAll(); }
|
||||
|
||||
catch(...)
|
||||
{
|
||||
Symbol errID = lumiera_error();
|
||||
WARN (memory, "Problems on de-allocation: %s", errID.c());
|
||||
}
|
||||
}
|
||||
try { killAll(); }
|
||||
|
||||
catch(...)
|
||||
{
|
||||
Symbol errID = lumiera_error();
|
||||
WARN (memory, "Problems on de-allocation: %s", errID.c());
|
||||
}
|
||||
|
||||
|
||||
size_t
|
||||
size () const
|
||||
|
|
|
|||
|
|
@ -74,10 +74,10 @@ namespace lib {
|
|||
|
||||
|
||||
public:
|
||||
/** Interface to be used by SingletonFactory's clients.
|
||||
* Manages internally the instance creation, lifecycle
|
||||
* and access handling in a multithreaded context.
|
||||
* @return "the" single instance of class S
|
||||
/** Interface to be used by clients to access the service instance.
|
||||
* Manages the instance creation, lifecycle and access in multithreaded context.
|
||||
* @return instance of class SI. When used in default configuration,
|
||||
* this service instance is a singleton
|
||||
*/
|
||||
SI&
|
||||
operator() ()
|
||||
|
|
@ -94,8 +94,20 @@ namespace lib {
|
|||
}
|
||||
|
||||
|
||||
|
||||
typedef DependencyFactory::InstanceConstructor Constructor;
|
||||
|
||||
/**
|
||||
* optionally, the instance creation process can be configured
|
||||
* \em once per type. By default, a singleton instance will be created.
|
||||
* Installing another factory function enables other kinds of dependency injection;
|
||||
* this configuration must be done prior to any use the dependency factory.
|
||||
* @remark typically the \c Depend<TY> factory will be placed into a static variable,
|
||||
* embedded into some service interface type. In this case, actual storage
|
||||
* for this static variable needs to be allocated within some translation unit.
|
||||
* And this is the point where this ctor will be invoked, in the static
|
||||
* initialisation phase of the respective translation unit (*.cpp)
|
||||
*/
|
||||
Depend (Constructor ctor = buildSingleton<SI>())
|
||||
{
|
||||
factory.installConstructorFunction (ctor);
|
||||
|
|
@ -104,8 +116,20 @@ namespace lib {
|
|||
// standard copy operations applicable
|
||||
|
||||
|
||||
|
||||
/* === Management / Test support interface === */
|
||||
|
||||
/** disable and destroy the actual service instance explicitly.
|
||||
* Next access will re-invoke the factory to create a new instance.
|
||||
* @warning this is a very dangerous operation. Concurrent accesses
|
||||
* might get NULL or even a reference to the old instance,
|
||||
* which, worse still, resides typically in the same memory
|
||||
* location as the new instance. The only way to prevent this
|
||||
* would be to synchronise any \em access (which is expensive)
|
||||
* Thus it is the client's duty to ensure there is no such
|
||||
* concurrent access, i.e. all clients of the old instance
|
||||
* should be disabled prior to invoking this function.
|
||||
*/
|
||||
static void
|
||||
shutdown()
|
||||
{
|
||||
|
|
@ -115,6 +139,14 @@ namespace lib {
|
|||
instance = NULL;
|
||||
}
|
||||
|
||||
/** temporarily shadow the service instance with the given replacement.
|
||||
* The purpose of this operation is to support unit testing.
|
||||
* @throw error::State in case there is already an installed replacement
|
||||
* @param mock heap allocated instance of the replacement (mock).
|
||||
* @warning not threadsafe. Same considerations as for \c shutdown() apply
|
||||
* @note ownership of mock will be transferred; the mock instance
|
||||
* will be destroyed automatically when deconfigured.
|
||||
*/
|
||||
static void
|
||||
injectReplacement (SI* mock)
|
||||
{
|
||||
|
|
@ -135,6 +167,7 @@ namespace lib {
|
|||
};
|
||||
|
||||
|
||||
|
||||
// Storage for SingletonFactory's static fields...
|
||||
template<class SI>
|
||||
SI* volatile Depend<SI>::instance;
|
||||
|
|
|
|||
|
|
@ -29,26 +29,137 @@
|
|||
|
||||
|
||||
#include "lib/nobug-init.hpp"
|
||||
#include "lib/error.hpp"
|
||||
//#include "lib/sync-classlock.hpp"
|
||||
|
||||
#include "lib/del-stash.hpp" ////////TODO
|
||||
|
||||
namespace lib {
|
||||
|
||||
namespace error = lumiera::error;
|
||||
|
||||
class AutoDestructor
|
||||
{
|
||||
typedef void KillFun(void*);
|
||||
|
||||
DelStash destructionExecutor_;
|
||||
static bool shutdownLock;
|
||||
|
||||
static AutoDestructor&
|
||||
instance()
|
||||
{
|
||||
static AutoDestructor _instance_;
|
||||
return _instance_;
|
||||
}
|
||||
|
||||
~AutoDestructor()
|
||||
{
|
||||
shutdownLock = true;
|
||||
}
|
||||
|
||||
static void
|
||||
__lifecycleCheck()
|
||||
{
|
||||
if (shutdownLock)
|
||||
throw error::Fatal("Attempt to re-access a service, "
|
||||
"while Application is already in shutdown"
|
||||
,error::LUMIERA_ERROR_LIFECYCLE);
|
||||
}
|
||||
|
||||
public:
|
||||
static void
|
||||
schedule (void* object, KillFun* customDeleter)
|
||||
{
|
||||
__lifecycleCheck();
|
||||
instance().destructionExecutor_.manage (object, customDeleter);
|
||||
}
|
||||
|
||||
static void
|
||||
kill (void* object)
|
||||
{
|
||||
__lifecycleCheck();
|
||||
instance().destructionExecutor_.kill (object);
|
||||
}
|
||||
};
|
||||
bool AutoDestructor::shutdownLock = false;
|
||||
|
||||
|
||||
template<typename TAR>
|
||||
struct InstanceHolder
|
||||
: boost::noncopyable
|
||||
{
|
||||
|
||||
TAR*
|
||||
buildInstance ()
|
||||
{
|
||||
#if NOBUG_MODE_ALPHA
|
||||
static uint callCount = 0;
|
||||
ASSERT ( 0 == callCount++ );
|
||||
#endif
|
||||
|
||||
// place new instance into embedded buffer
|
||||
TAR* newInstance = new(buff_) TAR;
|
||||
|
||||
try
|
||||
{
|
||||
AutoDestructor::schedule (newInstance, &destroy_in_place);
|
||||
return newInstance;
|
||||
}
|
||||
|
||||
catch (std::exception& problem)
|
||||
{
|
||||
_kill_immediately (newInstance);
|
||||
throw error::State (problem, "Failed to install a deleter function "
|
||||
"for clean-up at application shutdown. ");
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
_kill_immediately (newInstance);
|
||||
throw error::State ("Unknown error while installing a deleter function.");
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
/** storage for the service instance */
|
||||
char buff_[sizeof(TAR)];
|
||||
|
||||
|
||||
static void
|
||||
destroy_in_place (void* pInstance)
|
||||
{
|
||||
if (!pInstance) return;
|
||||
static_cast<TAR*> (pInstance) -> ~TAR();
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
_kill_immediately (void* allocatedObject)
|
||||
{
|
||||
destroy_in_place (allocatedObject);
|
||||
const char* errID = lumiera_error();
|
||||
WARN (memory, "Failure in DependencyFactory. Error flag was: %s", errID);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Factory to generate and manage service objects classified by type.
|
||||
*/
|
||||
class DependencyFactory
|
||||
{
|
||||
public:
|
||||
/** invoke the installed ctor function */
|
||||
void*
|
||||
buildInstance()
|
||||
{
|
||||
UNIMPLEMENTED("invoke the ctor function");
|
||||
REQUIRE (ctorFunction_);
|
||||
return ctorFunction_();
|
||||
}
|
||||
|
||||
void
|
||||
deconfigure (void* existingInstance)
|
||||
{
|
||||
UNIMPLEMENTED("deregister and destroy a managed product");
|
||||
AutoDestructor::kill (existingInstance);
|
||||
}
|
||||
|
||||
template<class TAR>
|
||||
|
|
@ -85,7 +196,8 @@ namespace lib {
|
|||
static void*
|
||||
createSingletonInstance()
|
||||
{
|
||||
UNIMPLEMENTED("trampoline function to create a singleton");
|
||||
static InstanceHolder<TAR> storage;
|
||||
return storage.buildInstance();
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -96,6 +208,8 @@ namespace lib {
|
|||
return & createSingletonInstance<TAR>;
|
||||
}
|
||||
|
||||
private:
|
||||
InstanceConstructor ctorFunction_;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue