diff options
author | David Robillard <d@drobilla.net> | 2009-11-01 17:12:41 +0000 |
---|---|---|
committer | David Robillard <d@drobilla.net> | 2009-11-01 17:12:41 +0000 |
commit | 6bb54878523e4a9cbf9aa886758fa584d1a9bd0a (patch) | |
tree | 8f27d389d9706eabb5d58e4bb090771fba0d4c03 /libs | |
parent | c3f6ab344ce2a65767e2afcc83ffc24b6054e61d (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.h | 75 |
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; |