Merge branch 'master' into audioengine
[ardour.git] / libs / pbd / pbd / rcu.h
index 4c252c87d157aae44e48a96f407e23b22df88a2e..909954fc0e1a8a681d19f3d931d93a5cb6e8b938 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2000-2007 Paul Davis 
+    Copyright (C) 2000-2007 Paul Davis
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 #define __pbd_rcu_h__
 
 #include "boost/shared_ptr.hpp"
-#include "glibmm/thread.h"
-#include <list> 
+#include "glibmm/threads.h"
 
-/* This header file defines a set of classes to implement Read-Copy-Update. we do not attempt to define RCU here - use google.
-   
-   The design consists of two parts. An RCUManager is an object which takes over management of a pointer to another object. 
+#include <list>
+
+/** @file Defines a set of classes to implement Read-Copy-Update.  We do not attempt to define RCU here - use google.
+
+   The design consists of two parts: an RCUManager and an RCUWriter.
+*/
+
+/** An RCUManager is an object which takes over management of a pointer to another object.
    It provides three key methods:
 
-               - reader() : obtains a shared pointer to the managed object that may be used for reading, without synchronization
+           - reader() : obtains a shared pointer to the managed object that may be used for reading, without synchronization
               - write_copy() : obtains a shared pointer to the object that may be used for writing/modification
               - update() : accepts a shared pointer to a (presumed) modified instance of the object and causes all
-                             future reader() and write_copy() calls to use that instance.
+                           future reader() and write_copy() calls to use that instance.
 
-   Any existing users of the value returned by reader() can continue to use their copy even as a write_copy()/update() takes place. 
+   Any existing users of the value returned by reader() can continue to use their copy even as a write_copy()/update() takes place.
    The RCU manager will manage the various instances of "the managed object" in a way that is transparent to users of the manager
    and managed object.
 */
-
-
 template<class T>
 class RCUManager
 {
   public:
+
        RCUManager (T* new_rcu_value) {
                x.m_rcu_value = new boost::shared_ptr<T> (new_rcu_value);
        }
+
        virtual ~RCUManager() { delete x.m_rcu_value; }
-        boost::shared_ptr<T> reader () const { return *((boost::shared_ptr<T> *) g_atomic_pointer_get (&x.gptr)); }
+
+       boost::shared_ptr<T> reader () const { return *((boost::shared_ptr<T> *) g_atomic_pointer_get (&x.gptr)); }
+
        /* this is an abstract base class - how these are implemented depends on the assumptions
           that one can make about the users of the RCUManager. See SerializedRCUManager below
           for one implementation.
@@ -65,7 +66,7 @@ class RCUManager
   protected:
        /* ordinarily this would simply be a declaration of a ptr to a shared_ptr<T>. however, the atomic
           operations that we are using (from glib) have sufficiently strict typing that it proved hard
-          to get them to accept even a cast value of the ptr-to-shared-ptr() as the argument to get() 
+          to get them to accept even a cast value of the ptr-to-shared-ptr() as the argument to get()
           and comp_and_exchange(). Consequently, we play a litle trick here that relies on the fact
           that sizeof(A*) == sizeof(B*) no matter what the types of A and B are. for most purposes
           we will use x.m_rcu_value, but when we need to use an atomic op, we use x.gptr. Both expressions
@@ -79,12 +80,12 @@ class RCUManager
 };
 
 
-/* Serialized RCUManager implements the RCUManager interface. It is based on the
+/** Serialized RCUManager implements the RCUManager interface. It is based on the
    following key assumption: among its users we have readers that are bound by
    RT time constraints, and writers who are not. Therefore, we do not care how
    slow the write_copy()/update() operations are, or what synchronization
    primitives they use.
-   
+
    Because of this design assumption, this class will serialize all
    writers. That is, objects calling write_copy()/update() will be serialized by
    a mutex. Only a single writer may be in the middle of write_copy()/update();
@@ -95,7 +96,7 @@ class RCUManager
    The class maintains a lock-protected "dead wood" list of old value of
    *m_rcu_value (i.e. shared_ptr<T>). The list is cleaned up every time we call
    write_copy(). If the list is the last instance of a shared_ptr<T> that
-   references the object (determined by inspecting its use_count()) then we
+   references the object (determined by shared_ptr::unique()) then we
    erase it from the list, thus deleting the object it points to.  This is lazy
    destruction - the SerializedRCUManager assumes that there will sufficient
    calls to write_copy() to ensure that we do not inadvertently leave objects
@@ -107,19 +108,16 @@ class RCUManager
    must be used with significant caution, although the use of shared_ptr<T>
    means that no actual objects will be deleted incorrectly if this is misused.
 */
 template<class T>
 class SerializedRCUManager : public RCUManager<T>
 {
 public:
+
        SerializedRCUManager(T* new_rcu_value)
                : RCUManager<T>(new_rcu_value)
        {
        }
+
        boost::shared_ptr<T> write_copy ()
        {
                m_lock.lock();
@@ -129,7 +127,7 @@ public:
                typename std::list<boost::shared_ptr<T> >::iterator i;
 
                for (i = m_dead_wood.begin(); i != m_dead_wood.end(); ) {
-                       if ((*i).use_count() == 1) {
+                       if ((*i).unique()) {
                                i = m_dead_wood.erase (i);
                        } else {
                                ++i;
@@ -142,7 +140,7 @@ public:
                */
 
                current_write_old = RCUManager<T>::x.m_rcu_value;
-               
+
                boost::shared_ptr<T> new_copy (new T(**current_write_old));
 
                return new_copy;
@@ -151,7 +149,7 @@ public:
                   be called or we will cause another writer to stall.
                */
        }
+
        bool update (boost::shared_ptr<T> new_value)
        {
                /* we still hold the write lock - other writers are locked out */
@@ -160,14 +158,14 @@ public:
 
                /* update, by atomic compare&swap. Only succeeds if the old
                   value has not been changed.
-                  
+
                   XXX but how could it? we hold the freakin' lock!
                */
 
                bool ret = g_atomic_pointer_compare_and_exchange (&RCUManager<T>::x.gptr,
                                                                  (gpointer) current_write_old,
                                                                  (gpointer) new_spp);
-               
+
                if (ret) {
 
                        // successful update : put the old value into dead_wood,
@@ -189,18 +187,18 @@ public:
        }
 
        void flush () {
-               Glib::Mutex::Lock lm (m_lock);
+               Glib::Threads::Mutex::Lock lm (m_lock);
                m_dead_wood.clear ();
        }
+
 private:
-       Glib::Mutex                      m_lock;
+       Glib::Threads::Mutex                      m_lock;
        boost::shared_ptr<T>*            current_write_old;
        std::list<boost::shared_ptr<T> > m_dead_wood;
 };
 
-/* RCUWriter is a convenience object that implements write_copy/update via
-   lifetime management. Creating the object obtais a writable copy, which can
+/** RCUWriter is a convenience object that implements write_copy/update via
+   lifetime management. Creating the object obtains a writable copy, which can
    be obtained via the get_copy() method; deleting the object will update
    the manager's copy. Code doing a write/update thus looks like:
 
@@ -213,19 +211,18 @@ private:
    } <= writer goes out of scope, update invoked
 
 */
 template<class T>
 class RCUWriter
 {
 public:
+
        RCUWriter(RCUManager<T>& manager)
                : m_manager(manager) {
-               m_copy = m_manager.write_copy();        
+               m_copy = m_manager.write_copy();
        }
+
        ~RCUWriter() {
-               if(m_copy.use_count() == 1) {
+               if (m_copy.unique()) {
                        /* As intended, our copy is the only reference
                           to the object pointed to by m_copy. Update
                           the manager with the (presumed) modified
@@ -240,16 +237,16 @@ public:
                           copy was private to this particular RCUWriter. Doing
                           so will not actually break anything but it violates
                           the design intention here and so we do not bother to
-                          update the manager's copy. 
+                          update the manager's copy.
 
                           XXX should we print a warning about this?
                        */
                }
+
        }
+
        boost::shared_ptr<T> get_copy() const { return m_copy; }
+
 private:
        RCUManager<T>& m_manager;
        boost::shared_ptr<T> m_copy;