// Copyright 2008 Google Inc. All Rights Reserved.
//
// The first call to GoogleOnceInit() with a particular GoogleOnceType
// argument will run the specified function. Other calls with the same
// argument will not run the function, but will wait for the provided
// function to finish running (if it is still running). This provides
// a safe, simple, and fast mechanism for one-time initialization in a
// multi-threaded process.
//
// This module is a replacement for pthread_once().  It was added
// since some versions of pthread_once() call the supplied function
//
// Example usage:
//   static GoogleOnceType once = GOOGLE_ONCE_INIT;
//   static void Initializer() {
//     ... do initialization ...
//   }
//   ...
//   void SomeFunction() {
//     GoogleOnceInit(&once, &Initializer);
//     ...
//   }

#ifndef BASE_ONCE_H_
#define BASE_ONCE_H_

#include "gutil/atomicops.h"
#include "gutil/integral_types.h"
#include "gutil/dynamic_annotations.h"
#include "gutil/macros.h"
#include "gutil/port.h"
#include "gutil/type_traits.h"

// The following enum values are not for use by clients
enum {
  GOOGLE_ONCE_INTERNAL_INIT  = 0,
  GOOGLE_ONCE_INTERNAL_RUNNING = 0x65C2937B,  // an improbable 32-bit value
  GOOGLE_ONCE_INTERNAL_WAITER = 0x05A308D2,  // a different improbable value
  GOOGLE_ONCE_INTERNAL_DONE = 0x3F2D8AB0,  // yet another improbable value
};

struct GoogleOnceType {
  Atomic32 state;
};

#define GOOGLE_ONCE_INIT { GOOGLE_ONCE_INTERNAL_INIT }

// For internal use only.
extern void GoogleOnceInternalInit(Atomic32* state, void (*func)(),
                                   void (*func_with_arg)(void*), void* arg);

inline void GoogleOnceInit(GoogleOnceType* state, void (*func)()) {
  Atomic32 s = Acquire_Load(&state->state);
  if (PREDICT_FALSE(s != GOOGLE_ONCE_INTERNAL_DONE)) {
    GoogleOnceInternalInit(&state->state, func, 0, 0);
  }
  ANNOTATE_HAPPENS_AFTER(&state->state);
}

// A version of GoogleOnceInit where the function argument takes a pointer
// of arbitrary type.
template<typename T>
inline void GoogleOnceInitArg(GoogleOnceType* state,
                              void (*func_with_arg)(T*), T* arg) {
  Atomic32 s = Acquire_Load(&state->state);
  if (PREDICT_FALSE(s != GOOGLE_ONCE_INTERNAL_DONE)) {
    // Deal with const T as well as non-const T.
    typedef typename base::remove_const<T>::type mutable_T;
    GoogleOnceInternalInit(&state->state, 0,
                           reinterpret_cast<void(*)(void*)>(func_with_arg),
                           const_cast<mutable_T*>(arg));
  }
  ANNOTATE_HAPPENS_AFTER(&state->state);
}

// GoogleOnceDynamic is like GoogleOnceType, but is dynamically
// initialized instead of statically initialized.  This should be used only
// when the variable is not of static storage class.
// It might be used to delay expensive initialization of part of a
// dynamically-allocated data structure until it is known to be needed.  For
// example:
//   class MyType {
//     GoogleOnceDynamic once_;
//     ComplexStuff* complex_stuff_;
//     static void InitComplexStuff(MyType* me) {
//       me->complex_stuff_ = ...;
//     }
//    public:
//     ComplexStuff* complex_stuff() {
//       this->once_.Init(&InitComplexStuff, this);
//       return this->complex_stuff_;
//     }
//   }
class GoogleOnceDynamic {
 public:
  GoogleOnceDynamic() : state_(GOOGLE_ONCE_INTERNAL_INIT) { }

  // If this->Init() has not been called before by any thread,
  // execute (*func_with_arg)(arg) then return.
  // Otherwise, wait until that prior invocation has finished
  // executing its function, then return.
  template<typename T>
  void Init(void (*func_with_arg)(T*), T* arg) {
    Atomic32 s = Acquire_Load(&this->state_);
    if (PREDICT_FALSE(s != GOOGLE_ONCE_INTERNAL_DONE)) {
      // Deal with const T as well as non-const T.
      typedef typename base::remove_const<T>::type mutable_T;
      GoogleOnceInternalInit(&this->state_, 0,
                             reinterpret_cast<void (*)(void*)>(func_with_arg),
                             const_cast<mutable_T*>(arg));
    }
    ANNOTATE_HAPPENS_AFTER(&this->state_);
  }
 private:
  Atomic32 state_;
  DISALLOW_COPY_AND_ASSIGN(GoogleOnceDynamic);
};

#endif  // BASE_ONCE_H_
