1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258
|
/*
Copyright (C) 2004-2008 Grame
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef __JackAtomicArrayState__
#define __JackAtomicArrayState__
#include "JackAtomic.h"
#include "JackCompilerDeps.h"
#include <string.h> // for memcpy
namespace Jack
{
/*!
\brief Counter for CAS
*/
PRE_PACKED_STRUCTURE
struct AtomicArrayCounter
{
union {
struct {
unsigned char fByteVal[4];
}
scounter;
UInt32 fLongVal;
}info;
AtomicArrayCounter()
{
info.fLongVal = 0;
}
AtomicArrayCounter(volatile const AtomicArrayCounter& obj)
{
info.fLongVal = obj.info.fLongVal;
}
AtomicArrayCounter(volatile AtomicArrayCounter& obj)
{
info.fLongVal = obj.info.fLongVal;
}
AtomicArrayCounter& operator=(volatile AtomicArrayCounter& obj)
{
info.fLongVal = obj.info.fLongVal;
return *this;
}
AtomicArrayCounter& operator=(AtomicArrayCounter& obj)
{
info.fLongVal = obj.info.fLongVal;
return *this;
}
} POST_PACKED_STRUCTURE;
#define Counter1(e) (e).info.fLongVal
#define GetIndex1(e, state) ((e).info.scounter.fByteVal[state])
#define SetIndex1(e, state, val) ((e).info.scounter.fByteVal[state] = val)
#define IncIndex1(e, state) ((e).info.scounter.fByteVal[state]++)
#define SwapIndex1(e, state) (((e).info.scounter.fByteVal[0] == state) ? 0 : state)
/*!
\brief A class to handle several states in a lock-free manner
Requirement:
- a "current" state
- several possible "pending" state
- an TrySwitchState(int state) operation to atomically switch a "pending" to the "current" state (the pending becomes the current).
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)
- a WriteNextStartState(int state) returns a "pending" state to be written into
- a WriteNextStartStop(int state) make the written "pending" state become "switchable"
Different pending states can be written independantly and concurrently.
GetCurrentIndex() *must* return an increasing value to be able to check reading current state coherency
The fCounter is an array of indexes to access the current and 3 different "pending" states.
WriteNextStateStart(int index) must return a valid state to be written into, and must invalidate state "index" ==> cur state switch.
WriteNextStateStop(int index) makes the "index" state become "switchable" with the current state.
TrySwitchState(int index) must detect that pending state is a new state, and does the switch
ReadCurrentState() must return the state
GetCurrentIndex() must return an index increased each new switch.
WriteNextStateStart(int index1) and WriteNextStateStart(int index2) can be interleaved
[switch counter][index state][index state][cur index]
*/
// CHECK livelock
PRE_PACKED_STRUCTURE
template <class T>
class JackAtomicArrayState
{
protected:
// fState[0] ==> current
// fState[1] ==> pending
// fState[2] ==> request
T fState[3];
volatile AtomicArrayCounter fCounter;
UInt32 WriteNextStateStartAux(int state, bool* result)
{
AtomicArrayCounter old_val;
AtomicArrayCounter new_val;
UInt32 cur_index;
UInt32 next_index;
bool need_copy;
do {
old_val = fCounter;
new_val = old_val;
*result = GetIndex1(new_val, state);
cur_index = GetIndex1(new_val, 0);
next_index = SwapIndex1(fCounter, state);
need_copy = (GetIndex1(new_val, state) == 0); // Written = false, switch just occured
SetIndex1(new_val, state, 0); // Written = false, invalidate state
} while (!CAS(Counter1(old_val), Counter1(new_val), (UInt32*)&fCounter));
if (need_copy)
memcpy(&fState[next_index], &fState[cur_index], sizeof(T));
return next_index;
}
void WriteNextStateStopAux(int state)
{
AtomicArrayCounter old_val;
AtomicArrayCounter new_val;
do {
old_val = fCounter;
new_val = old_val;
SetIndex1(new_val, state, 1); // Written = true, state becomes "switchable"
} while (!CAS(Counter1(old_val), Counter1(new_val), (UInt32*)&fCounter));
}
public:
JackAtomicArrayState()
{
Counter1(fCounter) = 0;
}
~JackAtomicArrayState() // Not virtual ??
{}
/*!
\brief Returns the current state : only valid in the RT reader thread
*/
T* ReadCurrentState()
{
return &fState[GetIndex1(fCounter, 0)];
}
/*!
\brief Returns the current switch counter
*/
UInt16 GetCurrentIndex()
{
return GetIndex1(fCounter, 3);
}
/*!
\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)
*/
T* TrySwitchState(int state)
{
AtomicArrayCounter old_val;
AtomicArrayCounter new_val;
do {
old_val = fCounter;
new_val = old_val;
if (GetIndex1(new_val, state)) { // If state has been written
SetIndex1(new_val, 0, SwapIndex1(new_val, state)); // Prepare switch
SetIndex1(new_val, state, 0); // Invalidate the state "state"
IncIndex1(new_val, 3); // Inc switch
}
} while (!CAS(Counter1(old_val), Counter1(new_val), (UInt32*)&fCounter));
return &fState[GetIndex1(fCounter, 0)]; // Read the counter again
}
/*!
\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)
*/
T* TrySwitchState(int state, bool* result)
{
AtomicArrayCounter old_val;
AtomicArrayCounter new_val;
do {
old_val = fCounter;
new_val = old_val;
if ((*result = GetIndex1(new_val, state))) { // If state has been written
SetIndex1(new_val, 0, SwapIndex1(new_val, state)); // Prepare switch
SetIndex1(new_val, state, 0); // Invalidate the state "state"
IncIndex1(new_val, 3); // Inc switch
}
} while (!CAS(Counter1(old_val), Counter1(new_val), (UInt32*)&fCounter));
return &fState[GetIndex1(fCounter, 0)]; // Read the counter again
}
/*!
\brief Start write operation : setup and returns the next state to update, check for recursive write calls.
*/
T* WriteNextStateStart(int state)
{
bool tmp;
UInt32 index = WriteNextStateStartAux(state, &tmp);
return &fState[index];
}
T* WriteNextStateStart(int state, bool* result)
{
UInt32 index = WriteNextStateStartAux(state, result);
return &fState[index];
}
/*!
\brief Stop write operation : make the next state ready to be used by the RT thread
*/
void WriteNextStateStop(int state)
{
WriteNextStateStopAux(state);
}
} POST_PACKED_STRUCTURE;
} // end of namespace
#endif
|