From ed7f975748421bf7725e371c070cc0ae222184a5 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sat, 19 Oct 2013 00:07:06 +0200 Subject: [PATCH] draft the creation and lifecycle facilities --- src/lib/del-stash.hpp | 17 +++-- src/lib/depend.hpp | 41 +++++++++-- src/lib/dependency-factory.hpp | 120 ++++++++++++++++++++++++++++++++- 3 files changed, 162 insertions(+), 16 deletions(-) diff --git a/src/lib/del-stash.hpp b/src/lib/del-stash.hpp index 92dcef26a..629b5da49 100644 --- a/src/lib/del-stash.hpp +++ b/src/lib/del-stash.hpp @@ -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 diff --git a/src/lib/depend.hpp b/src/lib/depend.hpp index 3c4ccb577..26f6b015e 100644 --- a/src/lib/depend.hpp +++ b/src/lib/depend.hpp @@ -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 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()) { 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 SI* volatile Depend::instance; diff --git a/src/lib/dependency-factory.hpp b/src/lib/dependency-factory.hpp index 1c74cb99d..74df44739 100644 --- a/src/lib/dependency-factory.hpp +++ b/src/lib/dependency-factory.hpp @@ -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 + 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 (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 @@ -85,7 +196,8 @@ namespace lib { static void* createSingletonInstance() { - UNIMPLEMENTED("trampoline function to create a singleton"); + static InstanceHolder storage; + return storage.buildInstance(); } @@ -96,6 +208,8 @@ namespace lib { return & createSingletonInstance; } + private: + InstanceConstructor ctorFunction_; };