Boost logo

Boost :

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;
}


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk