|
- /*
- 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
|