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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1790 - (show annotations) (download) (as text)
Sun Nov 2 12:05:00 2008 UTC (15 years, 5 months ago) by persson
File MIME type: text/x-c++hdr
File size: 7918 byte(s)
* added memory ordering constraints to improve stability on multi-core
  and multi-cpu systems

1 /***************************************************************************
2 * *
3 * Copyright (C) 2006-2008 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
28 namespace LinuxSampler {
29
30 /**
31 * Thread safe management of configuration data, where the data is
32 * updated by a single non real time thread and read by a number
33 * of real time threads.
34 *
35 * The synchronization is achieved by using two instances of the
36 * configuration data. The non real time thread gets access to the
37 * instance not currently in use by the real time threads by
38 * 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 * The real time threads need one Reader object each to access the
44 * 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 * reading the data. (Neither Lock nor Unlock will block the real
48 * time thread, or use any system calls.)
49 */
50 template<class T>
51 class SynchronizedConfig {
52 public:
53 SynchronizedConfig();
54
55 // methods for the real time thread
56
57 class Reader {
58 public:
59 /**
60 * Gets the configuration object for use by the
61 * real time thread. The object is safe to use
62 * (read only) until Unlock() is called.
63 *
64 * @returns a reference to the configuration
65 * object to be read by the real time
66 * thread
67 */
68 const T& Lock() {
69 lock.store(1, memory_order_relaxed);
70 atomic_thread_fence(memory_order_seq_cst);
71 return parent.config[parent.indexAtomic.load(
72 memory_order_acquire)];
73 }
74
75 /**
76 * Unlock the configuration object. Unlock() must
77 * be called by the real time thread after it has
78 * finished reading the configuration object. If
79 * the non real time thread is waiting in
80 * SwitchConfig() it will be awaken when no real
81 * time threads are locked anymore.
82 */
83 void Unlock() {
84 atomic_thread_fence(memory_order_release);
85 lock.store(0, memory_order_relaxed);
86 flag.store(0, memory_order_relaxed);
87 }
88
89 Reader(SynchronizedConfig& config);
90 ~Reader();
91 private:
92 friend class SynchronizedConfig;
93 SynchronizedConfig& parent;
94 atomic<int> lock;
95 atomic<int> flag;
96 Reader *next; // only used locally in SwitchConfig
97 };
98
99
100 // methods for the non real time thread
101
102 /**
103 * Gets the configuration object for use by the non real
104 * time thread. The object returned is not in use by the
105 * real time thread, so it can safely be updated. After
106 * the update is done, the non real time thread must call
107 * SwitchConfig() and the same update must be done again.
108 *
109 * @returns a reference to the configuration object to be
110 * updated by the non real time thread
111 */
112 T& GetConfigForUpdate();
113
114 /**
115 * Atomically switch the newly updated configuration
116 * object with the one used by the real time thread, then
117 * wait for the real time thread to finish working with
118 * the old object before returning the old object.
119 * SwitchConfig() must be called by the non real time
120 * thread after an update has been done, and the object
121 * returned must be updated in the same way as the first.
122 *
123 * @returns a reference to the configuration object to be
124 * updated by the non real time thread
125 */
126 T& SwitchConfig();
127
128 private:
129 atomic<int> indexAtomic;
130 int updateIndex;
131 T config[2];
132 std::set<Reader*> readers;
133 };
134
135 template<class T> SynchronizedConfig<T>::SynchronizedConfig() :
136 indexAtomic(0) {
137 updateIndex = 1;
138 }
139
140 template<class T> T& SynchronizedConfig<T>::GetConfigForUpdate() {
141 return config[updateIndex];
142 }
143
144 template<class T> T& SynchronizedConfig<T>::SwitchConfig() {
145 indexAtomic.store(updateIndex, memory_order_release);
146 atomic_thread_fence(memory_order_seq_cst);
147
148 // first put all locking readers in a linked list
149 Reader* lockingReaders = 0;
150 for (typename std::set<Reader*>::iterator iter = readers.begin() ;
151 iter != readers.end() ;
152 iter++) {
153 (*iter)->flag.store(1, memory_order_relaxed);
154 if ((*iter)->lock.load(memory_order_acquire) &&
155 (*iter)->flag.load(memory_order_acquire)) {
156 (*iter)->next = lockingReaders;
157 lockingReaders = *iter;
158 }
159 }
160
161 // wait until there are no locking readers left
162 while (lockingReaders) {
163 usleep(50000);
164 Reader** prev = &lockingReaders;
165 for (Reader* p = lockingReaders ; p ; p = p->next) {
166 if (p->lock.load(memory_order_acquire) &&
167 p->flag.load(memory_order_acquire)) prev = &p->next;
168 else *prev = p->next; // unlink
169 }
170 }
171
172 updateIndex ^= 1;
173 return config[updateIndex];
174 }
175
176
177 // ----- Reader ----
178
179 template <class T>
180 SynchronizedConfig<T>::Reader::Reader(SynchronizedConfig& config) :
181 parent(config), lock(0), flag(0) {
182 parent.readers.insert(this);
183 }
184
185 template <class T>
186 SynchronizedConfig<T>::Reader::~Reader() {
187 parent.readers.erase(this);
188 }
189
190 } // namespace LinuxSampler
191
192 #endif

  ViewVC Help
Powered by ViewVC