linuxsampler 2.3.1
SynchronizedConfig.h
Go to the documentation of this file.
1/***************************************************************************
2 * *
3 * Copyright (C) 2006-2016 Andreas Persson *
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
9 * *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the Free Software *
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, *
18 * MA 02110-1301 USA *
19 ***************************************************************************/
20
21#ifndef SYNCHRONIZEDCONFIG_H
22#define SYNCHRONIZEDCONFIG_H
23
24#include <set>
25#include <unistd.h>
26#include "lsatomic.h"
27#include "Mutex.h"
28
29namespace LinuxSampler {
30
59 template<class T>
61 public:
63
64 // methods for the real time thread
65
66 class Reader {
67 public:
77 /*const*/ T& Lock() { //TODO const currently commented for the DoubleBuffer usage below
78 lock.store(lockCount += 2, memory_order_relaxed);
80 return parent->config[parent->indexAtomic.load(
82 }
83
92 void Unlock() {
94 }
95
96 Reader(SynchronizedConfig& config);
97 Reader(SynchronizedConfig* config);
98 virtual ~Reader();
99 private:
100 friend class SynchronizedConfig;
101 SynchronizedConfig* parent;
102 int lockCount; // increased in every Lock(),
103 // lowest bit is always set.
104 atomic<int> lock; // equals lockCount when inside
105 // critical region, otherwise 0
106 Reader* next; // only used locally in SwitchConfig
107 int prevLock; // only used locally in SwitchConfig
108 };
109
110
111 // methods for the non real time thread
112
124
128 const T& GetUnsafeUpdateConfig() const {
129 return config[updateIndex];
130 }
131
145
146 private:
147 atomic<int> indexAtomic;
148 int updateIndex;
149 T config[2];
150 std::set<Reader*> readers;
151 };
152
154 indexAtomic(0) {
155 updateIndex = 1;
156 }
157
159 return config[updateIndex];
160 }
161
162 template<class T> T& SynchronizedConfig<T>::SwitchConfig() {
163 indexAtomic.store(updateIndex, memory_order_release);
165
166 // first put all locking readers in a linked list
167 Reader* lockingReaders = 0;
168 for (typename std::set<Reader*>::iterator iter = readers.begin() ;
169 iter != readers.end() ;
170 iter++) {
171 (*iter)->prevLock = (*iter)->lock.load(memory_order_acquire);
172 if ((*iter)->prevLock) {
173 (*iter)->next = lockingReaders;
174 lockingReaders = *iter;
175 }
176 }
177
178 // wait until there are no locking readers left
179 while (lockingReaders) {
180 usleep(50000);
181 Reader** prev = &lockingReaders;
182 for (Reader* p = lockingReaders ; p ; p = p->next) {
183 if (p->lock.load(memory_order_acquire) == p->prevLock) {
184 prev = &p->next;
185 } else {
186 *prev = p->next; // unlink
187 }
188 }
189 }
190
191 updateIndex ^= 1;
192 return config[updateIndex];
193 }
194
195
196 // ----- Reader ----
197
198 template <class T>
200 parent(&config), lockCount(1), lock(0) {
201 parent->readers.insert(this);
202 }
203
204 template <class T>
206 parent(config), lockCount(1), lock(0) {
207 parent->readers.insert(this);
208 }
209
210 template <class T>
212 parent->readers.erase(this);
213 }
214
215
216 // ----- Convenience classes on top of SynchronizedConfig ----
217
218
223 template<class T>
225 public:
236 virtual void beginSync() = 0; //TODO: or call it lock() instead ?
237
243 virtual T& syncedData() = 0;
244
250 virtual void endSync() = 0; //TODO: or call it unlock() instead ?
251 };
252
263 template<class T>
264 class Sync {
265 public:
267 this->syncer = syncer;
268 syncer->beginSync();
269 }
270
271 virtual ~Sync() {
272 syncer->endSync();
273 }
274
275 /*Sync& operator =(const Sync& arg) {
276 *this->data = *arg.data;
277 return *this;
278 }*/
279
280 /*Sync& operator =(const T& arg) {
281 *this->data = arg;
282 return *this;
283 }*/
284
285 const T& operator *() const { return syncer->syncedData(); }
286 T& operator *() { return syncer->syncedData(); }
287
288 const T* operator ->() const { return &syncer->syncedData(); }
289 T* operator ->() { return &syncer->syncedData(); }
290
291 private:
292 Synchronizer<T>* syncer;
293 };
294
302 template<class T>
303 class BackBuffer : public SynchronizedConfig<T>, public Synchronizer<T> {
304 public:
305 virtual void beginSync() OVERRIDE {
306 mutex.Lock();
307 }
308
312
313 virtual void endSync() OVERRIDE {
316 mutex.Unlock();
317 }
318
319 const T& unsafeData() const {
321 }
322
323 private:
324 Mutex mutex;
325 };
326
338 template<class T>
339 class FrontBuffer : public SynchronizedConfig<T>::Reader, public Synchronizer<T> {
340 public:
341 FrontBuffer(BackBuffer<T>& backBuffer) : SynchronizedConfig<T>::Reader::Reader(&backBuffer) {}
343 virtual T& syncedData() OVERRIDE { return *data; }
345 private:
346 T* data;
347 };
348
358 template<class T>
360 public:
361 DoubleBuffer() : m_front(m_back) {}
362
371 inline
372 Sync<T> front() { return Sync<T>(&m_front); }
373
385 inline
386 Sync<T> back() { return Sync<T>(&m_back); }
387
399 const T& unsafeBack() const { return m_back.unsafeData(); }
400
401 private:
402 BackBuffer<T> m_back;
403 FrontBuffer<T> m_front;
404 };
405
406} // namespace LinuxSampler
407
408#endif
BackBuffer object to be accessed by multiple non-real-time threads.
virtual void endSync() OVERRIDE
Signal that the synchronized code block has been left.
virtual T & syncedData() OVERRIDE
Retrieve reference to critical, shared data.
virtual void beginSync() OVERRIDE
Signal intention to enter a synchronized code block.
Synchronization / protection of data shared between multiple threads by using a double buffer design.
Sync< T > back()
Synchronized access of the shared data for multiple non-real-time threads.
const T & unsafeBack() const
Get the backbuffer data unprotected, that is without locking or any means of synchronizations.
Sync< T > front()
Synchronized access of the shared data for EXACTLY one real-time thread.
FrontBuffer object to be accessed by exactly ONE real-time thread.
virtual void endSync() OVERRIDE
Signal that the synchronized code block has been left.
FrontBuffer(BackBuffer< T > &backBuffer)
virtual T & syncedData() OVERRIDE
Retrieve reference to critical, shared data.
virtual void beginSync() OVERRIDE
Signal intention to enter a synchronized code block.
Wraps as a kind of pointer class some data object shared with other threads, to protect / synchronize...
Sync(Synchronizer< T > *syncer)
void Unlock()
Unlock the configuration object.
T & Lock()
Gets the configuration object for use by the real time thread.
Thread-safe management of configuration data, where the data is updated by a single non real time thr...
T & GetConfigForUpdate()
Gets the configuration object for use by the non real time thread.
T & SwitchConfig()
Atomically switch the newly updated configuration object with the one used by the real time thread,...
const T & GetUnsafeUpdateConfig() const
Get the data on update side for read-only access.
Base interface class for classes that implement synchronization of data shared between multiple threa...
virtual void beginSync()=0
Signal intention to enter a synchronized code block.
virtual void endSync()=0
Signal that the synchronized code block has been left.
virtual T & syncedData()=0
Retrieve reference to critical, shared data.
int load(memory_order order=memory_order_seq_cst) const volatile
Definition lsatomic.h:146
void store(int m, memory_order order=memory_order_seq_cst) volatile
Definition lsatomic.h:180
#define OVERRIDE
Definition global.h:84
Implementation of a small subset of the C++11 atomic operations.
@ memory_order_acquire
Definition lsatomic.h:103
@ memory_order_relaxed
Definition lsatomic.h:103
@ memory_order_release
Definition lsatomic.h:104
@ memory_order_seq_cst
Definition lsatomic.h:104
void atomic_thread_fence(memory_order order)
Definition lsatomic.h:107