Merged with trunk R1141
[ardour.git] / libs / pbd / pbd / rcu.h
1 #ifndef __pbd_rcu_h__
2 #define __pbd_rcu_h__
3
4 #include "boost/shared_ptr.hpp"
5 #include "glibmm/thread.h"
6  
7 #include <list> 
8
9 template<class T>
10 class RCUManager
11 {
12   public:
13  
14         RCUManager (T* new_rcu_value) {
15                 m_rcu_value = new boost::shared_ptr<T> (new_rcu_value);
16         }
17  
18         virtual ~RCUManager() { delete m_rcu_value; }
19  
20         boost::shared_ptr<T> reader () const { return *((boost::shared_ptr<T> *) g_atomic_pointer_get (the_pointer())); }
21  
22         virtual boost::shared_ptr<T> write_copy () = 0;
23         virtual bool update (boost::shared_ptr<T> new_value) = 0;
24
25   protected:
26         boost::shared_ptr<T>* m_rcu_value;
27
28         // this monstrosity is needed because of some wierd behavior by g++
29
30         gpointer * the_pointer() const { return (gpointer *) &m_rcu_value; }
31 };
32  
33  
34 template<class T>
35 class SerializedRCUManager : public RCUManager<T>
36 {
37 public:
38  
39         SerializedRCUManager(T* new_rcu_value)
40                 : RCUManager<T>(new_rcu_value)
41         {
42  
43         }
44  
45         boost::shared_ptr<T> write_copy ()
46         {
47                 m_lock.lock();
48
49                 // clean out any dead wood
50
51                 typename std::list<boost::shared_ptr<T> >::iterator i;
52
53                 for (i = m_dead_wood.begin(); i != m_dead_wood.end(); ) {
54                         if ((*i).use_count() == 1) {
55                                 i = m_dead_wood.erase (i);
56                         } else {
57                                 ++i;
58                         }
59                 }
60
61                 // store the current 
62
63                 current_write_old = RCUManager<T>::m_rcu_value;
64                 
65                 boost::shared_ptr<T> new_copy (new T(**current_write_old));
66
67                 return new_copy;
68         }
69  
70         bool update (boost::shared_ptr<T> new_value)
71         {
72                 // we hold the lock at this point effectively blocking
73                 // other writers.
74
75                 boost::shared_ptr<T>* new_spp = new boost::shared_ptr<T> (new_value);
76
77                 // update, checking that nobody beat us to it
78
79                 bool ret = g_atomic_pointer_compare_and_exchange (RCUManager<T>::the_pointer(),
80                                                                   (gpointer) current_write_old,
81                                                                   (gpointer) new_spp);
82                 
83                 if (ret) {
84
85                         // successful update : put the old value into dead_wood,
86
87                         m_dead_wood.push_back (*current_write_old);
88
89                         // now delete it - this gets rid of the shared_ptr<T> but
90                         // because dead_wood contains another shared_ptr<T> that
91                         // references the same T, the underlying object lives on
92
93                         delete current_write_old;
94                 }
95
96                 m_lock.unlock();
97
98                 return ret;
99         }
100
101         void flush () {
102                 Glib::Mutex::Lock lm (m_lock);
103                 m_dead_wood.clear ();
104         }
105  
106 private:
107         Glib::Mutex                      m_lock;
108         boost::shared_ptr<T>*            current_write_old;
109         std::list<boost::shared_ptr<T> > m_dead_wood;
110 };
111  
112 template<class T>
113 class RCUWriter
114 {
115 public:
116  
117         RCUWriter(RCUManager<T>& manager)
118                 : m_manager(manager)
119         {
120                 m_copy = m_manager.write_copy();        
121         }
122  
123         ~RCUWriter()
124         {
125                 // we can check here that the refcount of m_copy is 1
126  
127                 if(m_copy.use_count() == 1) {
128                         m_manager.update(m_copy);
129                 } else {
130  
131                         // critical error.
132                 }
133  
134         }
135  
136         // or operator boost::shared_ptr<T> ();
137         boost::shared_ptr<T> get_copy() { return m_copy; }
138  
139 private:
140  
141         RCUManager<T>& m_manager;
142  
143         // preferably this holds a pointer to T
144         boost::shared_ptr<T> m_copy;
145 };
146
147 #endif /* __pbd_rcu_h__ */