Update Changelog
[jack2.git] / common / JackAtomicState.h
blob6f90d66a3828b3b860fc04a7207703210d5d1f5d
1 /*
2 Copyright (C) 2004-2008 Grame
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU Lesser General Public License as published by
6 the Free Software Foundation; either version 2.1 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU Lesser General Public License for more details.
14 You should have received a copy of the GNU Lesser General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 #ifndef __JackAtomicState__
21 #define __JackAtomicState__
23 #include "JackAtomic.h"
24 #include "JackCompilerDeps.h"
25 #include <string.h> // for memcpy
27 namespace Jack
30 /*!
31 \brief Counter for CAS
34 PRE_PACKED_STRUCTURE
35 struct AtomicCounter
37 union {
38 struct {
39 UInt16 fShortVal1; // Cur
40 UInt16 fShortVal2; // Next
42 scounter;
43 UInt32 fLongVal;
44 }info;
46 AtomicCounter()
48 info.fLongVal = 0;
51 AtomicCounter(volatile const AtomicCounter& obj)
53 info.fLongVal = obj.info.fLongVal;
56 AtomicCounter(volatile AtomicCounter& obj)
58 info.fLongVal = obj.info.fLongVal;
61 AtomicCounter& operator=(AtomicCounter& obj)
63 info.fLongVal = obj.info.fLongVal;
64 return *this;
67 AtomicCounter& operator=(volatile AtomicCounter& obj)
69 info.fLongVal = obj.info.fLongVal;
70 return *this;
73 } POST_PACKED_STRUCTURE;
75 #define Counter(e) (e).info.fLongVal
76 #define CurIndex(e) (e).info.scounter.fShortVal1
77 #define NextIndex(e) (e).info.scounter.fShortVal2
79 #define CurArrayIndex(e) (CurIndex(e) & 0x0001)
80 #define NextArrayIndex(e) ((CurIndex(e) + 1) & 0x0001)
82 /*!
83 \brief A class to handle two states (switching from one to the other) in a lock-free manner
86 // CHECK livelock
88 PRE_PACKED_STRUCTURE
89 template <class T>
90 class JackAtomicState
93 protected:
95 T fState[2];
96 volatile AtomicCounter fCounter;
97 SInt32 fCallWriteCounter;
99 UInt32 WriteNextStateStartAux()
101 AtomicCounter old_val;
102 AtomicCounter new_val;
103 UInt32 cur_index;
104 UInt32 next_index;
105 bool need_copy;
106 do {
107 old_val = fCounter;
108 new_val = old_val;
109 cur_index = CurArrayIndex(new_val);
110 next_index = NextArrayIndex(new_val);
111 need_copy = (CurIndex(new_val) == NextIndex(new_val));
112 NextIndex(new_val) = CurIndex(new_val); // Invalidate next index
113 } while (!CAS(Counter(old_val), Counter(new_val), (UInt32*)&fCounter));
114 if (need_copy)
115 memcpy(&fState[next_index], &fState[cur_index], sizeof(T));
116 return next_index;
119 void WriteNextStateStopAux()
121 AtomicCounter old_val;
122 AtomicCounter new_val;
123 do {
124 old_val = fCounter;
125 new_val = old_val;
126 NextIndex(new_val)++; // Set next index
127 } while (!CAS(Counter(old_val), Counter(new_val), (UInt32*)&fCounter));
130 public:
132 JackAtomicState()
134 Counter(fCounter) = 0;
135 fCallWriteCounter = 0;
138 ~JackAtomicState() // Not virtual ??
142 \brief Returns the current state : only valid in the RT reader thread
144 T* ReadCurrentState()
146 return &fState[CurArrayIndex(fCounter)];
150 \brief Returns the current state index
152 UInt16 GetCurrentIndex()
154 return CurIndex(fCounter);
158 \brief Tries to switch to the next state and returns the new current state (either the same as before if case of switch failure or the new one)
160 T* TrySwitchState()
162 AtomicCounter old_val;
163 AtomicCounter new_val;
164 do {
165 old_val = fCounter;
166 new_val = old_val;
167 CurIndex(new_val) = NextIndex(new_val); // Prepare switch
168 } while (!CAS(Counter(old_val), Counter(new_val), (UInt32*)&fCounter));
169 return &fState[CurArrayIndex(fCounter)]; // Read the counter again
173 \brief Tries to switch to the next state and returns the new current state (either the same as before if case of switch failure or the new one)
175 T* TrySwitchState(bool* result)
177 AtomicCounter old_val;
178 AtomicCounter new_val;
179 do {
180 old_val = fCounter;
181 new_val = old_val;
182 *result = (CurIndex(new_val) != NextIndex(new_val));
183 CurIndex(new_val) = NextIndex(new_val); // Prepare switch
184 } while (!CAS(Counter(old_val), Counter(new_val), (UInt32*)&fCounter));
185 return &fState[CurArrayIndex(fCounter)]; // Read the counter again
189 \brief Start write operation : setup and returns the next state to update, check for recursive write calls.
191 T* WriteNextStateStart()
193 UInt32 next_index = (fCallWriteCounter++ == 0)
194 ? WriteNextStateStartAux()
195 : NextArrayIndex(fCounter); // We are inside a wrapping WriteNextStateStart call, NextArrayIndex can be read safely
196 return &fState[next_index];
200 \brief Stop write operation : make the next state ready to be used by the RT thread
202 void WriteNextStateStop()
204 if (--fCallWriteCounter == 0)
205 WriteNextStateStopAux();
208 bool IsPendingChange()
210 return CurIndex(fCounter) != NextIndex(fCounter);
214 // Single writer : write methods get the *next* state to be updated
215 void TestWriteMethod()
217 T* state = WriteNextStateStart();
218 ......
219 ......
220 WriteNextStateStop();
223 // First RT call possibly switch state
224 void TestReadRTMethod1()
226 T* state = TrySwitchState();
227 ......
228 ......
231 // Other RT methods can safely use the current state during the *same* RT cycle
232 void TestReadRTMethod2()
234 T* state = ReadCurrentState();
235 ......
236 ......
239 // Non RT read methods : must check state coherency
240 void TestReadMethod()
242 T* state;
243 UInt16 cur_index;
244 UInt16 next_index = GetCurrentIndex();
245 do {
246 cur_index = next_index;
247 state = ReadCurrentState();
249 ......
250 ......
252 next_index = GetCurrentIndex();
253 } while (cur_index != next_index);
257 } POST_PACKED_STRUCTURE;
259 } // end of namespace
261 #endif