jack2/common/JackAtomicState.h

266 lines
7.3 KiB
C++

/*
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 __JackAtomicState__
#define __JackAtomicState__
#include "JackAtomic.h"
#include "JackCompilerDeps.h"
#include <string.h> // for memcpy
#include <cstddef>
namespace Jack
{
/*!
\brief Counter for CAS
*/
PRE_PACKED_STRUCTURE
struct AtomicCounter
{
union {
struct {
UInt16 fShortVal1; // Cur
UInt16 fShortVal2; // Next
}
scounter;
UInt32 fLongVal;
}info;
AtomicCounter()
{
info.fLongVal = 0;
}
AtomicCounter(volatile const AtomicCounter& obj)
{
info.fLongVal = obj.info.fLongVal;
}
AtomicCounter(volatile AtomicCounter& obj)
{
info.fLongVal = obj.info.fLongVal;
}
AtomicCounter& operator=(AtomicCounter& obj)
{
info.fLongVal = obj.info.fLongVal;
return *this;
}
AtomicCounter& operator=(volatile AtomicCounter& obj)
{
info.fLongVal = obj.info.fLongVal;
return *this;
}
} POST_PACKED_STRUCTURE;
#define Counter(e) (e).info.fLongVal
#define CurIndex(e) (e).info.scounter.fShortVal1
#define NextIndex(e) (e).info.scounter.fShortVal2
#define CurArrayIndex(e) (CurIndex(e) & 0x0001)
#define NextArrayIndex(e) ((CurIndex(e) + 1) & 0x0001)
/*!
\brief A class to handle two states (switching from one to the other) in a lock-free manner
*/
// CHECK livelock
PRE_PACKED_STRUCTURE
template <class T>
class JackAtomicState
{
protected:
T fState[2];
alignas(UInt32) alignas(AtomicCounter) volatile AtomicCounter fCounter;
SInt32 fCallWriteCounter;
UInt32 WriteNextStateStartAux()
{
AtomicCounter old_val;
AtomicCounter new_val;
UInt32 cur_index;
UInt32 next_index;
bool need_copy;
do {
old_val = fCounter;
new_val = old_val;
cur_index = CurArrayIndex(new_val);
next_index = NextArrayIndex(new_val);
need_copy = (CurIndex(new_val) == NextIndex(new_val));
NextIndex(new_val) = CurIndex(new_val); // Invalidate next index
} while (!CAS(Counter(old_val), Counter(new_val), (UInt32*)&fCounter));
if (need_copy)
memcpy(&fState[next_index], &fState[cur_index], sizeof(T));
return next_index;
}
void WriteNextStateStopAux()
{
AtomicCounter old_val;
AtomicCounter new_val;
do {
old_val = fCounter;
new_val = old_val;
NextIndex(new_val)++; // Set next index
} while (!CAS(Counter(old_val), Counter(new_val), (UInt32*)&fCounter));
}
public:
JackAtomicState()
{
static_assert(offsetof(JackAtomicState, fCounter) % sizeof(fCounter) == 0,
"fCounter must be aligned within JackAtomicState");
Counter(fCounter) = 0;
fCallWriteCounter = 0;
}
~JackAtomicState() // Not virtual ??
{}
/*!
\brief Returns the current state : only valid in the RT reader thread
*/
T* ReadCurrentState()
{
return &fState[CurArrayIndex(fCounter)];
}
/*!
\brief Returns the current state index
*/
UInt16 GetCurrentIndex()
{
return CurIndex(fCounter);
}
/*!
\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()
{
AtomicCounter old_val;
AtomicCounter new_val;
do {
old_val = fCounter;
new_val = old_val;
CurIndex(new_val) = NextIndex(new_val); // Prepare switch
} while (!CAS(Counter(old_val), Counter(new_val), (UInt32*)&fCounter));
return &fState[CurArrayIndex(fCounter)]; // 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(bool* result)
{
AtomicCounter old_val;
AtomicCounter new_val;
do {
old_val = fCounter;
new_val = old_val;
*result = (CurIndex(new_val) != NextIndex(new_val));
CurIndex(new_val) = NextIndex(new_val); // Prepare switch
} while (!CAS(Counter(old_val), Counter(new_val), (UInt32*)&fCounter));
return &fState[CurArrayIndex(fCounter)]; // Read the counter again
}
/*!
\brief Start write operation : setup and returns the next state to update, check for recursive write calls.
*/
T* WriteNextStateStart()
{
UInt32 next_index = (fCallWriteCounter++ == 0)
? WriteNextStateStartAux()
: NextArrayIndex(fCounter); // We are inside a wrapping WriteNextStateStart call, NextArrayIndex can be read safely
return &fState[next_index];
}
/*!
\brief Stop write operation : make the next state ready to be used by the RT thread
*/
void WriteNextStateStop()
{
if (--fCallWriteCounter == 0)
WriteNextStateStopAux();
}
bool IsPendingChange()
{
return CurIndex(fCounter) != NextIndex(fCounter);
}
/*
// Single writer : write methods get the *next* state to be updated
void TestWriteMethod()
{
T* state = WriteNextStateStart();
......
......
WriteNextStateStop();
}
// First RT call possibly switch state
void TestReadRTMethod1()
{
T* state = TrySwitchState();
......
......
}
// Other RT methods can safely use the current state during the *same* RT cycle
void TestReadRTMethod2()
{
T* state = ReadCurrentState();
......
......
}
// Non RT read methods : must check state coherency
void TestReadMethod()
{
T* state;
UInt16 cur_index;
UInt16 next_index = GetCurrentIndex();
do {
cur_index = next_index;
state = ReadCurrentState();
......
......
next_index = GetCurrentIndex();
} while (cur_index != next_index);
}
*/
} POST_PACKED_STRUCTURE;
} // end of namespace
#endif