| Index: src/plugin/DetachedInitialization.h |
| =================================================================== |
| --- a/src/plugin/DetachedInitialization.h |
| +++ b/src/plugin/DetachedInitialization.h |
| @@ -25,27 +25,19 @@ |
| #include <condition_variable> |
| /** |
| - * A detached initializer for static class members |
| + * Initialization performed in a detached thread |
| * |
| - * Detached initializers use a separate thread to avoid delays at instantiation. |
| - * Variables statically initialized in a DLL cannot, however, contain threads. |
| - * Thus such initialization must occur in the constructor at the earliest. |
| - * |
| - * This kind of initialization acts shares much in common with a singleton. |
| - * Rather than constructing a singleton, instead we execute an initialization function. |
| + * This kind of initialization shares much in common with a singleton. |
| + * Rather than constructing a singleton, instead we execute an initialization function exactly once. |
| * The multiprocessing and synchronization issues, however, are essentially identical. |
| - * |
| - * The designated usage of this class: |
| - * 1) Define a static class member 'x' of this type with an initializer function. |
| - * 2) Call 'x.SpawnInitializer()' in the constructors to run the initializer. |
| - * 3) Call 'x.EnsureInitialized()' before relying upon any of the side effects of the initializer. |
| - * |
| * The implementation acts much like a multithreaded singleton initialization using a double-checked lock. |
| * See https://en.wikipedia.org/wiki/Double-checked_locking and http://preshing.com/20130922/acquire-and-release-fences/ |
| * The flag members of this class enable fast-return paths, that is, a return without obtaining a mutex lock. |
| * |
| - * \tparam F |
| - * The initializer function |
| + * Designated usage: |
| + * 1) Derive from this class, defining the factory Initializer(). |
| + * 2) Call 'SpawnInitializer()' in the constructors. |
| + * 3) Call 'EnsureInitialized()' before relying upon any of the side effects of the initializer. |
| * |
| * \par Serialization Guarantees |
| * The visible effect of these function returns are serialized: |
| @@ -53,7 +45,6 @@ |
| * - ThreadMain() before EnsureInitialized() |
| * - If ThreadMain() started, ThreadMain() before destructor |
| */ |
| -template<void (&F)()> |
| class DetachedInitializer |
| { |
| // "protected" declaration allows white-box subclass for testing |
| @@ -103,17 +94,32 @@ |
| { |
| return hasInitializerStarted.load(std::memory_order_acquire); |
| } |
| + |
| + /** |
| + * Type of initializer objects |
| + * |
| + * Note that the underlying function returns 'void'. |
| + * This class refuses responsibility for specific error semantics. |
| + * If the initializer thread throws, then so will EnsureInitialized(). |
| + * Anything beyond this is the responsibility of the owner. |
| + */ |
| + typedef std::function<void()> InitializerType; |
| /** |
| - * Main function for thread |
| + * Factory function for initializers |
| */ |
| - void ThreadMain() |
| + virtual InitializerType Initializer() = 0; |
| + |
| + /** |
| + * Main function for initialization thread |
| + */ |
| + void ThreadMain(InitializerType f) |
| { |
| try |
| { |
| - F(); |
| + f(); |
| } |
| - catch (std::exception& e) |
| + catch (...) |
| { |
| // Assert initializer threw an exception |
| initializerException = std::current_exception(); |
| @@ -217,7 +223,7 @@ |
| * If it does throw, the initialization-started flag won't be set. |
| * An exception here is thus a soft error, since a future invocation might succeed. |
| */ |
| - std::thread th([this]() -> void { ThreadMain(); }); |
| + std::thread th(std::bind([this](InitializerType f) -> void { ThreadMain(f); }, Initializer())); |
| // Memory fence ensures store ends before any other load or store |
| hasInitializerStarted.store(true, std::memory_order_release); |
| th.detach(); // Won't throw because the thread is both valid and joinable |
| @@ -255,4 +261,19 @@ |
| } |
| }; |
| +/** |
| + * Detached initializers use a separate thread to avoid delays at instantiation. |
| + * The constructors of variables statically initialized in a DLL cannot, however, contain threads. |
| + * Thus such initialization must (generally) occur in the constructor. |
| + */ |
| +template<void(&F)()> |
| +class DetachedInitializerStaticFunction |
| + : public DetachedInitializer |
| +{ |
| + InitializerType Initializer() override |
| + { |
| + return InitializerType([] { F(); }); |
| + } |
| +}; |
| + |
| #endif // _DETACHED_INITIALIZATION_H_ |