ardour
rcu.h
Go to the documentation of this file.
1 /*
2  Copyright (C) 2000-2007 Paul Davis
3 
4  This program is free software; you can redistribute it and/or modify
5  it under the terms of the GNU General Public License as published by
6  the Free Software Foundation; either version 2 of the License, or
7  (at your option) any later version.
8 
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  GNU General Public License for more details.
13 
14  You should have received a copy of the GNU General Public License
15  along with this program; if not, write to the Free Software
16  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17 
18 */
19 
20 #ifndef __pbd_rcu_h__
21 #define __pbd_rcu_h__
22 
23 #include "boost/shared_ptr.hpp"
24 #include "glibmm/threads.h"
25 
26 #include <list>
27 
28 #include "pbd/libpbd_visibility.h"
29 
47 template<class T>
48 class /*LIBPBD_API*/ RCUManager
49 {
50  public:
51 
52  RCUManager (T* new_rcu_value) {
53  x.m_rcu_value = new boost::shared_ptr<T> (new_rcu_value);
54  }
55 
56  virtual ~RCUManager() { delete x.m_rcu_value; }
57 
58  boost::shared_ptr<T> reader () const { return *((boost::shared_ptr<T> *) g_atomic_pointer_get (&x.gptr)); }
59 
60  /* this is an abstract base class - how these are implemented depends on the assumptions
61  that one can make about the users of the RCUManager. See SerializedRCUManager below
62  for one implementation.
63  */
64 
65  virtual boost::shared_ptr<T> write_copy () = 0;
66  virtual bool update (boost::shared_ptr<T> new_value) = 0;
67 
68  protected:
69  /* ordinarily this would simply be a declaration of a ptr to a shared_ptr<T>. however, the atomic
70  operations that we are using (from glib) have sufficiently strict typing that it proved hard
71  to get them to accept even a cast value of the ptr-to-shared-ptr() as the argument to get()
72  and comp_and_exchange(). Consequently, we play a litle trick here that relies on the fact
73  that sizeof(A*) == sizeof(B*) no matter what the types of A and B are. for most purposes
74  we will use x.m_rcu_value, but when we need to use an atomic op, we use x.gptr. Both expressions
75  evaluate to the same address.
76  */
77 
78  union {
80  mutable volatile gpointer gptr;
81  } x;
82 };
83 
84 
113 template<class T>
114 class /*LIBPBD_API*/ SerializedRCUManager : public RCUManager<T>
115 {
116 public:
117 
118  SerializedRCUManager(T* new_rcu_value)
119  : RCUManager<T>(new_rcu_value)
120  {
121  }
122 
124  {
125  m_lock.lock();
126 
127  // clean out any dead wood
128 
129  typename std::list<boost::shared_ptr<T> >::iterator i;
130 
131  for (i = m_dead_wood.begin(); i != m_dead_wood.end(); ) {
132  if ((*i).unique()) {
133  i = m_dead_wood.erase (i);
134  } else {
135  ++i;
136  }
137  }
138 
139  /* store the current so that we can do compare and exchange
140  when someone calls update(). Notice that we hold
141  a lock, so this store of m_rcu_value is atomic.
142  */
143 
145 
146  boost::shared_ptr<T> new_copy (new T(**current_write_old));
147 
148  return new_copy;
149 
150  /* notice that the write lock is still held: update() MUST
151  be called or we will cause another writer to stall.
152  */
153  }
154 
155  bool update (boost::shared_ptr<T> new_value)
156  {
157  /* we still hold the write lock - other writers are locked out */
158 
159  boost::shared_ptr<T>* new_spp = new boost::shared_ptr<T> (new_value);
160 
161  /* update, by atomic compare&swap. Only succeeds if the old
162  value has not been changed.
163 
164  XXX but how could it? we hold the freakin' lock!
165  */
166 
167  bool ret = g_atomic_pointer_compare_and_exchange (&RCUManager<T>::x.gptr,
168  (gpointer) current_write_old,
169  (gpointer) new_spp);
170 
171  if (ret) {
172 
173  // successful update : put the old value into dead_wood,
174 
175  m_dead_wood.push_back (*current_write_old);
176 
177  // now delete it - this gets rid of the shared_ptr<T> but
178  // because dead_wood contains another shared_ptr<T> that
179  // references the same T, the underlying object lives on
180 
181  delete current_write_old;
182  }
183 
184  /* unlock, allowing other writers to proceed */
185 
186  m_lock.unlock();
187 
188  return ret;
189  }
190 
191  void flush () {
193  m_dead_wood.clear ();
194  }
195 
196 private:
197  Glib::Threads::Mutex m_lock;
199  std::list<boost::shared_ptr<T> > m_dead_wood;
200 };
201 
216 template<class T>
217 class /*LIBPBD_API*/ RCUWriter
218 {
219 public:
220 
222  : m_manager(manager) {
223  m_copy = m_manager.write_copy();
224  }
225 
227  if (m_copy.unique()) {
228  /* As intended, our copy is the only reference
229  to the object pointed to by m_copy. Update
230  the manager with the (presumed) modified
231  version.
232  */
233  m_manager.update(m_copy);
234  } else {
235  /* This means that some other object is using our copy
236  of the object. This can only happen if the scope in
237  which this RCUWriter exists passed it to a function
238  that created a persistent reference to it, since the
239  copy was private to this particular RCUWriter. Doing
240  so will not actually break anything but it violates
241  the design intention here and so we do not bother to
242  update the manager's copy.
243 
244  XXX should we print a warning about this?
245  */
246  }
247 
248  }
249 
251 
252 private:
255 };
256 
257 #endif /* __pbd_rcu_h__ */
RCUWriter(RCUManager< T > &manager)
Definition: rcu.h:221
void flush()
Definition: rcu.h:191
bool update(boost::shared_ptr< T > new_value)
Definition: rcu.h:155
virtual ~RCUManager()
Definition: rcu.h:56
~RCUWriter()
Definition: rcu.h:226
virtual boost::shared_ptr< T > write_copy()=0
boost::shared_ptr< T > m_copy
Definition: rcu.h:254
boost::shared_ptr< T > get_copy() const
Definition: rcu.h:250
RCUManager(T *new_rcu_value)
Definition: rcu.h:52
SerializedRCUManager(T *new_rcu_value)
Definition: rcu.h:118
RCUManager< T > & m_manager
Definition: rcu.h:253
Glib::Threads::Mutex m_lock
Definition: rcu.h:197
boost::shared_ptr< T > * m_rcu_value
Definition: rcu.h:79
boost::shared_ptr< T > * current_write_old
Definition: rcu.h:198
virtual bool update(boost::shared_ptr< T > new_value)=0
std::list< boost::shared_ptr< T > > m_dead_wood
Definition: rcu.h:199
volatile gpointer gptr
Definition: rcu.h:80
Definition: rcu.h:48
boost::shared_ptr< T > write_copy()
Definition: rcu.h:123
Definition: rcu.h:217
boost::shared_ptr< T > reader() const
Definition: rcu.h:58
union RCUManager::@25 x