summaryrefslogtreecommitdiff
path: root/libs
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2009-11-01 17:12:41 +0000
committerDavid Robillard <d@drobilla.net>2009-11-01 17:12:41 +0000
commit6bb54878523e4a9cbf9aa886758fa584d1a9bd0a (patch)
tree8f27d389d9706eabb5d58e4bb090771fba0d4c03 /libs
parentc3f6ab344ce2a65767e2afcc83ffc24b6054e61d (diff)
Doxygen readable comments, tidy, fix whitespace.
git-svn-id: svn://localhost/ardour2/branches/3.0@5995 d708f5d6-7413-0410-9779-e7cbd77b26cf
Diffstat (limited to 'libs')
-rw-r--r--libs/pbd/pbd/rcu.h75
1 files changed, 36 insertions, 39 deletions
diff --git a/libs/pbd/pbd/rcu.h b/libs/pbd/pbd/rcu.h
index 4c252c87d1..7c63acdb0d 100644
--- a/libs/pbd/pbd/rcu.h
+++ b/libs/pbd/pbd/rcu.h
@@ -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
@@ -22,38 +22,39 @@
#include "boost/shared_ptr.hpp"
#include "glibmm/thread.h"
-
-#include <list>
-/* 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)); }
-
+
/* 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();
@@ -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();
@@ -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,
@@ -192,15 +190,15 @@ public:
Glib::Mutex::Lock lm (m_lock);
m_dead_wood.clear ();
}
-
+
private:
Glib::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.use_count() == 1) {
/* 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;