Boost logo

Boost :

From: Chris Thomasson (cristom_at_[hidden])
Date: 2006-11-02 00:35:18


Here is the C++ pseudo-code:

namespace atomic {
  template<typename T> class var;
  template<typename T> class once;

  namespace mb {
    enum naked_e { naked };
    enum fence_e { fence };
    enum release_e { release };
    enum depends_e { depends };
    enum acquire_depends_e { acquire_depends };
  }

  namespace externlib_api {
    class hashed_mutex;

    template<typename T> extern
    T load(T volatile*, mb::naked_e) const throw();

    template<typename T> extern
    T load(T volatile*, mb::depends_e) const throw();

    template<typename T> extern
    void store(T volatile*, T const&, mb::release_e) throw();

    template<typename T> extern
    void store(T volatile*, T const&, mb::naked_e) throw();

    template<typename T> extern
    bool cas(T volatile*, T const&, T const&, mb::acquire_depends_e)
throw();

    template<typename T> extern
    bool cas(T volatile*, T const&, T const&, mb::fence_e) throw();
  }

  template<typename T>
  class var {
    mutable T volatile m_state;

  public:
    var() throw() {}
    var(T const &state) throw() : m_state(state) {}

  public:
    inline T load(mb::depends_e) const throw() {
      return externlib_api::load(&m_state, mb::depends);
    }

    inline T load(mb::naked_e) const throw() {
      return externlib_api::load(&m_state, mb::naked);
    }

  public:
    inline void store(T const &xchg, mb::fence_e) throw() {
      externlib_api::store(&m_state, xchg, mb::fence);
    }

    inline void store(T const &xchg, mb::naked_e) throw() {
      externlib_api::store(&m_state, xchg, mb::naked);
    }

public:
    inline bool cas(T const &cmp, T const &xchg, mb::fence_e) throw() {
      return externlib_api::cas(&m_state, cmp, xchg, mb::fence);
    }

    inline bool cas(T const &cmp, T const &xchg, mb::acquire_depends_e)
throw() {
      return externlib_api::cas(&m_state, cmp, xchg, mb::acquire_depends);
    }
  };

 template<typename T>
  class once {
    var<T*> m_state;
    var<intword_t> m_count;

  public:
    once() throw() : m_state(0), m_count(1) {}

    ~once() throw() {
      if (try_dec()) {
        try { delete local; }
          catch(...) { assert(false); throw; }
      }
    }

  private:
    bool try_dec() throw() {
      intword_t local;
      do {
        local = m_count.load(mb::naked);
        if (! local || local < 1) { return false; }
        // use mb::fence to cover *both acquire and release
        // wrt the result of the decrement
      } while(! m_count.cas(local, local - 1, mb::fence));
      return (local == 1);
    }

    bool try_inc() throw() {
      intword_t local;
      do {
        local = m_count.load(mb::naked);
        if (! local || local < 1) { return false; }
      } while(! m_count.cas(local, local + 1, mb::acquire_depends));
      return true;
    }

  public:
    // atomic load / thread-saftey: strong
    T* load() const {
      T *local = m_state.load(mb::depends);
      if (! local) {
        externlib_api::hashed_mutex::guard_t const &lock(this);
        if (! try_inc()) { return 0; }
        local = m_state.load(mb::naked);
        if (! local) {
          // call try_dec on exceptions...
          local = new T;
          m_state.store(local, mb::release);
        }
      } else if(! try_inc()) {
        return 0;
      }

      return local;
    }

    // dec the refcount
    void dec() { if (try_dec()) { delete local; } }
  };
}

okay, here is sample usage now:

static atomic::once<foo> g_foo;

void some_threads(...) {
  for(;;) {
    // whatever...

    foo *myfoo = g_foo.load();
    if (myfoo) {
      // use myfoo...

      // okay, we are finished with myfoo
      g_foo.dec();
    }

  }
}

This should be a workable solution to the static initialization problem...

What do you all think?


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