/[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 846 - (hide annotations) (download) (as text)
Sun Mar 19 16:38:22 2006 UTC (18 years, 2 months ago) by persson
File MIME type: text/x-c++hdr
File size: 7337 byte(s)
* more thread safety fixes: another fix for lscp "load engine" and
  midi thread. Sysex midi protected against lscp. Instrument loader
  thread protected against lscp thread.

1 persson 840 /***************************************************************************
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 persson 846 #include <set>
26 persson 840
27     namespace LinuxSampler {
28    
29     /**
30     * Thread safe management of configuration data, where the data is
31 persson 846 * updated by a single non real time thread and read by a number
32     * of real time threads.
33 persson 840 *
34     * The synchronization is achieved by using two instances of the
35     * configuration data. The non real time thread gets access to the
36 persson 846 * instance not currently in use by the real time threads by
37 persson 840 * 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 persson 846 * The real time threads need one Reader object each to access the
43     * confuration data. This object must be created outside the real
44     * time thread. The Lock() function returns a reference to the
45     * 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 persson 840 */
49     template<class T>
50     class SynchronizedConfig {
51     public:
52     SynchronizedConfig();
53    
54     // methods for the real time thread
55    
56 persson 846 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 persson 840
72 persson 846 /**
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 persson 840
84 persson 846 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 persson 840
93 persson 846
94 persson 840 // 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 persson 846 std::set<Reader*> readers;
127 persson 840 };
128    
129     template<class T> SynchronizedConfig<T>::SynchronizedConfig() {
130     atomic_set(&indexAtomic, 0);
131 persson 846 updateIndex = 1;
132 persson 840 }
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 persson 846
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 persson 840 usleep(50000);
155 persson 846 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 persson 840 }
165    
166 persson 846
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 persson 840 } // namespace LinuxSampler
181    
182     #endif

  ViewVC Help
Powered by ViewVC