Doxygen readable comments, tidy, fix whitespace.
git-svn-id: svn://localhost/ardour2/branches/3.0@5995 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
parent
c3f6ab344c
commit
6bb5487852
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user