jack2 codebase
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

262 lines
8.3KB

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