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

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

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 840 by persson, Sun Feb 26 13:00:08 2006 UTC revision 1424 by schoenebeck, Sun Oct 14 22:00:17 2007 UTC
# Line 1  Line 1 
1  /***************************************************************************  /***************************************************************************
2   *                                                                         *   *                                                                         *
3   *   Copyright (C) 2006 Andreas Persson                                    *   *   Copyright (C) 2006, 2007 Andreas Persson                              *
4   *                                                                         *   *                                                                         *
5   *   This program is free software; you can redistribute it and/or modify  *   *   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  *   *   it under the terms of the GNU General Public License as published by  *
# Line 21  Line 21 
21  #ifndef __SYNCHRONIZEDCONFIG_H__  #ifndef __SYNCHRONIZEDCONFIG_H__
22  #define __SYNCHRONIZEDCONFIG_H__  #define __SYNCHRONIZEDCONFIG_H__
23    
24  #include "atomic.h"  #include <set>
25    
26  namespace LinuxSampler {  namespace LinuxSampler {
27    
28      /**      /**
29       * Thread safe management of configuration data, where the data is       * Thread safe management of configuration data, where the data is
30       * updated by a single non real time thread and read by a single       * updated by a single non real time thread and read by a number
31       * real time thread.       * of real time threads.
32       *       *
33       * The synchronization is achieved by using two instances of the       * The synchronization is achieved by using two instances of the
34       * configuration data. The non real time thread gets access to the       * configuration data. The non real time thread gets access to the
35       * instance not currently in use by the real time thread by       * instance not currently in use by the real time threads by
36       * calling GetConfigForUpdate(). After the data is updated, the       * calling GetConfigForUpdate(). After the data is updated, the
37       * non real time thread must call SwitchConfig() and redo the       * non real time thread must call SwitchConfig() and redo the
38       * update on the other instance. SwitchConfig() blocks until it is       * update on the other instance. SwitchConfig() blocks until it is
39       * safe to modify the other instance.       * safe to modify the other instance.
40       *       *
41       * The real time thread calls Lock() to get access to the data,       * The real time threads need one Reader object each to access the
42       * and Unlock() when it is finished reading the data. (Neither       * configuration data. This object must be created outside the
43       * Lock nor Unlock will block the real time thread, or use any       * real time thread. The Lock() function returns a reference to
44       * system calls.)       * the data to be read, and Unlock() must be called when finished
45         * reading the data. (Neither Lock nor Unlock will block the real
46         * time thread, or use any system calls.)
47       */       */
48      template<class T>      template<class T>
49      class SynchronizedConfig {      class SynchronizedConfig {
50            struct atomic_t { volatile int word; };
51    
52          public:          public:
53              SynchronizedConfig();              SynchronizedConfig();
54    
55              // methods for the real time thread              // methods for the real time thread
56    
57              /**              class Reader {
58               * Gets the configuration object for use by the real time                  public:
59               * thread. The object is safe to use (read only) until                      /**
60               * Unlock() is called.                       * Gets the configuration object for use by the
61               *                       * real time thread. The object is safe to use
62               * @returns a reference to the configuration object to be                       * (read only) until Unlock() is called.
63               *          read by the real time thread                       *
64               */                       * @returns a reference to the configuration
65              const T& Lock() {                       *          object to be read by the real time
66                  atomic_set(&lock, 1);                       *          thread
67                  return config[atomic_read(&indexAtomic)];                       */
68              }                      const T& Lock() {
69                            atomicSet(&lock, 1);
70              /**                          return parent.config[atomicRead(&parent.indexAtomic)];
71               * Unlock the configuration object. Unlock() must be                      }
72               * called by the real time thread after it has finished  
73               * reading the configuration object. If the non real time                      /**
74               * thread is waiting in SwitchConfig() it will be awaken.                       * Unlock the configuration object. Unlock() must
75               */                       * be called by the real time thread after it has
76              void Unlock() {                       * finished reading the configuration object. If
77                  atomic_set(&lock, 0);                       * the non real time thread is waiting in
78              }                       * SwitchConfig() it will be awaken when no real
79                         * time threads are locked anymore.
80                         */
81                        void Unlock() {
82                            atomicSet(&lock, 0);
83                        }
84    
85                        Reader(SynchronizedConfig& config);
86                        ~Reader();
87                    private:
88                        friend class SynchronizedConfig;
89                        SynchronizedConfig& parent;
90                        atomic_t lock;
91                        Reader *next; // only used locally in SwitchConfig
92                };
93    
94    
95              // methods for the non real time thread              // methods for the non real time thread
# Line 103  namespace LinuxSampler { Line 121  namespace LinuxSampler {
121              T& SwitchConfig();              T& SwitchConfig();
122    
123          private:          private:
             atomic_t lock;  
124              atomic_t indexAtomic;              atomic_t indexAtomic;
125              int updateIndex;              int updateIndex;
126              T config[2];              T config[2];
127                std::set<Reader*> readers;
128    
129                static int atomicRead(atomic_t* pSharedVariable) {
130                    return pSharedVariable->word;
131                }
132    
133                static void atomicSet(atomic_t* pSharedVariable, int value) {
134                    pSharedVariable->word = value;
135                }
136      };      };
137    
138      template<class T> SynchronizedConfig<T>::SynchronizedConfig() {      template<class T> SynchronizedConfig<T>::SynchronizedConfig() {
139          atomic_set(&lock, 0);          atomicSet(&indexAtomic, 0);
140          atomic_set(&indexAtomic, 0);          updateIndex = 1;
141      }      }
142    
143      template<class T> T& SynchronizedConfig<T>::GetConfigForUpdate() {      template<class T> T& SynchronizedConfig<T>::GetConfigForUpdate() {
         updateIndex = atomic_read(&indexAtomic) ^ 1;  
144          return config[updateIndex];          return config[updateIndex];
145      }      }
146    
147      template<class T> T& SynchronizedConfig<T>::SwitchConfig() {      template<class T> T& SynchronizedConfig<T>::SwitchConfig() {
148          atomic_set(&indexAtomic, updateIndex);          atomicSet(&indexAtomic, updateIndex);
149          while (atomic_read(&lock))  
150            // first put all locking readers in a linked list
151            Reader* lockingReaders = 0;
152            for (typename std::set<Reader*>::iterator iter = readers.begin() ;
153                 iter != readers.end() ;
154                 iter++) {
155                if (atomicRead(&(*iter)->lock)) {
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);              usleep(50000);
164          return config[updateIndex ^ 1];              Reader** prev = &lockingReaders;
165                for (Reader* p = lockingReaders ; p ; p = p->next) {
166                    if (atomicRead(&p->lock)) prev = &p->next;
167                    else *prev = p->next; // unlink
168                }
169            }
170    
171            updateIndex ^= 1;
172            return config[updateIndex];
173        }
174    
175    
176        // ----- Reader ----
177    
178        template <class T>
179        SynchronizedConfig<T>::Reader::Reader(SynchronizedConfig& config) : parent(config) {
180            atomicSet(&lock, 0);
181            parent.readers.insert(this);
182        }
183    
184        template <class T>
185        SynchronizedConfig<T>::Reader::~Reader() {
186            parent.readers.erase(this);
187      }      }
188    
189  } // namespace LinuxSampler  } // namespace LinuxSampler

Legend:
Removed from v.840  
changed lines
  Added in v.1424

  ViewVC Help
Powered by ViewVC