Compiles on Windows again.
[jack2.git] / common / JackAtomicArrayState.h
blob8a0daf080c0cea811dcdc9e10f6e8b47757d8b31
1 /*
2 Copyright (C) 2004-2006 Grame
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 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 General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 #ifndef __JackAtomicArrayState__
21 #define __JackAtomicArrayState__
23 #include "JackAtomic.h"
24 #include "JackCompilerDeps.h"
25 #include <string.h> // for memcpy
27 namespace Jack
30 /*!
31 \brief Counter for CAS
34 struct AtomicArrayCounter
36 union {
37 struct {
38 unsigned char fByteVal[4];
40 scounter;
41 UInt32 fLongVal;
42 }info;
44 AtomicArrayCounter()
46 info.fLongVal = 0;
49 AtomicArrayCounter(volatile const AtomicArrayCounter& obj)
51 info.fLongVal = obj.info.fLongVal;
54 AtomicArrayCounter(volatile AtomicArrayCounter& obj)
56 info.fLongVal = obj.info.fLongVal;
59 AtomicArrayCounter& operator=(volatile AtomicArrayCounter& obj)
61 info.fLongVal = obj.info.fLongVal;
62 return *this;
65 AtomicArrayCounter& operator=(AtomicArrayCounter& obj)
67 info.fLongVal = obj.info.fLongVal;
68 return *this;
71 } POST_PACKED_STRUCTURE;
73 #define Counter1(e) (e).info.fLongVal
74 #define GetIndex1(e, state) ((e).info.scounter.fByteVal[state])
75 #define SetIndex1(e, state, val) ((e).info.scounter.fByteVal[state] = val)
76 #define IncIndex1(e, state) ((e).info.scounter.fByteVal[state]++)
77 #define SwapIndex1(e, state) (((e).info.scounter.fByteVal[0] == state) ? 0 : state)
79 /*!
80 \brief A class to handle several states in a lock-free manner
82 Requirement:
84 - a "current" state
85 - several possible "pending" state
86 - an TrySwitchState(int state) operation to atomically switch a "pending" to the "current" state (the pending becomes the current).
88 The TrySwitchState operation returns a "current" state (either the same if switch fails or the new one, one can know if the switch has succeeded)
90 - a WriteNextStartState(int state) returns a "pending" state to be written into
91 - a WriteNextStartStop(int state) make the written "pending" state become "switchable"
93 Different pending states can be written independantly and concurrently.
95 GetCurrentIndex() *must* return an increasing value to be able to check reading current state coherency
97 The fCounter is an array of indexes to access the current and 3 different "pending" states.
99 WriteNextStateStart(int index) must return a valid state to be written into, and must invalidate state "index" ==> cur state switch.
100 WriteNextStateStop(int index) makes the "index" state become "switchable" with the current state.
101 TrySwitchState(int index) must detect that pending state is a new state, and does the switch
102 ReadCurrentState() must return the state
103 GetCurrentIndex() must return an index increased each new switch.
104 WriteNextStateStart(int index1) and WriteNextStateStart(int index2) can be interleaved
106 [switch counter][index state][index state][cur index]
110 // CHECK livelock
112 template <class T>
113 class JackAtomicArrayState
116 protected:
118 // fState[0] ==> current
119 // fState[1] ==> pending
120 // fState[2] ==> request
122 T fState[3];
123 volatile AtomicArrayCounter fCounter;
125 UInt32 WriteNextStateStartAux(int state, bool* result)
127 AtomicArrayCounter old_val;
128 AtomicArrayCounter new_val;
129 UInt32 cur_index;
130 UInt32 next_index;
131 bool need_copy;
132 do {
133 old_val = fCounter;
134 new_val = old_val;
135 *result = GetIndex1(new_val, state);
136 cur_index = GetIndex1(new_val, 0);
137 next_index = SwapIndex1(fCounter, state);
138 need_copy = (GetIndex1(new_val, state) == 0); // Written = false, switch just occured
139 SetIndex1(new_val, state, 0); // Written = false, invalidate state
140 } while (!CAS(Counter1(old_val), Counter1(new_val), (UInt32*)&fCounter));
141 if (need_copy)
142 memcpy(&fState[next_index], &fState[cur_index], sizeof(T));
143 return next_index;
146 void WriteNextStateStopAux(int state)
148 AtomicArrayCounter old_val;
149 AtomicArrayCounter new_val;
150 do {
151 old_val = fCounter;
152 new_val = old_val;
153 SetIndex1(new_val, state, 1); // Written = true, state becomes "switchable"
154 } while (!CAS(Counter1(old_val), Counter1(new_val), (UInt32*)&fCounter));
157 public:
159 JackAtomicArrayState()
161 Counter1(fCounter) = 0;
164 ~JackAtomicArrayState() // Not virtual ??
168 \brief Returns the current state : only valid in the RT reader thread
171 T* ReadCurrentState()
173 return &fState[GetIndex1(fCounter, 0)];
177 \brief Returns the current switch counter
180 UInt16 GetCurrentIndex()
182 return GetIndex1(fCounter, 3);
186 \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)
189 T* TrySwitchState(int state)
191 AtomicArrayCounter old_val;
192 AtomicArrayCounter new_val;
193 do {
194 old_val = fCounter;
195 new_val = old_val;
196 if (GetIndex1(new_val, state)) { // If state has been written
197 SetIndex1(new_val, 0, SwapIndex1(new_val, state)); // Prepare switch
198 SetIndex1(new_val, state, 0); // Invalidate the state "state"
199 IncIndex1(new_val, 3); // Inc switch
201 } while (!CAS(Counter1(old_val), Counter1(new_val), (UInt32*)&fCounter));
202 return &fState[GetIndex1(fCounter, 0)]; // Read the counter again
206 \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)
209 T* TrySwitchState(int state, bool* result)
211 AtomicArrayCounter old_val;
212 AtomicArrayCounter new_val;
213 do {
214 old_val = fCounter;
215 new_val = old_val;
216 if ((*result = GetIndex1(new_val, state))) { // If state has been written
217 SetIndex1(new_val, 0, SwapIndex1(new_val, state)); // Prepare switch
218 SetIndex1(new_val, state, 0); // Invalidate the state "state"
219 IncIndex1(new_val, 3); // Inc switch
221 } while (!CAS(Counter1(old_val), Counter1(new_val), (UInt32*)&fCounter));
222 return &fState[GetIndex1(fCounter, 0)]; // Read the counter again
226 \brief Start write operation : setup and returns the next state to update, check for recursive write calls.
229 T* WriteNextStateStart(int state)
231 bool tmp;
232 UInt32 index = WriteNextStateStartAux(state, &tmp);
233 return &fState[index];
236 T* WriteNextStateStart(int state, bool* result)
238 UInt32 index = WriteNextStateStartAux(state, result);
239 return &fState[index];
243 \brief Stop write operation : make the next state ready to be used by the RT thread
246 void WriteNextStateStop(int state)
248 WriteNextStateStopAux(state);
251 } POST_PACKED_STRUCTURE;
253 } // end of namespace
256 #endif