$include_dir="/home/hyper-archives/boost/include"; include("$include_dir/msg-header.inc") ?>
From: Chris Thomasson (cristom_at_[hidden])
Date: 2006-11-04 02:01:02
"Roland Schwarz" <roland.schwarz_at_[hidden]> wrote in message
news:454A9E56.8050901_at_chello.at...
> Chris Thomasson wrote:
>> Here is one without a ctor:
>
> It still has a dtor. Same as Anmthony already said applies to dtor too.
>
> You will end up needing to drop the class and use a function with a
> flag I suspect.
Yup. I think so...
>
> Btw.: The usage scenario you were showing earlier is not quite what
> we had in mind. We were speaking about local statics.
Ahhh... Okay, I was thinking global.
> In your example usage, you have moved the static part
> to global namespace. There are far more less problems.
Indeed!  :^)
> The problems are getting nasty when needing local statics
> as in
>
> void foo()
> {
> static bar mybar;
>
> }
>
> You really might need them, because the static possibly
> depending an a template parameter, which makes it impossible
> to move it to global namespace.
>
> So you will need to be able to declare the once class inside the
> function body scope.
I see.
> Back to ctor/dtor. Once you have found out that ctors and dtors
> are bad, what is left? Well several options:
>
> 1) a once function and a flag (instead of a class)
> 2) a static initializeable mutex
> 1) and 2) are roughly equivalent in that one can be built out of
> the other.
Agreed. However, one can be lock-free after the initialization takes 
place... IMHO, this is a fairly important aspect of this type of algorithm. 
I am going to give my pseudo-code one more shot at the end of the post in 
the form of compliable code that uses my AppCore library. This time, no 
class, just a single C function. I urge you to bear with me, and try to take 
a quick look at it... Thank you all for your time and patience!
:^)
> 1) has been used in pthreads (before a static mutex type was available)
>    Boost.Thread also uses this also mainly in implementation code.
>
> 2) I have tried to find out if it is possible to implement a mutex
>    that is statically initializeable with the additional constraint
>    that zero initialization suffices.
>    I have provided a prototype that let me believe it really can be
>    done. (Such a mutex has the additional benefit that you need no
>    explicit memory management.)
You can do a spinlock for sure... How are you setting the mutex's waitset?
Are you deferring waitset allocation until first point of contention (e.g.,
lazy mutex) ?
Okay here is a link to AppCore:
http://appcore.home.comcast.net/
And here is the once code; please tell me what you think of my technique:
once-experimental.cpp
---------------
#include <cassert>
#include <cstdio>
#include <appcore.h>
// User API Decl
namespace atomic {
  template<typename T> class once_ptr;
} // namespace atomic
// System API Decl
namespace atomic {
namespace sys {
  typedef ac_intword_t refs_t;
  template<typename T> struct once_POD;
  template<typename T> struct once_def_POD;
  static bool once_inc(refs_t*) throw();
  static bool once_dec(refs_t*) throw();
  static void dbg_allocs_inc() throw();
  static void dbg_allocs_dec() throw();
}} // namespace atomic::sys
// System API Def
namespace atomic {
namespace sys {
  // Debug counter
  static ac_intword_t dbg_allocs = 0;
  // Inc the debug counter
  void dbg_allocs_inc() throw() {
    ac_intword_t allocs = ac_atomic_inc_acquire(&dbg_allocs);
    std::printf("atomic::sys::dbg_allocs_inc - %i\n", allocs);
  }
  // Inc the debug counter
  void dbg_allocs_dec() throw() {
    ac_intword_t allocs = ac_atomic_dec_acquire(&dbg_allocs);
    std::printf("atomic::sys::dbg_allocs_dec - %i\n", allocs);
  }
  // Inc the refcount if its >= 0.
  // returns true if the refcount was inc'd,
  // otherwise false
  bool once_inc(refs_t *_this) throw() {
    refs_t local, cmp;
    do {
      local = ::ac_mb_load_naked(_this);
      if (local < 0) { return false; }
      cmp = local;
      local = ac_atomic_cas_acquire(_this, local, local + 1);
    } while(cmp != local);
    std::printf("atomic::sys::once_inc(); - %i\n", local + 1);
    return true;
  }
  // Dec the refcount if its >= 0.
  // returns true for the last ref,
  // otherwise false
  bool once_dec(refs_t *_this) throw() {
    refs_t local, cmp;
    do {
      local = ::ac_mb_load_naked(_this);
      if (local < 0) { return false; }
      cmp = local;
      local = ac_atomic_cas_acquire(_this, local, local - 1);
    } while(cmp != local);
    std::printf("atomic::sys::once_dec(); - %i\n", local - 1);
    if (local == 1) {
      ac_mb_store_release(_this, 0);
      return true;
    } else if (! local) {
      return true;
    }
    return false;
  }
  // once is a POD
  #define ATOMIC_ONCE_SYS_STATICINIT() {0, 0}
  template<typename T>
  struct once_POD {
    typedef T type_t;
    typedef once_def_POD<once_POD> define_POD_t;
    refs_t m_refs;
    type_t *m_state;
  };
  // define is a POD that holds a once POD
  template<typename T>
  struct once_def_POD {
    typedef typename T::type_t type_t;
    static T s_this;
    // Acquires a reference and calls ctor for first ref.
    // returns pointer to ref,
    // otherwise NULL
    type_t* acquire() {
      type_t *local = (type_t*)ac_mb_loadptr_depends(&s_this.m_state);
      if (! local) {
        // hashed_mutex::guard_t lock(&_this);
        local = (type_t*)ac_mb_loadptr_depends(&s_this.m_state);
        if (! once_inc(&s_this.m_refs)) { return 0; }
        if (! local) {
          try { local = new type_t; }
          catch(...) {
            (void)once_dec(&s_this.m_refs);
            throw;
          }
          dbg_allocs_inc();
          std::printf("\n(%p)once_def_POD::acquire(); - %p\n", (void*)this, 
(void*)local);
          ac_mb_storeptr_release(&s_this.m_state, local);
        }
      } else if(! once_inc(&s_this.m_refs)) {
        return 0;
      }
      return local;
    }
    // Releases a reference and calls dtor if last ref.
    // returns nothing,
    // otherwise false
    void release() {
      type_t *local = (type_t*)ac_mb_loadptr_depends(&s_this.m_state);
      if (once_dec(&s_this.m_refs)) {
        if (local) {
          ac_atomic_casptr_acquire(&s_this.m_state, local, 0);
          delete local;
          std::printf("(%p)once_def_POD::release(); - %p\n\n", (void*)this, 
(void*)local);
          dbg_allocs_dec();
        }
      }
    }
      // static POD init
  };  template<typename T>
      T once_def_POD<T>::s_this = ATOMIC_ONCE_SYS_STATICINIT();
}} // namespace atomic::sys
// User API Def
namespace atomic {
  // Holds an acquired reference.
  template<typename T>
  class once_ptr {
  public:
    typedef typename sys::once_POD<T>::define_POD_t define_POD_t;
  private:
    define_POD_t *m_once;
    T *m_state;
  private:
    T* acquire() {
      return (m_once) ? m_once->acquire() : 0;
    }
    void release() {
      if (m_once) { assert(m_state); m_once->release(); }
    }
  public:
    once_ptr() throw() : m_once(0), m_state(0) {}
    once_ptr(define_POD_t &_once)
      : m_once(&_once), m_state(_once.acquire()) {}
    ~once_ptr() { release(); }
  public:
    once_ptr(once_ptr const &rhs)
      : m_once(rhs.m_once), m_state(rhs.acquire()) {}
    once_ptr const& operator =(once_ptr &rhs) {
      define_POD_t *old = m_once;
      if (old != rhs.m_once) {
        define_POD_t *_once = rhs.m_once;
        T *_state = rhs.acquire();
        release();
        m_once = _once;
        m_state = _state;
      }
      return *this;
    }
    once_ptr const& operator =(define_POD_t &rhs) {
      define_POD_t *old = m_once;
      if (old != &rhs) {
        define_POD_t *_once = &rhs;
        T *_state = rhs.acquire();
        release();
        m_once = _once;
        m_state = _state;
      }
      return *this;
    }
  public:
    T* load() const throw() { return m_state; }
  public:
    T* operator ->() { return load(); }
    T& operator *() { return *load(); }
  public:
    operator bool() throw() { return (m_state != 0); }
    bool operator !() throw() { return (m_state == 0); }
  };
} // namespace atomic
// Here is sample usage:
struct foo1 {
  typedef atomic::once_ptr<foo1> once_ptr_t;
  void whatever() {
    std::printf("(%p)foo1::whatever();\n", (void*)this);
  }
  foo1() { std::printf("(%p)foo1::foo1();\n", (void*)this); }
  ~foo1() { std::printf("(%p)foo1::~foo1();\n", (void*)this); }
};
static void funca_for_multiple_threads() {
  static foo1::once_ptr_t::define_POD_t s_foo;
  foo1::once_ptr_t myfoo1(s_foo);
  if (myfoo1) {
    myfoo1->whatever();
  }
}
struct foo2 {
  typedef atomic::once_ptr<foo1> once_ptr_t;
  void whatever() {
    std::printf("(%p)foo2::whatever();\n", (void*)this);
    funca_for_multiple_threads();
  }
  foo2() {
    static foo1::once_ptr_t::define_POD_t s_foo;
    foo1::once_ptr_t myfoo1(s_foo);
    if (myfoo1) {
      myfoo1->whatever();
    }
    std::printf("(%p)foo2::foo2();\n", (void*)this);
  }
  ~foo2() { std::printf("(%p)foo2::~foo2();\n", (void*)this); }
};
struct foo3 {
  typedef atomic::once_ptr<foo3> once_ptr_t;
  static foo1::once_ptr_t::define_POD_t s_foo1;
  void whatever() {
    std::printf("(%p)foo3::whatever();\n", (void*)this);
    foo1::once_ptr_t myfoo1(s_foo1);
    if (myfoo1) {
      myfoo1->whatever();
    }
    funca_for_multiple_threads();
  }
  foo3() {
    static foo2::once_ptr_t::define_POD_t s_foo2;
    foo2::once_ptr_t myfoo2(s_foo2), myfoo2a;
    if (myfoo2) {
      myfoo2->whatever();
      foo1::once_ptr_t myfoo1(s_foo1);
      myfoo2a = myfoo2;
      if (myfoo1) {
        myfoo1->whatever();
      }
      myfoo2a->whatever();
    }
    std::printf("(%p)foo3::foo3();\n", (void*)this);
  }
  ~foo3() {
    whatever();
    std::printf("(%p)foo3::~foo3();\n", (void*)this);
  }
};  foo1::once_ptr_t::define_POD_t foo3::s_foo1;
static void funcb_for_multiple_threads() {
  funca_for_multiple_threads();
  static foo2::once_ptr_t::define_POD_t s_myfoo2a;
  funca_for_multiple_threads();
  foo2::once_ptr_t smyfooa(s_myfoo2a), smyfooaa;
  if (smyfooa) {
    smyfooa->whatever();
  }
  {
    funca_for_multiple_threads();
    static foo2::once_ptr_t::define_POD_t s_myfoo2b;
    foo1 myfoo1;
    foo2::once_ptr_t smyfooa(s_myfoo2a);
    if (smyfooa) {
      smyfooaa = smyfooa;
      smyfooaa->whatever();
    }
    foo2 myfoo2;
    {
      myfoo1.whatever();
      foo2::once_ptr_t smyfoo2(s_myfoo2a);
      if (smyfoo2) {
        smyfoo2->whatever();
      }
      myfoo2.whatever();
      funca_for_multiple_threads();
      {
        foo3 myfoo3;
        myfoo3.whatever();
        foo2 myfoo2;
        myfoo2.whatever();
      }
      myfoo2.whatever();
    }
    foo3 myfoo3;
    foo2::once_ptr_t smyfoo2(s_myfoo2b);
    if (smyfoo2) {
      smyfoo2->whatever();
      funca_for_multiple_threads();
      myfoo3.whatever();
    }
    funca_for_multiple_threads();
  }
  if (smyfooaa) {
    smyfooaa->whatever();
  }
  funca_for_multiple_threads();
}
int main(int argc, char *argv[])
{
  funca_for_multiple_threads();
  funcb_for_multiple_threads();
  funca_for_multiple_threads();
  funcb_for_multiple_threads();
  return 0;
}