// 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`
//
//@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 `bslmt_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:
// ```
// /// Return a reference to a modifiable singleton object.
// MySingleton *getSingleton()
// {
//    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
                        // =============

/// 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.
#define BSLS_BSLONCE_INITIALIZER { { 0xdead } }


 // 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`.
struct BslOnce {

  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

    /// 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).
    bool doEnter();

  public:
    // MANIPULATORS

    /// 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.
    bool enter();

    /// Exit the one-time block of code.  The behavior is undefined unless
    /// the caller had previously called `enter`, and `enter` had returned
    /// `true`.
    void leave();
};


                        // ==================
                        // class BslOnceGuard
                        // ==================

/// This class provides a guard for managing a `BslOnce` for the purpose of
/// executing a block of code (only) once.
class BslOnceGuard {

  private:

     // DATA
    BslOnce *d_once;  // once gate-keeper

    // NOT IMPLEMENTED
    BslOnceGuard(const BslOnceGuard&);
    BslOnceGuard& operator=(const BslOnceGuard&);


  public:
    // CREATORS

    /// 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.
    ~BslOnceGuard();

    // MANIPULATORS

    /// 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.
    bool enter(BslOnce *once);

    /// Exit the one-time block of code.  The behavior is undefined unless
    /// the caller had previously called `enter`, and `enter` had returned
    /// `true`.
    void leave();
};


// ============================================================================
//                         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 ----------------------------------
