// bslmt_once.h -*-C++-*- // ---------------------------------------------------------------------------- // NOTICE // // This component is not up to date with current BDE coding standards, and // should not be used as an example for new development. // ---------------------------------------------------------------------------- #ifndef INCLUDED_BSLMT_ONCE #define INCLUDED_BSLMT_ONCE #include <bsls_ident.h> BSLS_IDENT("$Id: $") //@PURPOSE: Provide a thread-safe way to execute code once per process. // //@CLASSES: // bslmt::Once: Gate-keeper for code executed only once per process // bslmt::OnceGuard: Guard class for safely using 'bslmt::Once' // //@SEE_ALSO: bslmt_qlock // //@DESCRIPTION: This component provides a pair of classes, 'bslmt::Once' and // 'bslmt::OnceGuard', which give the caller a way to run a body of code // exactly once within the current process, particularly in the presence of // multiple threads. This component also defines the macro 'BSLMT_ONCE_DO', // which provides syntactic sugar to make one-time execution nearly fool-proof. // A common use of one-time execution is the initialization of singletons on // first use. // // The 'bslmt::Once' class is designed to be statically allocated and // initialized using the 'BSLMT_ONCE_INITIALIZER' macro. Client code may use // the 'bslmt::Once' object in one of two ways: 1) it may use the 'callOnce' // method to call a function or functor or 2) it may call the 'enter' and // 'leave' methods just before and after the code that is intended to be // executed only once. That code must be executed conditionally on 'enter' // returning 'true', indicating that the caller is the first thread to pass // through this region of code. The 'leave' method must be executed at the end // of the code region, indicating that the one-time execution has completed and // unblocking any threads waiting on 'enter'. // // A safer way to use the 'enter' and 'leave' methods of 'bslmt::Once' is to // manage the 'bslmt::Once' object using a 'bslmt::OnceGuard' object // constructed from the 'bslmt::Once' object. Calling 'enter' on the // 'bslmt::OnceGuard' object will call 'enter' on its associated 'bslmt::Once' // object. If the call to 'enter' returns 'true', then the destructor for the // guard will automatically call 'leave' on its associated 'bslmt::Once' // object. The 'bslmt::OnceGuard' class is intended to be allocated on the // stack (i.e., as a local variable) so that it is automatically destroyed at // the end of its enclosing block. Thus, the to call 'leave' of the // 'bslmt::Once' object is enforced by the compiler. // // An even easier way to use the facilities of this component is to use the // 'BSLMT_ONCE_DO' macro. This macro behaves like an 'if' statement -- // executing the following [compound] statement the first time the control // passes through it in the course of a program's execution, and blocking other // calling threads until the [compound] statement is executed the first time. // Thus, bracketing arbitrary code in a 'BSLMT_ONCE_DO' construct is the // easiest way to ensure that code will be executed only once for a program. // The 'BSLMT_ONCE_DO' behaves correctly even if there are 'return' statements // within the one-time code block. // // The implementation of this component uses appropriate memory barriers so // that changes made in the one-time execution code are immediately visible to // all threads at the end of the one-time code block. // ///Warning ///------- // The 'BSLMT_ONCE_DO' macro consists of a declaration and a 'for' loop. // Consequently, the following is syntactically incorrect: //.. // if (xyz) BSLMT_ONCE_DO { stuff() } //.. // Also, a 'break' or 'continue' statement within a 'BSLMT_ONCE_DO' construct // terminates the 'BSLMT_ONCE_DO', not a surrounding loop or 'switch' // statement. For example: //.. // switch (xyz) { // case 0: BSLMT_ONCE_DO { stuff(); break; /* does not break case */ } // case 1: // Oops! case 0 falls through to here. // } //.. // ///Thread Safety ///------------- // Objects of the 'bslmt::Once' class are intended to be shared among threads // and may be accessed and modified simultaneously in multiple threads by using // the methods provided. To allow static initialization, 'bslmt::Once' is a // POD type with public member variables. It is not safe to directly access or // manipulate its member variables (including object initialization) // simultaneously from multiple threads. (Note that static initialization // takes place before multiple threading begins, and is thus safe.) // // The 'bslmt::OnceGuard' objects are designed to be used only by their creator // threads and are typically created on the stack. It is not safe to use a // 'bslmt::OnceGuard' by a thread other than its creator. // ///Usage ///----- // Typically, the facilities in this component are used to implement a // thread-safe singleton. Below, we implement the a singleton four ways, // illustrating the two ways to directly use 'bslmt::Once', the use of // 'bslmt::OnceGuard', and the use of 'BSLMT_ONCE_DO'. In each example, the // singleton functions take a C-string ('const char*') argument and return a // reference to a 'bsl::string' object constructed from the input string. Only // the first call to each singleton function affect the contents of the // singleton string. (The argument is ignored on subsequent calls.) // ///First Implementation /// - - - - - - - - - - // Our first implementation uses the 'BSLMT_ONCE_DO' construct, the // recommended way to use this component. The function is a variation of the // singleton pattern described by Scott Meyers, except that the 'BSLMT_ONCE_DO' // macro is used to handle multiple entries to the function in a thread-safe // manner: //.. // const bsl::string& singleton0(const char *s) // { // static bsl::string *theSingletonPtr = 0; // BSLMT_ONCE_DO { // static bsl::string theSingleton(s, // bslma::Default::globalAllocator()); // theSingletonPtr = &theSingleton; // } // return *theSingletonPtr; // } //.. // The 'BSLMT_ONCE_DO' mechanism suffices for most situations; however, if more // flexibility is required, review the remaining examples in this series for // more design choices. The next example will use the lowest-level facilities // of 'bslmt::Once'. The two following examples use progressively higher-level // facilities to produce simpler singleton implementations (though none as // simple as the 'BSLMT_ONCE_DO' example above). // ///Second Implementation ///- - - - - - - - - - - // The next singleton function implementation directly uses the 'doOnce' method // of 'bslmt::Once'. We begin by declaring a functor type that does most of // the work of the singleton, i.e., constructing the string and setting a // (static) pointer to the string: //.. // static bsl::string *theSingletonPtr = 0; // // class SingletonInitializer { // const char *d_initialValue; // // public: // SingletonInitializer(const char *initialValue) // : d_initialValue(initialValue) // { // } // // void operator()() const { // static bsl::string theSingleton(d_initialValue); // theSingletonPtr = &theSingleton; // } // }; //.. // The function call operator of the type above is *not* thread-safe. Firstly, // many threads might attempt to simultaneously construct the 'theSingleton' // object. Secondly, once 'theSingletonPtr' is set by one thread, other // threads still might not see the change (and try to initialize the singleton // again). // // The 'singleton1' function, below, invokes 'SingletonInitializer::operator()' // via the 'callOnce' method of 'bslmt::Once' to ensure that 'operator()' is // called by only one thread and that the result is visible to all threads. We // start by creating and initializing a static object of type 'bslmt::Once': //.. // const bsl::string& singleton1(const char *s) // { // static bslmt::Once once = BSLMT_ONCE_INITIALIZER; //.. // We construct a 'SingletonInitializer' instance, effectively "binding" the // argument 's' so that it may be used in a function invoked by 'callOnce', // which takes only a no-argument functor (or function). The first thread (and // only the first thread) entering this section of code will set 'theSingleton'. //.. // once.callOnce(SingletonInitializer(s)); // return *theSingletonPtr; // } //.. // Once we return from 'callOnce', the appropriate memory barrier has been // executed so that the change to 'theSingletonPtr' is visible to all threads. // A thread calling 'callOnce' after the initialization has completed would // immediately return from the call. A thread calling 'callOnce' while // initialization is still in progress would block until initialization // completes and then return. // // *Implementation* *Note*: As an optimization, developers sometimes pre-check // the value to be set, 'theSingletonPtr' in this case, to avoid (heavy) memory // barrier operations; however, that practice is not recommended here. First, // the value of the string may be cached by a different CPU, even though the // pointer has already been updated on the common memory bus. Second, The // implementation of the 'callOnce' method is fast enough that a pre-check // would not provide any performance benefit. // // The one advantage of this implementation over the previous one is that an // exception thrown from within 'singletonImp' will cause the 'bslmt::Once' // object to be restored to its original state, so that the next entry into the // singleton will retry the operation. // ///Third Implementation /// - - - - - - - - - - // Our next implementation, 'singleton2', eliminates the need for the // 'singletonImp' function and thereby does away with the use of the // a functor type to "bind" the initialization parameter; however, it does // require use of 'bslmt::Once::OnceLock', created on each thread's stack and // passed to the methods of 'bslmt::Once'. First, we declare a static // 'bslmt::Once' object as before, and also declare a static pointer to // 'bsl::string': //.. // const bsl::string& singleton2(const char *s) // { // static bslmt::Once once = BSLMT_ONCE_INITIALIZER; // static bsl::string *theSingletonPtr = 0; //.. // Next, we define a local 'bslmt::Once::OnceLock' object and pass it to the // 'enter' method: //.. // bslmt::Once::OnceLock onceLock; // if (once.enter(&onceLock)) { //.. // If the 'enter' method returns 'true', we proceed with the initialization of // the singleton, as before. //.. // static bsl::string theSingleton(s); // theSingletonPtr = &theSingleton; //.. // When initialization is complete, the 'leave' method is called for the same // context cookie previously used in the call to 'enter': //.. // once.leave(&onceLock); // } //.. // When any thread reaches this point, initialization has been complete and // initialized string is returned: //.. // return *theSingletonPtr; // } //.. // ///Fourth Implementation ///- - - - - - - - - - - // Our final implementation, 'singleton3', uses 'bslmt::OnceGuard' to simplify // the previous implementation by using 'bslmt::OnceGuard' to hide (automate) // the use of 'bslmt::Once::OnceLock'. We begin as before, defining a static // 'bslmt::Once' object and a static 'bsl::string' pointer: //.. // const bsl::string& singleton3(const char *s) // { // static bslmt::Once once = BSLMT_ONCE_INITIALIZER; // static bsl::string *theSingletonPtr = 0; //.. // We then declare a local 'bslmt::OnceGuard' object and associate it with the // 'bslmt::Once' object before entering the one-time initialization region: //.. // bslmt::OnceGuard onceGuard(&once); // if (onceGuard.enter()) { // static bsl::string theSingleton(s); // theSingletonPtr = &theSingleton; // } // return *theSingletonPtr; // } //.. // Note that it is unnecessary to call 'onceGuard.leave()' because that is // called automatically before the function returns. This machinery makes the // code more robust in the presence of, e.g., return statements in the // initialization code. // // If there is significant code after the end of the one-time initialization, // the guard and the initialization code should be enclosed in an extra block // so that the guard is destroyed as soon as validly possible and allow other // threads waiting on the initialization to continue. Alternatively, one can // call 'onceGuard.leave()' explicitly at the end of the initialization. // ///Using the Semaphore Implementations ///- - - - - - - - - - - - - - - - - - // The following pair of functions, 'thread1func' and 'thread2func' which will // be run by different threads: //.. // void *thread1func(void *) // { // const bsl::string& s0 = singleton0("0 hello"); // const bsl::string& s1 = singleton1("1 hello"); // const bsl::string& s2 = singleton2("2 hello"); // const bsl::string& s3 = singleton3("3 hello"); // // assert('0' == s0[0]); // assert('1' == s1[0]); // assert('2' == s2[0]); // assert('3' == s3[0]); // // // ... lots more code goes here // return 0; // } // // void *thread2func(void *) // { // const bsl::string& s0 = singleton0("0 goodbye"); // const bsl::string& s1 = singleton1("1 goodbye"); // const bsl::string& s2 = singleton2("2 goodbye"); // const bsl::string& s3 = singleton3("3 goodbye"); // // assert('0' == s0[0]); // assert('1' == s1[0]); // assert('2' == s2[0]); // assert('3' == s3[0]); // // // ... lots more code goes here // return 0; // } //.. // Both threads attempt to initialize the four singletons. In our example, // each thread passes a distinct argument to the singleton, allowing us to // identify the thread that initializes the singleton. (In practice, the // arguments passed to a specific singleton are almost always fixed and most // singletons don't take arguments at all.) // // Assuming that the first thread function wins all of the races to initialize // the singletons, the first singleton is set to "0 hello", the second // singleton to "1 hello", etc. //.. // int usageExample1() // { // void startThread1(); // void startThread2(); // // startThread1(); // startThread2(); // // assert(singleton0("0") == "0 hello"); // assert(singleton1("1") == "1 hello"); // assert(singleton2("2") == "2 hello"); // assert(singleton3("3") == "3 hello"); // // return 0; // } //.. #include <bslscm_version.h> #include <bslmt_qlock.h> #include <bsls_atomicoperations.h> #include <bsls_assert.h> #include <bsls_buildtarget.h> #include <bsls_platform.h> namespace BloombergLP { #if defined(BSLS_PLATFORM_CMP_MSVC) # define BSLMT_ONCE_UNIQNUM __COUNTER__ // MSVC: The '__LINE__' macro breaks when '/ZI' is used (see Q199057 or // KB199057). Fortunately the '__COUNTER__' extension provided by MSVC // is even better. #else # define BSLMT_ONCE_UNIQNUM __LINE__ #endif #define BSLMT_ONCE_DO \ BSLMT_ONCE_DO_IMP(BSLMT_ONCE_CAT(bslmt_doOnceObj, BSLMT_ONCE_UNIQNUM)) // This macro provides a simple control construct to bracket a piece of // code that should only be executed once during the course of a // multithreaded program. Usage: //.. // BSLMT_ONCE_DO { /* one-time code goes here */ } //.. // Leaving a 'BSLMT_ONCE_DO' construct via 'break', 'continue', or 'return' // will put the construct in a "done" state (unless 'BSLMT_ONCE_CANCEL' has // been called) and will unblock all threads waiting to enter the one-time // region. Note that a 'break' or 'continue' within the one-time code will // terminate only the 'BSLMT_ONCE_DO' construct, not any surrounding loop // or switch statement. Due to a bug in the Microsoft Visual C++ 2003 // compiler, the behavior is undefined if an exception is thrown from // within this construct and is not caught within the same construct. Only // one call to 'BSLMT_ONCE_DO' may appear on a single source-code line in // any code block. #define BSLMT_ONCE_CANCEL() bslmt_doOnceGuard.cancel() // This macro provides a way to cancel once processing within a // 'BSLMT_ONCE_DO' construct. It will not compile outside of a // 'BSLMT_ONCE_DO' construct. Executing this function-like macro will set // the state of the 'BSLMT_ONCE_DO' construct to "not entered", possibly // unblocking a thread waiting to enter the one-time code region. Note // that this macro does not exit the 'BSLMT_ONCE_DO' construct (i.e., it // does not have 'break' or 'return' semantics). #define BSLMT_ONCE_INITIALIZER { BSLMT_QLOCK_INITIALIZER, { 0 } } namespace bslmt { // Use this macro to initialize an object of type 'Once'. E.g.: //.. // Once once = BSLMT_ONCE_INITIALIZER; //.. // ========== // class Once // ========== class Once { // Gate-keeper class for code that should only execute once per process. // This class is a POD-type and can be statically initialized to the value // of the 'BSLMT_ONCE_INITIALIZE' macro. For this reason, it does not have // any explicitly-declared constructors or destructor. // PRIVATE TYPES enum { e_NOT_ENTERED, e_IN_PROGRESS, e_DONE }; private: // NOT IMPLEMENTED Once& operator=(const Once&); // Copy-assignment is not allowed. We cannot declare a private copy // constructor because that would make this class a non-POD. public: // These variables are public so that this (POD) type can be statically // initialized. Do not access these variables directly. // DATA QLock d_mutex; // public, but do *not* access directly bsls::AtomicOperations::AtomicTypes::Int d_state; // public, but do *not* access directly public: // PUBLIC TYPES typedef QLockGuard OnceLock; // Special token created by a single thread to pass to the 'enter', // 'leave', and 'cancel' methods. // MANIPULATORS bool enter(OnceLock *onceLock); // Lock the internal mutex using the specified 'onceLock' (possibly // blocking if another thread has already locked the mutex). If no // other thread has yet called 'enter' or 'callOnce' on this object, // return 'true'. Otherwise, unlock the mutex and return 'false'. The // mutex lock may be skipped if it can be determined that it will not // be needed. The behavior is undefined if 'onceLock' is already in a // locked state on entry to this method. Note that if 'enter' returns // 'true', the caller *must* eventually call 'leave', or else other // threads may block indefinitely. void leave(OnceLock *onceLock); // Set this object into a state such that pending and future calls to // 'enter' or 'callOnce' will return 'false' or do nothing, // respectively, then unlock the internal mutex using the specified // 'onceLock' (possibly unblocking pending calls to 'enter' or // 'callOnce'). The behavior is undefined unless 'onceLock' was locked // by a matching call to 'enter' on this object and has not been // tampered-with since. void cancel(OnceLock *onceLock); // Revert this object to the state it was in before 'enter' or // 'callOnce' was called, then unlock the internal mutex using the // specified 'onceLock' (possibly unblocking pending calls to 'enter' // or 'callOnce'). This method may only be used to cancel execution of // one-time code that has not yet completed. The behavior is undefined // unless 'onceLock' was locked by a matching call to 'enter' on this // object and has not been tampered-with since (especially by calling // 'leave'). template <class FUNC> void callOnce(FUNC& function); template <class FUNC> void callOnce(const FUNC& function); // If no other thread has yet called 'enter' or 'callOnce', then call // the specified 'function' and set this object to the state where // pending and future calls to 'enter' or 'callOnce' will return // 'false' or do nothing, respectively. Otherwise, wait for the // one-time code to complete and return without calling 'function' // where 'function' is a function or functor that can be called with no // arguments. Note that one-time code is considered not to have run if // 'function' terminates with an exception. }; // =============== // class OnceGuard // =============== class OnceGuard { // Guard class for using 'Once' safely. Construct an object of this class // before conditionally entering one-time processing code. Destroy the // object when the one-time code is complete. When used this way, this // object will be in an "in-progress" state during the time that the // one-time code is being executed. // PRIVATE TYPES enum State { e_NOT_ENTERED, e_IN_PROGRESS, e_DONE }; // DATA Once::OnceLock d_onceLock; Once *d_once; State d_state; // NOT IMPLEMENTED OnceGuard(const OnceGuard&); OnceGuard& operator=(const OnceGuard&); public: // CREATORS explicit OnceGuard(Once *once = 0); // Initialize this object to guard the (optionally) specified 'once' // object. If 'once' is not specified, then it must be set later using // the 'setOnce' method before other methods may be called. ~OnceGuard(); // Destroy this object. If this object is not in an "in-progress" // state, do nothing. If this object is in an "in-progress" state and // is being destroyed in the course of normal processing, then call // 'leave' on the associated 'Once' object. Due to a bug in the MS // VC++ 2003 compiler, the behavior is undefined if this destructor is // called in the course of stack-unwinding during exception processing // (i.e., if an exception escapes from the one-time code region. // [Eventually, we hope to call 'cancel' if this destructor is called // during exception-processing.] // MANIPULATORS void setOnce(Once *once); // Set this object to guard the specified 'once' object. The behavior // is undefined if this object is currently in the "in-progress" state. bool enter(); // Call 'enter' on the associated 'Once' object and return the result. // If 'Once::enter' returns 'true', set this object into the // "in-progress" state. The behavior is undefined unless this object // has been associated with a 'Once' object, either in the constructor // or using 'setOnce', or if this object is already in the // "in-progress" state. void leave(); // If this object is in the "in-progress" state, call 'leave' on the // associated 'Once' object and exit the "in-progress" state. // Otherwise, do nothing. void cancel(); // If this object is in the "in-progress" state, call 'cancel' on the // associated 'Once' object and exit the "in-progress" state. // Otherwise, do nothing. // ACCESSORS bool isInProgress() const; // Return 'true' if this object is in the "in-progress" state. The // object is in-progress if 'enter' has been called and returned 'true' // and neither 'leave' nor 'cancel' have been called. The one-time // code controlled by this object should only be executing if this // object is in the "in-progress" state. }; } // close package namespace // ============================================================================ // INLINE DEFINITIONS // ============================================================================ // ---------------------------------- // Token concatenation support macros // ---------------------------------- // Second layer needed to ensure that arguments are expanded before // concatenation. #define BSLMT_ONCE_CAT(X, Y) BSLMT_ONCE_CAT_IMP(X, Y) #define BSLMT_ONCE_CAT_IMP(X, Y) X##Y // ------------------------------------- // Implementation of BSLMT_ONCE_DO Macro // ------------------------------------- // Use a for-loop to initialize the guard, test if we can enter the // once-region, then leave the once-region at the end. Each invocation of this // macro within a source file supplies a different 'doOnceObj' name. #define BSLMT_ONCE_DO_IMP(doOnceObj) \ static BloombergLP::bslmt::Once doOnceObj = BSLMT_ONCE_INITIALIZER; \ for (BloombergLP::bslmt::OnceGuard /* NOLINT */\ bslmt_doOnceGuard(&doOnceObj);\ bslmt_doOnceGuard.enter(); bslmt_doOnceGuard.leave()) // --------------- // class OnceGuard // --------------- // CREATORS inline bslmt::OnceGuard::OnceGuard(Once *once) : d_once(once) , d_state(e_NOT_ENTERED) { } // MANIPULATORS inline void bslmt::OnceGuard::setOnce(Once *once) { BSLS_ASSERT_SAFE(e_IN_PROGRESS != d_state); d_once = once; d_state = e_NOT_ENTERED; } // ACCESSORS inline bool bslmt::OnceGuard::isInProgress() const { return e_IN_PROGRESS == d_state; } // ---------- // class Once // ---------- template <class FUNC> inline void bslmt::Once::callOnce(FUNC& function) { OnceGuard guard(this); if (guard.enter()) { #ifdef BDE_BUILD_TARGET_EXC try { function(); } catch (...) { guard.cancel(); throw; } #else function(); #endif } } template <class FUNC> inline void bslmt::Once::callOnce(const FUNC& function) { OnceGuard guard(this); if (guard.enter()) { #ifdef BDE_BUILD_TARGET_EXC try { function(); } catch (...) { guard.cancel(); throw; } #else function(); #endif } } } // close enterprise namespace #if !defined(BSL_DOUBLE_UNDERSCORE_XLAT) || 1 == BSL_DOUBLE_UNDERSCORE_XLAT #define BSLMT_ONCE__CAT(X, Y) BSLMT_ONCE_CAT(X, Y) #endif #endif // ---------------------------------------------------------------------------- // Copyright 2015 Bloomberg Finance L.P. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // ----------------------------- END-OF-FILE ----------------------------------