The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
/* Copyright 2009 Cybozu Labs, Inc. All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 * 
 * THIS SOFTWARE IS PROVIDED BY CYBOZU LABS, INC. ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
 * EVENT SHALL CYBOZU LABS, INC. OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * 
 * The views and conclusions contained in the software and documentation are
 * those of the authors and should not be interpreted as representing official
 * policies, either expressed or implied, of Cybozu Labs, Inc.
 */

#ifndef cac_rwlock_h
#define cac_rwlock_h

template <typename T> class cac_rwlock_t {
public:
  
  class readref {
  protected:
    cac_rwlock_t<T>* m_;
  public:
    readref(cac_rwlock_t<T>& m) : m_(&m) {
      pthread_rwlock_rdlock(&m_->rwlock_);
    }
    ~readref() {
      pthread_rwlock_unlock(&m_->rwlock_);
    }
    operator const T*() { return &m_->t_; }
    const T& operator*() { return *operator const T*(); }
    const T* operator->() { return operator const T*(); }
  private:
    readref(const readref&);
    readref& operator=(const readref&);
    
    friend class cac_rwlock_t<T>::writeref;
    readref(cac_rwlock_t<T>& m, int) : m_(&m) {}
  };
  
  class writeref : public readref {
  public:
    writeref(cac_rwlock_t<T>& m) : readref(m, 0) {
      pthread_rwlock_wrlock(&m.rwlock_);
    }
    operator T*() {
      // GCC 4.1 seems to require the use of "this->"
      return &this->m_->t_;
    }
    T& operator*() { return *operator T*(); }
    T* operator->() { return operator T*(); }
  };
  
protected:
  friend class cac_rwlock_t<T>::readref;
  friend class cac_rwlock_t<T>::writeref;
  T t_;
  pthread_rwlock_t rwlock_;
public:
  cac_rwlock_t(pthread_rwlockattr_t* attr) : t_() {
    pthread_rwlock_init(&rwlock_, attr);
  }
  ~cac_rwlock_t() {
    pthread_rwlock_destroy(&rwlock_);
  }
  const T* unsafe_ref() const { return &t_; }
  T* unsafe_ref() { return &t_; }
  pthread_rwlock_t* rwlock() { return &rwlock_; }
private:
  cac_rwlock_t(const cac_rwlock_t&);
  cac_rwlock_t& operator=(const cac_rwlock_t&);
};

#endif