// bsls_bslonce.h -*-C++-*- #ifndef INCLUDED_BSLS_BSLONCE #define INCLUDED_BSLS_BSLONCE #include <bsls_ident.h> BSLS_IDENT("$Id: $") //@PURPOSE: Provide BSL a thread-safe way to execute code once per process. // //@CLASSES: // bsls::BslOnce: statically initializable gate-keeper for a once-block // bsls::BslOnceGuard: guard for safely using 'bsls::BslOnce' // //@SEE_ALSO: // //@DESCRIPTION: This component provides a pair of classes, 'bsls::BslOnce' // and 'bsls::BslOnceGuard', which give the caller a way to run a block of // code exactly once within the current process, particularly in the presence // of multiple threads. The typical purpose of this one-time execution is the // initialization of a singleton on first use. // // [!WARNING!] Clients outside of 'bsl' should *not* use this component. // Because of its location in the hierarchy, this component guards critical // sections using a spin-lock. Equivalent components that are more robust and // efficient will be provided at a higher level (see 'bcemt_once'). // // A 'bsls::BslOnce' object can be statically initialized using the // 'BSLS_BSLONCE_INITIALIZER' macro. // ///Usage ///----- // This section illustrates intended use of this component. // ///Example 1: Using 'bsls::BslOnce' to Perform a Singleton Initialization /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // The following example demonstrates using 'bsls::BslOnce' to initialize a // singleton object. // // First we declare a 'struct', 'MySingleton', whose definition is elided: //.. // struct MySingleton { // // // PUBLIC DATA // int d_exampleData; // // // ... // }; //.. // Notice that the data members are public because we want to avoid dynamic // runtime initialize (i.e., initialization at run-time before the start of // 'main') when an object of this type is declared in a static context. // // Now we implement a function 'getSingleton' that returns a singleton object. // 'getSingleton' uses 'BslOnce' to ensure the singleton is initialized only // once, and that the singleton is initialized before the function returns: //.. // MySingleton *getSingleton() // // Return a reference to a modifiable singleton object. // { // static MySingleton singleton = { 0 }; // static BslOnce once = BSLS_BSLONCE_INITIALIZER; // // BslOnceGuard onceGuard; // if (onceGuard.enter(&once)) { // // Initialize 'singleton'. Note that this code is executed exactly // // once. // // } // return &singleton; // } //.. // Notice that 'BslOnce' must be initialized to 'BSLS_BSLONCE_INITIALIZER', and // that 'singleton' is a function scoped static variable to avoid allocating // it on the 'heap' (which might be reported as leaked memory). #include <bsls_atomicoperations.h> #ifdef BDE_BUILD_TARGET_SAFE // This component needs to be below bsls_assert in the physical hierarchy, so // 'BSLS_ASSERT' macros can't be used here. To workaround this issue, we use // the C 'assert' instead. #include <assert.h> #define BSLS_BSLONCE_ASSERT_SAFE(x) assert((x)) #else #define BSLS_BSLONCE_ASSERT_SAFE(x) #endif namespace BloombergLP { namespace bsls { // ============= // class BslOnce // ============= #define BSLS_BSLONCE_INITIALIZER { { 0xdead } } // Use this macro to initialize an object of type 'bsls::Once'. E.g.: //.. // bsls::Once once = BSLS_BSLONCE_INITIALIZER; //.. // Note that we use an unlikely arbitrary value to permit effectively // asserting a 'BslOnce' for correct initialization. struct BslOnce { // This 'struct' provides a simple data type for ensuring a block of code // is executed (only) once. Note that this is defined as a 'struct' to // allow constant initialization in a global or static context using // 'BSLS_BSLONCE_INITIALIZER'. public: // PUBLIC DATA bsls::AtomicOperations::AtomicTypes::Int d_onceState; // The state of the one-time block of code managed // by this object (must be one of the 'State' // values). This value is public to allow static // initialization (with 'BSLS_BSLONCE_INITIALIZER'), // but should never be directly accessed or // modified. private: // PRIVATE TYPES enum State { // Note that we select unusual integer values in order to more // effectively test (in appropriate build modes) that 'd_onceState' // was correctly initialized. e_NOT_ENTERED = 0xdead, e_IN_PROGRESS, e_DONE }; // PRIVATE MANIPULATORS bool doEnter(); // Enter the one-time block of code. Return 'true' if the one-time // block of code has been entered, and 'false' if the one-time block of // code has already been executed. If this function returns 'false' // then the thread of execution in which 'enter' returned 'true' has // already called 'leave' -- i.e., the one-time block of code is // guaranteed to have *completed* execution. The behavior is undefined // unless this object was originally initialized to // 'BSLS_BSLONCE_INITIALIZER'. Note that this private variant of // 'enter' does not perform a test before attempting to acquire the // spin-lock, and is meant to be implemented out of line (so that the // expected path of 'enter' may be more easily inlined). public: // MANIPULATORS bool enter(); // Enter the one-time block of code. Return 'true' if the one-time // block of code has been entered, and 'false' if the one-time block of // code has already been executed. If this function returns 'false' // then the thread of execution in which 'enter' returned 'true' has // already called 'leave' -- i.e., the one-time block of code is // guaranteed to have *completed* execution. The behavior is undefined // unless this object was originally initialized to // 'BSLS_BSLONCE_INITIALIZER'. Note that a successful 'enter' locks a // spin-lock; it is imperative that 'leave' be called quickly. void leave(); // Exit the one-time block of code. The behavior is undefined unless // the caller had previously called 'enter', and 'enter' had returned // 'true'. }; // ================== // class BslOnceGuard // ================== class BslOnceGuard { // This class provides a guard for managing a 'BslOnce' for the purpose of // executing a block of code (only) once. private: // DATA BslOnce *d_once; // once gate-keeper // NOT IMPLEMENTED BslOnceGuard(const BslOnceGuard&); BslOnceGuard& operator=(const BslOnceGuard&); public: // CREATORS BslOnceGuard(); // Create a guard to manage a block of code that is executed once. ~BslOnceGuard(); // Destroy this guard, and if 'enter' had been called on this object // without a subsequent call to 'leave', then call 'leave' to signal // the completion of the one-time block of code. // MANIPULATORS bool enter(BslOnce *once); // Enter the one-time block of code that is managed by the specified // 'once'. Return 'true' if the one-time block of code has been // entered, and 'false' if the one-time block of code has already been // executed. If this function returns 'false' then the thread of // execution in which 'enter' returned 'true' has already called // 'leave' -- i.e., the one-time block of code is guaranteed to have // *completed* execution. The behavior is undefined unless 'once' was // originally initialized to 'BSLS_BSLONCE_INITIALIZER'. Note that a // successful 'enter' locks a spin-lock; it is imperative that 'leave' // be called quickly. void leave(); // Exit the one-time block of code. The behavior is undefined unless // the caller had previously called 'enter', and 'enter' had returned // 'true'. }; // ============================================================================ // INLINE DEFINITIONS // ============================================================================ // ------------- // class BslOnce // ------------- // MANIPULATORS inline bool BslOnce::enter() { if (e_DONE == bsls::AtomicOperations::getIntAcquire(&d_onceState)) { return false; // RETURN } return doEnter(); }; inline void BslOnce::leave() { BSLS_BSLONCE_ASSERT_SAFE(e_IN_PROGRESS == bsls::AtomicOperations::getIntRelaxed(&d_onceState)); bsls::AtomicOperations::setIntRelease(&d_onceState, e_DONE); } // ================== // class BslOnceGuard // ================== // CREATORS inline BslOnceGuard::BslOnceGuard() : d_once(0) { } inline BslOnceGuard::~BslOnceGuard() { if (d_once) { d_once->leave(); } } // MANIPULATORS inline bool BslOnceGuard::enter(BslOnce *once) { BSLS_BSLONCE_ASSERT_SAFE(once); BSLS_BSLONCE_ASSERT_SAFE(!d_once); bool success = once->enter(); // If the block guarded by 'once' has successfully been entered, set // 'd_once' so that 'leave' will be called when this guard is destroyed. if (success) { d_once = once; } return success; } inline void BslOnceGuard::leave() { BSLS_BSLONCE_ASSERT_SAFE(d_once); d_once->leave(); d_once = 0; } } // close package namespace } // close enterprise namespace #endif // ---------------------------------------------------------------------------- // Copyright 2014 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 ----------------------------------