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.

236 lines
7.6KB

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