Logo Search packages:      
Sourcecode: efax-gtk version File versions

shared_ptr.h

/* Copyright (C) 2004 Chris Vine

This program is distributed under the General Public Licence, version 2.
For particulars of this and relevant disclaimers see the file
COPYING distributed with the source files.

*/

#ifndef SHARED_PTR_H
#define SHARED_PTR_H

#include <glibmm/thread.h>

template <class T> class Shared_ptr {

  struct {
    unsigned int* ref_count_p;
    T* obj_p;
  } ref_items;

  void unreference(void) {
    --(*ref_items.ref_count_p);
    if (*ref_items.ref_count_p == 0) {
      delete ref_items.obj_p;  // if this is NULL it is still safe to apply delete to it
      delete ref_items.ref_count_p;
    }
  }

  void reference(void) {
    ++(*ref_items.ref_count_p);
  }

public:
  // constructor of first Shared_ptr holding the referenced object
  explicit Shared_ptr(T* ptr = 0) {
    ref_items.ref_count_p = new unsigned int(1);
    ref_items.obj_p = ptr;
  }

  // copy constructors
  Shared_ptr(const Shared_ptr& sh_ptr) throw() {
    ref_items = sh_ptr.ref_items;
    reference();
  }

  template <class U> friend class Shared_ptr;

  template <class U> Shared_ptr(const Shared_ptr<U>& sh_ptr) throw() {
    // because we are allowing an implicit cast from derived to
    // base class referenced object, we need to assign from each
    // member of sh_ptr.ref_items separately
    ref_items.ref_count_p = sh_ptr.ref_items.ref_count_p;
    ref_items.obj_p = sh_ptr.ref_items.obj_p;
    reference();
  }

  // copy assignment
  Shared_ptr& operator=(const Shared_ptr& sh_ptr) throw() {

    // check whether we are already referencing this object -
    // if so make this a null op.  This will also deal with
    // self-assignment
    if (ref_items.obj_p != sh_ptr.ref_items.obj_p) {

      // first unreference any object referenced by this shared pointer
      unreference();

      // now inherit the ref_items structure from the assigning
      // shared pointer and reference the object it references
      ref_items = sh_ptr.ref_items;
      reference();
    }
    return *this;
  }

  template <class U> Shared_ptr& operator=(const Shared_ptr<U>& sh_ptr) throw() {
    return operator=(Shared_ptr(sh_ptr));
  }

  T* get(void) const throw() {return ref_items.obj_p;}
  T& operator*(void) const throw() {return *ref_items.obj_p;}
  T* operator->(void) const throw() {return ref_items.obj_p;}

  unsigned int get_refcount(void) const throw() {return *ref_items.ref_count_p;}

  // destructor
  ~Shared_ptr(void) throw() {unreference();}
};

/*
  Class Shared_lock_ptr is a version of the shared pointer class which
  includes mutex locking so that it can be accessed in multiple
  threads. Note that only the reference count is protected by a mutex,
  so this is thread safe in the sense in which a raw pointer is thread
  safe.  A shared pointer accessed in one thread referencing a
  particular object is thread safe as against another shared pointer
  accessing the same object in a different thread.  It is thus
  suitable for use in different Std C++ containers which exist in
  different threads but which contain shared objects by reference.
  But:

  1.  If the referenced object is to be modified in one thread and
      read or modified in another thread an appropriate mutex for the
      referenced object is required (unless that referenced object
      does its own locking).

  2.  If the same instance of shared pointer is to be modified in one
      thread (by assigning to the pointer so that it references a
      different object), and copied (assigned from or used as the
      argument of a copy constructor) or modified in another thread, a
      mutex for that instance of shared pointer is required.

  3.  Objects referenced by shared pointers which are objects for
      which POSIX provides no guarantees (in the main, those which are
      not built-in types), such as strings and similar containers, may
      not support concurrent reads in different threads.  That depends
      on the library implementation concerned.  If that is the case, a
      mutex for the referenced object will also be required when
      reading any given instance of such an object in more than one
      thread by dereferencing any shared pointers referencing it (and
      indeed, when not using shared pointers at all).
*/

template <class T> class Shared_lock_ptr {

  struct {
    Glib::Mutex* mutex_p;
    unsigned int* ref_count_p;
    T* obj_p;
  } ref_items;

  // Shared_lock_ptr<T>::unreference() does not throw exceptions because 
  // Glib::Mutex::~Mutex(), Glib::Mutex::lock() and Glib::Mutex::unlock()
  // do not throw
  void unreference(void) {
    ref_items.mutex_p->lock();
    --(*ref_items.ref_count_p);
    if (*ref_items.ref_count_p == 0) {
      delete ref_items.obj_p;  // if this is NULL it is still safe to apply delete to it
      delete ref_items.ref_count_p;
      ref_items.mutex_p->unlock();
      delete ref_items.mutex_p;
    }
    else ref_items.mutex_p->unlock();
  }

  // Shared_lock_ptr<T>::reference() does not throw exceptions because
  // Glib::Mutex::Lock::Lock() and Glib::Mutex:::Lock::~Lock() do not throw
  void reference(void) {
    Glib::Mutex::Lock lock(*ref_items.mutex_p);
    ++(*ref_items.ref_count_p);
  }

public:
  // constructor of first Shared_lock_ptr holding the referenced object
  explicit Shared_lock_ptr(T* ptr = 0) {
    ref_items.mutex_p = new Glib::Mutex;
    // make this constructor exception safe
    try {
      ref_items.ref_count_p = new unsigned int(1);
    }
    catch (...) {
      delete ref_items.mutex_p;
      throw;
    }
    ref_items.obj_p = ptr;
  }

  // copy constructors
  Shared_lock_ptr(const Shared_lock_ptr& sh_ptr) throw() {
    ref_items = sh_ptr.ref_items;
    reference();
  }

  template <class U> friend class Shared_lock_ptr;

  template <class U> Shared_lock_ptr(const Shared_lock_ptr<U>& sh_ptr) throw() {
    // because we are allowing an implicit cast from derived to
    // base class referenced object, we need to assign from each
    // member of sh_ptr.ref_items separately
    ref_items.mutex_p = sh_ptr.ref_items.mutex_p;
    ref_items.obj_p = sh_ptr.ref_items.obj_p;
    ref_items.ref_count_p = sh_ptr.ref_items.ref_count_p;
    reference();
  }

  // copy assignment
  Shared_lock_ptr& operator=(const Shared_lock_ptr& sh_ptr) throw() {

    // check whether we are already referencing this object -
    // if so make this a null op.  This will also deal with
    // self-assignment
    if (ref_items.obj_p != sh_ptr.ref_items.obj_p) {

      // first unreference any object referenced by this shared pointer
      unreference();
      
      // now inherit the ref_items structure from the assigning
      // shared pointer and reference the object it references
      ref_items = sh_ptr.ref_items;
      reference();
    }
    return *this;
  }

  template <class U> Shared_lock_ptr& operator=(const Shared_lock_ptr<U>& sh_ptr) throw() {
    return operator=(Shared_lock_ptr(sh_ptr));
  }
  
  T* get(void) const throw() {return ref_items.obj_p;}
  T& operator*(void) const throw() {return *ref_items.obj_p;}
  T* operator->(void) const throw() {return ref_items.obj_p;}

  unsigned int get_refcount(void) const throw() {
    Glib::Mutex::Lock lock(*ref_items.mutex_p);
    return *ref_items.ref_count_p;
  }

  // destructor
  ~Shared_lock_ptr(void) throw() {unreference();}
};

#endif

Generated by  Doxygen 1.6.0   Back to index