/[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 1038 - (show annotations) (download) (as text)
Sat Feb 3 15:33:00 2007 UTC (17 years, 2 months ago) by persson
File MIME type: text/x-c++hdr
File size: 7339 byte(s)
* playback is no longer disabled during instrument loading
* all notes playing on a channel that changes its instrument keep
  playing with the old instrument until they get a note off command
* new thread safety fix for lscp "load engine" and "set channel audio
  output device"

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

  ViewVC Help
Powered by ViewVC