Dmitry Baikov jackmp-time patch: add jack_get_time, jack_time_to_frames, jack_frames_...
[jack2.git] / common / JackAtomicArrayState.h
blob3a9065c07a54651c78747b25e60061c0befc5e9f
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 "JackError.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& operator=(volatile AtomicArrayCounter& obj)
46 info.fLongVal = obj.info.fLongVal;
47 return *this;
51 #define Counter1(e) (e).info.fLongVal
52 #define GetIndex1(e, state) ((e).info.scounter.fByteVal[state])
53 #define SetIndex1(e, state, val) ((e).info.scounter.fByteVal[state] = val)
54 #define IncIndex1(e, state) ((e).info.scounter.fByteVal[state]++)
55 #define SwapIndex1(e, state) (((e).info.scounter.fByteVal[0] == state) ? 0 : state)
57 /*!
58 \brief A class to handle several states in a lock-free manner
60 Requirement:
62 - a "current" state
63 - several possible "pending" state
64 - an TrySwitchState(int state) operation to atomically switch a "pending" to the "current" state (the pending becomes the current).
66 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)
68 - a WriteNextStartState(int state) returns a "pending" state to be written into
69 - a WriteNextStartStop(int state) make the written "pending" state become "switchable"
71 Different pending states can be written independantly and concurrently.
73 GetCurrentIndex() *must* return an increasing value to be able to check reading current state coherency
75 The fCounter is an array of indexes to access the current and 3 different "pending" states.
77 ¥ WriteNextStateStart(int index) must return a valid state to be written into, and must invalidate state "index" ==> cur state switch.
78 ¥ WriteNextStateStop(int index) makes the "index" state become "switchable" with the current state.
79 ¥ TrySwitchState(int index) must detect that pending state is a new state, and does the switch
80 ¥ ReadCurrentState() must return the state
81 ¥ GetCurrentIndex() must return an index increased each new switch.
82 ¥ WriteNextStateStart(int index1) and WriteNextStateStart(int index2) can be interleaved
84 [switch counter][index state][index state][cur index]
88 // CHECK livelock
90 template <class T>
91 class JackAtomicArrayState
94 protected:
96 // fState[0] ==> current
97 // fState[1] ==> pending
98 // fState[2] ==> request
100 T fState[3];
101 volatile AtomicArrayCounter fCounter;
103 UInt32 WriteNextStateStartAux(int state, bool* result)
105 AtomicArrayCounter old_val;
106 AtomicArrayCounter new_val;
107 UInt32 cur_index;
108 UInt32 next_index;
109 bool need_copy;
110 do {
111 old_val = fCounter;
112 new_val = old_val;
113 *result = GetIndex1(new_val, state);
114 cur_index = GetIndex1(new_val, 0);
115 next_index = SwapIndex1(fCounter, state);
116 need_copy = (GetIndex1(new_val, state) == 0); // Written = false, switch just occured
117 SetIndex1(new_val, state, 0); // Written = false, invalidate state
118 } while (!CAS(Counter1(old_val), Counter1(new_val), (UInt32*)&fCounter));
119 if (need_copy)
120 memcpy(&fState[next_index], &fState[cur_index], sizeof(T));
121 return next_index;
124 void WriteNextStateStopAux(int state)
126 AtomicArrayCounter old_val;
127 AtomicArrayCounter new_val;
128 do {
129 old_val = fCounter;
130 new_val = old_val;
131 SetIndex1(new_val, state, 1); // Written = true, state becomes "switchable"
132 } while (!CAS(Counter1(old_val), Counter1(new_val), (UInt32*)&fCounter));
135 public:
137 JackAtomicArrayState()
139 JackLog("JackAtomicArrayState constructor\n");
140 Counter1(fCounter) = 0;
143 ~JackAtomicArrayState() // Not virtual ??
147 \brief Returns the current state : only valid in the RT reader thread
150 T* ReadCurrentState()
152 return &fState[GetIndex1(fCounter, 0)];
156 \brief Returns the current switch counter
159 UInt16 GetCurrentIndex()
161 return GetIndex1(fCounter, 3);
165 \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)
168 T* TrySwitchState(int state)
170 AtomicArrayCounter old_val;
171 AtomicArrayCounter new_val;
172 do {
173 old_val = fCounter;
174 new_val = old_val;
175 if (GetIndex1(new_val, state)) { // If state has been written
176 SetIndex1(new_val, 0, SwapIndex1(new_val, state)); // Prepare switch
177 SetIndex1(new_val, state, 0); // Invalidate the state "state"
178 IncIndex1(new_val, 3); // Inc switch
180 } while (!CAS(Counter1(old_val), Counter1(new_val), (UInt32*)&fCounter));
181 return &fState[GetIndex1(fCounter, 0)]; // Read the counter again
185 \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)
188 T* TrySwitchState(int state, bool* result)
190 AtomicArrayCounter old_val;
191 AtomicArrayCounter new_val;
192 do {
193 old_val = fCounter;
194 new_val = old_val;
195 if ((*result = GetIndex1(new_val, state))) { // If state has been written
196 SetIndex1(new_val, 0, SwapIndex1(new_val, state)); // Prepare switch
197 SetIndex1(new_val, state, 0); // Invalidate the state "state"
198 IncIndex1(new_val, 3); // Inc switch
200 } while (!CAS(Counter1(old_val), Counter1(new_val), (UInt32*)&fCounter));
201 return &fState[GetIndex1(fCounter, 0)]; // Read the counter again
205 \brief Start write operation : setup and returns the next state to update, check for recursive write calls.
208 T* WriteNextStateStart(int state)
210 bool tmp;
211 UInt32 index = WriteNextStateStartAux(state, &tmp);
212 return &fState[index];
215 T* WriteNextStateStart(int state, bool* result)
217 UInt32 index = WriteNextStateStartAux(state, result);
218 return &fState[index];
222 \brief Stop write operation : make the next state ready to be used by the RT thread
224 void WriteNextStateStop(int state)
226 WriteNextStateStopAux(state);
231 } // end of namespace
234 #endif