/[svn]/linuxsampler/trunk/src/common/SynchronizedConfig.h
ViewVC logotype

Diff of /linuxsampler/trunk/src/common/SynchronizedConfig.h

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 2452 by persson, Sun Apr 29 16:14:45 2012 UTC revision 2453 by schoenebeck, Mon May 13 19:11:08 2013 UTC
# Line 24  Line 24 
24  #include <set>  #include <set>
25  #include <unistd.h>  #include <unistd.h>
26  #include "lsatomic.h"  #include "lsatomic.h"
27    #include "Mutex.h"
28    
29  namespace LinuxSampler {  namespace LinuxSampler {
30    
# Line 73  namespace LinuxSampler { Line 74  namespace LinuxSampler {
74                       *          object to be read by the real time                       *          object to be read by the real time
75                       *          thread                       *          thread
76                       */                       */
77                      const T& Lock() {                      /*const*/ T& Lock() { //TODO const currently commented for the DoubleBuffer usage below
78                          lock.store(lockCount += 2, memory_order_relaxed);                          lock.store(lockCount += 2, memory_order_relaxed);
79                          atomic_thread_fence(memory_order_seq_cst);                          atomic_thread_fence(memory_order_seq_cst);
80                          return parent.config[parent.indexAtomic.load(                          return parent->config[parent->indexAtomic.load(
81                                  memory_order_acquire)];                                  memory_order_acquire)];
82                      }                      }
83    
# Line 93  namespace LinuxSampler { Line 94  namespace LinuxSampler {
94                      }                      }
95    
96                      Reader(SynchronizedConfig& config);                      Reader(SynchronizedConfig& config);
97                      ~Reader();                      Reader(SynchronizedConfig* config);
98                        virtual ~Reader();
99                  private:                  private:
100                      friend class SynchronizedConfig;                      friend class SynchronizedConfig;
101                      SynchronizedConfig& parent;                      SynchronizedConfig* parent;
102                      int lockCount; // increased in every Lock(),                      int lockCount; // increased in every Lock(),
103                                     // lowest bit is always set.                                     // lowest bit is always set.
104                      atomic<int> lock; // equals lockCount when inside                      atomic<int> lock; // equals lockCount when inside
# Line 188  namespace LinuxSampler { Line 190  namespace LinuxSampler {
190    
191      template <class T>      template <class T>
192      SynchronizedConfig<T>::Reader::Reader(SynchronizedConfig& config) :      SynchronizedConfig<T>::Reader::Reader(SynchronizedConfig& config) :
193            parent(&config), lock(0), lockCount(1) {
194            parent->readers.insert(this);
195        }
196        
197        template <class T>
198        SynchronizedConfig<T>::Reader::Reader(SynchronizedConfig* config) :
199          parent(config), lock(0), lockCount(1) {          parent(config), lock(0), lockCount(1) {
200          parent.readers.insert(this);          parent->readers.insert(this);
201      }      }
202    
203      template <class T>      template <class T>
204      SynchronizedConfig<T>::Reader::~Reader() {      SynchronizedConfig<T>::Reader::~Reader() {
205          parent.readers.erase(this);          parent->readers.erase(this);
206      }      }
207        
208        
209        // ----- Convenience classes on top of SynchronizedConfig ----
210    
211    
212        /**
213         * Base interface class for classes that implement synchronization of data
214         * shared between multiple threads.
215         */
216        template<class T>
217        class Synchronizer {
218        public:
219            /**
220             * Signal intention to enter a synchronized code block. Depending
221             * on the actual implementation, this call may block the calling
222             * thread until it is safe to actually use the protected data. After
223             * this call returns, it is safe for the calling thread to access and
224             * modify the shared data. As soon as the thread is done with accessing
225             * the shared data, it MUST call endSync().
226             *
227             * @return the shared protected data
228             */
229            virtual T& beginSync() = 0; //TODO: or call it lock() instead ?
230                
231            /**
232             * Signal that the synchronized code block has been left. Depending
233             * on the actual implementation, this call may block the calling
234             * thread for a certain amount of time.
235             */
236            virtual void endSync() = 0; //TODO: or call it unlock() instead ?
237        };
238    
239        /**
240         * Wraps as a kind of pointer class some data object shared with other
241         * threads, to protect / synchronize the shared data against
242         * undeterministic concurrent access. It does so by locking the shared
243         * data in the Sync constructor and unlocking the shared data in the Sync
244         * destructor. Accordingly it can always be considered safe to access the
245         * shared data during the whole life time of the Sync object. Due to
246         * this design, a Sync object MUST only be accessed and destroyed
247         * by exactly one and the same thread which created that same Sync object.
248         */
249        template<class T>
250        class Sync {
251        public:
252            Sync(Synchronizer<T>* syncer) {
253                this->syncer = syncer;
254                this->data = &syncer->beginSync();
255            }
256            
257            virtual ~Sync() {
258                syncer->endSync();
259            }
260            
261            Sync& operator =(const Sync& arg) {
262                *this->data = *arg.data;
263                return *this;
264            }
265    
266            Sync& operator =(const T& arg) {
267                *this->data = arg;
268                return *this;
269            }
270            
271            const T& operator *() const { return *data; }
272            T&       operator *()       { return *data; }
273    
274            const T* operator ->() const { return data; }
275            T*       operator ->()       { return data; }
276    
277        private:
278            Synchronizer<T>* syncer; ///< Points to the object that shall be responsible to protect the shared data.
279            T* data; ///< Points to the shared data that should be protected.
280        };
281    
282        /**
283         * BackBuffer object to be accessed by multiple non-real-time threads.
284         *
285         * Since a back buffer is designed for being accessed by non-real-time
286         * threads, its methods involved may block the calling thread for a long
287         * amount of time.
288         */
289        template<class T>
290        class BackBuffer : public SynchronizedConfig<T>, public Synchronizer<T> {
291        public:
292            virtual T& beginSync() OVERRIDE {
293                mutex.Lock();
294                data = &SynchronizedConfig<T>::GetConfigForUpdate();
295                return *data;
296            }
297    
298            virtual void endSync() OVERRIDE {
299                const T clone = *data;
300                SynchronizedConfig<T>::SwitchConfig() = clone;
301                mutex.Unlock();
302            }
303    
304        private:
305            T* data;
306            Mutex mutex;
307        };
308    
309        /**
310         * FrontBuffer object to be accessed by exactly ONE real-time thread.
311         * A front buffer is designed for real-time access. That is, its methods
312         * involved are lock free, that is none of them block the calling thread
313         * for a long time.
314         *
315         * If you need the front buffer's data to be accessed by multiple real-time
316         * threads instead, then you need to create multiple instances of the
317         * FrontBuffer object. They would point to the same data, but ensure
318         * protection against concurrent access among those real-time threads.
319         */
320        template<class T>
321        class FrontBuffer : public SynchronizedConfig<T>::Reader, public Synchronizer<T> {
322        public:
323            FrontBuffer(BackBuffer<T>& backBuffer) : SynchronizedConfig<T>::Reader::Reader(&backBuffer) {}
324            virtual T& beginSync() OVERRIDE { return SynchronizedConfig<T>::Reader::Lock(); }
325            virtual void endSync() OVERRIDE { SynchronizedConfig<T>::Reader::Unlock(); }
326        };
327    
328        /**
329         * Synchronization / protection of data shared between multiple threads by
330         * using a double buffer design. The FrontBuffer is meant to be accessed by
331         * exactly one real-time thread, whereas the BackBuffer is meant to be
332         * accessed by multiple non-real-time threads.
333         *
334         * This class is built on top of SynchronizedConfig as convenient API to
335         * reduce the amount of code required to protect shared data.
336         */
337        template<class T>
338        class DoubleBuffer {
339        public:
340            DoubleBuffer() : m_front(m_back) {}
341            
342            /**
343             * Synchronized access of the shared data for EXACTLY one real-time
344             * thread.
345             *
346             * The returned shared data is wrapped into a Sync object, which
347             * ensures that the shared data is protected against concurrent access
348             * during the life time of the returned Sync object.
349             */
350            inline
351            Sync<T> front() { return Sync<T>(&m_front); }
352            
353            /**
354             * Synchronized access of the shared data for multiple non-real-time
355             * threads.
356             *
357             * The returned shared data is wrapped into a Sync object, which
358             * ensures that the shared data is protected against concurrent access
359             * during the life time of the returned Sync object.
360             *
361             * As soon as the returned Sync object is destroyed, the FrontBuffer
362             * will automatically be exchanged by the hereby modified BackBuffer.
363             */
364            inline
365            Sync<T> back() { return Sync<T>(&m_back); }
366    
367        private:
368            BackBuffer<T> m_back; ///< Back buffer (non real-time thread(s) side).
369            FrontBuffer<T> m_front; ///< Front buffer (real-time thread side).
370        };
371    
372  } // namespace LinuxSampler  } // namespace LinuxSampler
373    

Legend:
Removed from v.2452  
changed lines
  Added in v.2453

  ViewVC Help
Powered by ViewVC