| OLD | NEW | 
|    1 /* |    1 /* | 
|    2  * This file is part of Adblock Plus <https://adblockplus.org/>, |    2  * This file is part of Adblock Plus <https://adblockplus.org/>, | 
|    3  * Copyright (C) 2006-2015 Eyeo GmbH |    3  * Copyright (C) 2006-2015 Eyeo GmbH | 
|    4  * |    4  * | 
|    5  * Adblock Plus is free software: you can redistribute it and/or modify |    5  * Adblock Plus is free software: you can redistribute it and/or modify | 
|    6  * it under the terms of the GNU General Public License version 3 as |    6  * it under the terms of the GNU General Public License version 3 as | 
|    7  * published by the Free Software Foundation. |    7  * published by the Free Software Foundation. | 
|    8  * |    8  * | 
|    9  * Adblock Plus is distributed in the hope that it will be useful, |    9  * Adblock Plus is distributed in the hope that it will be useful, | 
|   10  * but WITHOUT ANY WARRANTY; without even the implied warranty of |   10  * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|   11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the |   11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|   12  * GNU General Public License for more details. |   12  * GNU General Public License for more details. | 
|   13  * |   13  * | 
|   14  * You should have received a copy of the GNU General Public License |   14  * You should have received a copy of the GNU General Public License | 
|   15  * along with Adblock Plus.  If not, see <http://www.gnu.org/licenses/>. |   15  * along with Adblock Plus.  If not, see <http://www.gnu.org/licenses/>. | 
|   16  */ |   16  */ | 
|   17  |   17  | 
|   18 #ifndef _DETACHED_INITIALIZATION_H_ |   18 #ifndef _DETACHED_INITIALIZATION_H_ | 
|   19 #define _DETACHED_INITIALIZATION_H_ |   19 #define _DETACHED_INITIALIZATION_H_ | 
|   20  |   20  | 
|   21 #include <atomic> |   21 #include <atomic> | 
|   22 #include <thread> |   22 #include <thread> | 
|   23 #include <mutex> |   23 #include <mutex> | 
|   24 #include <chrono> |   24 #include <chrono> | 
|   25 #include <condition_variable> |   25 #include <condition_variable> | 
|   26  |   26  | 
|   27 /** |   27 /** | 
|   28  * A detached initializer for static class members |   28  * Initialization performed in a detached thread | 
|   29  * |   29  * | 
|   30  * Detached initializers use a separate thread to avoid delays at instantiation. |   30  * This kind of initialization shares much in common with a singleton. | 
|   31  * Variables statically initialized in a DLL cannot, however, contain threads. |   31  * Rather than constructing a singleton, instead we execute an initialization fu
     nction exactly once. | 
|   32  * Thus such initialization must occur in the constructor at the earliest. |  | 
|   33  * |  | 
|   34  * This kind of initialization acts shares much in common with a singleton. |  | 
|   35  * Rather than constructing a singleton, instead we execute an initialization fu
     nction. |  | 
|   36  * The multiprocessing and synchronization issues, however, are essentially iden
     tical. |   32  * The multiprocessing and synchronization issues, however, are essentially iden
     tical. | 
|   37  * |  | 
|   38  * The designated usage of this class: |  | 
|   39  * 1) Define a static class member 'x' of this type with an initializer function
     . |  | 
|   40  * 2) Call 'x.SpawnInitializer()' in the constructors to run the initializer. |  | 
|   41  * 3) Call 'x.EnsureInitialized()' before relying upon any of the side effects o
     f the initializer. |  | 
|   42  * |  | 
|   43  * The implementation acts much like a multithreaded singleton initialization us
     ing a double-checked lock. |   33  * The implementation acts much like a multithreaded singleton initialization us
     ing a double-checked lock. | 
|   44  * See https://en.wikipedia.org/wiki/Double-checked_locking and http://preshing.
     com/20130922/acquire-and-release-fences/ |   34  * See https://en.wikipedia.org/wiki/Double-checked_locking and http://preshing.
     com/20130922/acquire-and-release-fences/ | 
|   45  * The flag members of this class enable fast-return paths, that is, a return wi
     thout obtaining a mutex lock. |   35  * The flag members of this class enable fast-return paths, that is, a return wi
     thout obtaining a mutex lock. | 
|   46  * |   36  * | 
|   47  * \tparam F |   37  * Designated usage: | 
|   48  *   The initializer function |   38  * 1) Derive from this class, defining the factory Initializer(). | 
 |   39  * 2) Call 'SpawnInitializer()' in the constructors. | 
 |   40  * 3) Call 'EnsureInitialized()' before relying upon any of the side effects of 
     the initializer. | 
|   49  * |   41  * | 
|   50  * \par Serialization Guarantees |   42  * \par Serialization Guarantees | 
|   51  * The visible effect of these function returns are serialized:  |   43  * The visible effect of these function returns are serialized:  | 
|   52  *   - SpawnInitializer() before ThreadMain() |   44  *   - SpawnInitializer() before ThreadMain() | 
|   53  *   - ThreadMain() before EnsureInitialized() |   45  *   - ThreadMain() before EnsureInitialized() | 
|   54  *   - If ThreadMain() started, ThreadMain() before destructor |   46  *   - If ThreadMain() started, ThreadMain() before destructor | 
|   55  */ |   47  */ | 
|   56 template<void (&F)()> |  | 
|   57 class DetachedInitializer |   48 class DetachedInitializer | 
|   58 { |   49 { | 
|   59   // "protected" declaration allows white-box subclass for testing |   50   // "protected" declaration allows white-box subclass for testing | 
|   60 protected: |   51 protected: | 
|   61   /** |   52   /** | 
|   62    * Set to true after initializer thread successfully constructed; remains true
      thereafter. |   53    * Set to true after initializer thread successfully constructed; remains true
      thereafter. | 
|   63    */ |   54    */ | 
|   64   std::atomic<bool> hasInitializerStarted; |   55   std::atomic<bool> hasInitializerStarted; | 
|   65  |   56  | 
|   66   /** |   57   /** | 
| (...skipping 29 matching lines...) Expand all  Loading... | 
|   96   inline bool AlreadyInitialized() // noexcept |   87   inline bool AlreadyInitialized() // noexcept | 
|   97   { |   88   { | 
|   98     // Memory fence ensures load starts after any other load or store |   89     // Memory fence ensures load starts after any other load or store | 
|   99     return isInitializationComplete.load(std::memory_order_acquire); |   90     return isInitializationComplete.load(std::memory_order_acquire); | 
|  100   } |   91   } | 
|  101  |   92  | 
|  102   inline bool AlreadyStartedInitializer() // noexcept |   93   inline bool AlreadyStartedInitializer() // noexcept | 
|  103   { |   94   { | 
|  104     return hasInitializerStarted.load(std::memory_order_acquire); |   95     return hasInitializerStarted.load(std::memory_order_acquire); | 
|  105   } |   96   } | 
 |   97    | 
 |   98   /** | 
 |   99    * Type of initializer objects | 
 |  100    * | 
 |  101    * Note that the underlying function returns 'void'. | 
 |  102    * This class refuses responsibility for specific error semantics. | 
 |  103    * If the initializer thread throws, then so will EnsureInitialized(). | 
 |  104    * Anything beyond this is the responsibility of the owner. | 
 |  105    */ | 
 |  106   typedef std::function<void()> InitializerType; | 
|  106  |  107  | 
|  107   /** |  108   /** | 
|  108    * Main function for thread |  109    * Factory function for initializers | 
|  109    */ |  110    */ | 
|  110   void ThreadMain() |  111   virtual InitializerType Initializer() = 0; | 
 |  112  | 
 |  113   /** | 
 |  114    * Main function for initialization thread | 
 |  115    */ | 
 |  116   void ThreadMain(InitializerType f) | 
|  111   { |  117   { | 
|  112     try |  118     try | 
|  113     { |  119     { | 
|  114       F(); |  120       f(); | 
|  115     } |  121     } | 
|  116     catch (std::exception& e) |  122     catch (...) | 
|  117     { |  123     { | 
|  118       // Assert initializer threw an exception |  124       // Assert initializer threw an exception | 
|  119       initializerException = std::current_exception(); |  125       initializerException = std::current_exception(); | 
|  120     } |  126     } | 
|  121     /* |  127     /* | 
|  122      * Synchronize with reading the completion status in EnsureInitialized(). |  128      * Synchronize with reading the completion status in EnsureInitialized(). | 
|  123      * Serialize return from SpawnInitializer() and this function. |  129      * Serialize return from SpawnInitializer() and this function. | 
|  124      */ |  130      */ | 
|  125     LockGuardType lg(mutex); |  131     LockGuardType lg(mutex); | 
|  126     isInitializationComplete.store(true, std::memory_order_release); |  132     isInitializationComplete.store(true, std::memory_order_release); | 
| (...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
|  210          */ |  216          */ | 
|  211         if (AlreadyStartedInitializer()) |  217         if (AlreadyStartedInitializer()) | 
|  212         { |  218         { | 
|  213           return; |  219           return; | 
|  214         } |  220         } | 
|  215         /* |  221         /* | 
|  216          * This thread constructor is the only statement that can throw in this 
     function. |  222          * This thread constructor is the only statement that can throw in this 
     function. | 
|  217          * If it does throw, the initialization-started flag won't be set. |  223          * If it does throw, the initialization-started flag won't be set. | 
|  218          * An exception here is thus a soft error, since a future invocation mig
     ht succeed. |  224          * An exception here is thus a soft error, since a future invocation mig
     ht succeed. | 
|  219          */ |  225          */ | 
|  220         std::thread th([this]() -> void { ThreadMain(); }); |  226         std::thread th(std::bind([this](InitializerType f) -> void { ThreadMain(
     f); }, Initializer())); | 
|  221         // Memory fence ensures store ends before any other load or store |  227         // Memory fence ensures store ends before any other load or store | 
|  222         hasInitializerStarted.store(true, std::memory_order_release); |  228         hasInitializerStarted.store(true, std::memory_order_release); | 
|  223         th.detach(); // Won't throw because the thread is both valid and joinabl
     e |  229         th.detach(); // Won't throw because the thread is both valid and joinabl
     e | 
|  224         return; |  230         return; | 
|  225       } |  231       } | 
|  226       else |  232       else | 
|  227       { |  233       { | 
|  228         // Assert mutex is not locked |  234         // Assert mutex is not locked | 
|  229         if (AlreadyStartedInitializer()) |  235         if (AlreadyStartedInitializer()) | 
|  230         { |  236         { | 
| (...skipping 17 matching lines...) Expand all  Loading... | 
|  248   void EnsureInitialized() |  254   void EnsureInitialized() | 
|  249   { |  255   { | 
|  250     EnsureInitializedNoexcept(); |  256     EnsureInitializedNoexcept(); | 
|  251     if (initializerException) |  257     if (initializerException) | 
|  252     { |  258     { | 
|  253       std::rethrow_exception(initializerException); |  259       std::rethrow_exception(initializerException); | 
|  254     } |  260     } | 
|  255   } |  261   } | 
|  256 }; |  262 }; | 
|  257  |  263  | 
 |  264 /** | 
 |  265  * Detached initializers use a separate thread to avoid delays at instantiation. | 
 |  266  * The constructors of variables statically initialized in a DLL cannot, however
     , contain threads. | 
 |  267  * Thus such initialization must (generally) occur in the constructor. | 
 |  268  */ | 
 |  269 template<void(&F)()> | 
 |  270 class DetachedInitializerStaticFunction | 
 |  271   : public DetachedInitializer | 
 |  272 { | 
 |  273   InitializerType Initializer() override | 
 |  274   { | 
 |  275     return InitializerType([] { F(); }); | 
 |  276   } | 
 |  277 }; | 
 |  278  | 
|  258 #endif // _DETACHED_INITIALIZATION_H_ |  279 #endif // _DETACHED_INITIALIZATION_H_ | 
| OLD | NEW |