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