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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2343 - (hide annotations) (download) (as text)
Sun Apr 29 16:14:45 2012 UTC (12 years, 1 month ago) by persson
File MIME type: text/x-c++hdr
File size: 8427 byte(s)
* fixed configure script error with old autoconf versions
* LV2: use the new lv2 package if present
* lsatomic.h: use gcc provided atomic functions if building with gcc
  4.7 and C++11
* added comments in lsatomic.h

1 persson 840 /***************************************************************************
2     * *
3 persson 2343 * Copyright (C) 2006-2012 Andreas Persson *
4 persson 840 * *
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 persson 1790 #ifndef SYNCHRONIZEDCONFIG_H
22     #define SYNCHRONIZEDCONFIG_H
23 persson 840
24 persson 846 #include <set>
25 persson 1790 #include <unistd.h>
26     #include "lsatomic.h"
27 persson 840
28     namespace LinuxSampler {
29    
30     /**
31 persson 2343 * Thread-safe management of configuration data, where the data is
32 persson 846 * updated by a single non real time thread and read by a number
33     * of real time threads.
34 persson 840 *
35     * The synchronization is achieved by using two instances of the
36     * configuration data. The non real time thread gets access to the
37 persson 846 * instance not currently in use by the real time threads by
38 persson 840 * calling GetConfigForUpdate(). After the data is updated, the
39     * non real time thread must call SwitchConfig() and redo the
40     * update on the other instance. SwitchConfig() blocks until it is
41     * safe to modify the other instance.
42     *
43 persson 846 * The real time threads need one Reader object each to access the
44 persson 1038 * configuration data. This object must be created outside the
45     * real time thread. The Lock() function returns a reference to
46     * the data to be read, and Unlock() must be called when finished
47 persson 846 * reading the data. (Neither Lock nor Unlock will block the real
48     * time thread, or use any system calls.)
49 persson 2343 *
50     * Note that the non real time side isn't safe for concurrent
51     * access, so if there are multiple non real time threads that
52     * update the configuration data, a mutex has to be used.
53     *
54     * Implementation note: the memory order parameters and fences are
55     * very carefully chosen to make the code fast but still safe for
56     * memory access reordering made by the CPU.
57 persson 840 */
58     template<class T>
59     class SynchronizedConfig {
60     public:
61     SynchronizedConfig();
62    
63     // methods for the real time thread
64    
65 persson 846 class Reader {
66     public:
67     /**
68     * Gets the configuration object for use by the
69     * real time thread. The object is safe to use
70     * (read only) until Unlock() is called.
71     *
72     * @returns a reference to the configuration
73     * object to be read by the real time
74     * thread
75     */
76     const T& Lock() {
77 persson 1887 lock.store(lockCount += 2, memory_order_relaxed);
78 persson 1790 atomic_thread_fence(memory_order_seq_cst);
79     return parent.config[parent.indexAtomic.load(
80     memory_order_acquire)];
81 persson 846 }
82 persson 840
83 persson 846 /**
84     * Unlock the configuration object. Unlock() must
85     * be called by the real time thread after it has
86     * finished reading the configuration object. If
87     * the non real time thread is waiting in
88     * SwitchConfig() it will be awaken when no real
89     * time threads are locked anymore.
90     */
91     void Unlock() {
92 persson 1887 lock.store(0, memory_order_release);
93 persson 846 }
94 persson 840
95 persson 846 Reader(SynchronizedConfig& config);
96     ~Reader();
97     private:
98     friend class SynchronizedConfig;
99     SynchronizedConfig& parent;
100 persson 1887 int lockCount; // increased in every Lock(),
101     // lowest bit is always set.
102     atomic<int> lock; // equals lockCount when inside
103     // critical region, otherwise 0
104     Reader* next; // only used locally in SwitchConfig
105     int prevLock; // only used locally in SwitchConfig
106 persson 846 };
107 persson 840
108 persson 846
109 persson 840 // methods for the non real time thread
110    
111     /**
112     * Gets the configuration object for use by the non real
113     * time thread. The object returned is not in use by the
114     * real time thread, so it can safely be updated. After
115     * the update is done, the non real time thread must call
116     * SwitchConfig() and the same update must be done again.
117     *
118     * @returns a reference to the configuration object to be
119     * updated by the non real time thread
120     */
121     T& GetConfigForUpdate();
122    
123     /**
124     * Atomically switch the newly updated configuration
125     * object with the one used by the real time thread, then
126     * wait for the real time thread to finish working with
127     * the old object before returning the old object.
128     * SwitchConfig() must be called by the non real time
129     * thread after an update has been done, and the object
130     * returned must be updated in the same way as the first.
131     *
132     * @returns a reference to the configuration object to be
133     * updated by the non real time thread
134     */
135     T& SwitchConfig();
136    
137     private:
138 persson 1790 atomic<int> indexAtomic;
139 persson 840 int updateIndex;
140     T config[2];
141 persson 846 std::set<Reader*> readers;
142 persson 840 };
143    
144 persson 1790 template<class T> SynchronizedConfig<T>::SynchronizedConfig() :
145     indexAtomic(0) {
146 persson 846 updateIndex = 1;
147 persson 840 }
148    
149     template<class T> T& SynchronizedConfig<T>::GetConfigForUpdate() {
150     return config[updateIndex];
151     }
152    
153     template<class T> T& SynchronizedConfig<T>::SwitchConfig() {
154 persson 1790 indexAtomic.store(updateIndex, memory_order_release);
155     atomic_thread_fence(memory_order_seq_cst);
156 persson 846
157     // first put all locking readers in a linked list
158     Reader* lockingReaders = 0;
159     for (typename std::set<Reader*>::iterator iter = readers.begin() ;
160     iter != readers.end() ;
161     iter++) {
162 persson 1887 (*iter)->prevLock = (*iter)->lock.load(memory_order_acquire);
163     if ((*iter)->prevLock) {
164 persson 846 (*iter)->next = lockingReaders;
165     lockingReaders = *iter;
166     }
167     }
168    
169     // wait until there are no locking readers left
170     while (lockingReaders) {
171 persson 840 usleep(50000);
172 persson 846 Reader** prev = &lockingReaders;
173     for (Reader* p = lockingReaders ; p ; p = p->next) {
174 persson 1887 if (p->lock.load(memory_order_acquire) == p->prevLock) {
175     prev = &p->next;
176     } else {
177     *prev = p->next; // unlink
178     }
179 persson 846 }
180     }
181    
182     updateIndex ^= 1;
183     return config[updateIndex];
184 persson 840 }
185    
186 persson 846
187     // ----- Reader ----
188    
189     template <class T>
190 persson 1790 SynchronizedConfig<T>::Reader::Reader(SynchronizedConfig& config) :
191 persson 1887 parent(config), lock(0), lockCount(1) {
192 persson 846 parent.readers.insert(this);
193     }
194    
195     template <class T>
196     SynchronizedConfig<T>::Reader::~Reader() {
197     parent.readers.erase(this);
198     }
199    
200 persson 840 } // namespace LinuxSampler
201    
202     #endif

  ViewVC Help
Powered by ViewVC