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.

266 lines
7.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 __JackAtomicState__
  16. #define __JackAtomicState__
  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 AtomicCounter
  28. {
  29. union {
  30. struct {
  31. UInt16 fShortVal1; // Cur
  32. UInt16 fShortVal2; // Next
  33. }
  34. scounter;
  35. UInt32 fLongVal;
  36. }info;
  37. AtomicCounter()
  38. {
  39. info.fLongVal = 0;
  40. }
  41. AtomicCounter(volatile const AtomicCounter& obj)
  42. {
  43. info.fLongVal = obj.info.fLongVal;
  44. }
  45. AtomicCounter(volatile AtomicCounter& obj)
  46. {
  47. info.fLongVal = obj.info.fLongVal;
  48. }
  49. AtomicCounter& operator=(AtomicCounter& obj)
  50. {
  51. info.fLongVal = obj.info.fLongVal;
  52. return *this;
  53. }
  54. AtomicCounter& operator=(volatile AtomicCounter& obj)
  55. {
  56. info.fLongVal = obj.info.fLongVal;
  57. return *this;
  58. }
  59. } POST_PACKED_STRUCTURE;
  60. #define Counter(e) (e).info.fLongVal
  61. #define CurIndex(e) (e).info.scounter.fShortVal1
  62. #define NextIndex(e) (e).info.scounter.fShortVal2
  63. #define CurArrayIndex(e) (CurIndex(e) & 0x0001)
  64. #define NextArrayIndex(e) ((CurIndex(e) + 1) & 0x0001)
  65. /*!
  66. \brief A class to handle two states (switching from one to the other) in a lock-free manner
  67. */
  68. // CHECK livelock
  69. PRE_PACKED_STRUCTURE
  70. template <class T>
  71. class JackAtomicState
  72. {
  73. protected:
  74. T fState[2];
  75. alignas(UInt32) alignas(AtomicCounter) volatile AtomicCounter fCounter;
  76. SInt32 fCallWriteCounter;
  77. UInt32 WriteNextStateStartAux()
  78. {
  79. AtomicCounter old_val;
  80. AtomicCounter new_val;
  81. UInt32 cur_index;
  82. UInt32 next_index;
  83. bool need_copy;
  84. do {
  85. old_val = fCounter;
  86. new_val = old_val;
  87. cur_index = CurArrayIndex(new_val);
  88. next_index = NextArrayIndex(new_val);
  89. need_copy = (CurIndex(new_val) == NextIndex(new_val));
  90. NextIndex(new_val) = CurIndex(new_val); // Invalidate next index
  91. } while (!CAS(Counter(old_val), Counter(new_val), (UInt32*)&fCounter));
  92. if (need_copy)
  93. memcpy(&fState[next_index], &fState[cur_index], sizeof(T));
  94. return next_index;
  95. }
  96. void WriteNextStateStopAux()
  97. {
  98. AtomicCounter old_val;
  99. AtomicCounter new_val;
  100. do {
  101. old_val = fCounter;
  102. new_val = old_val;
  103. NextIndex(new_val)++; // Set next index
  104. } while (!CAS(Counter(old_val), Counter(new_val), (UInt32*)&fCounter));
  105. }
  106. public:
  107. JackAtomicState()
  108. {
  109. static_assert(offsetof(JackAtomicState, fCounter) % sizeof(fCounter) == 0,
  110. "fCounter must be aligned within JackAtomicState");
  111. Counter(fCounter) = 0;
  112. fCallWriteCounter = 0;
  113. }
  114. ~JackAtomicState() // Not virtual ??
  115. {}
  116. /*!
  117. \brief Returns the current state : only valid in the RT reader thread
  118. */
  119. T* ReadCurrentState()
  120. {
  121. return &fState[CurArrayIndex(fCounter)];
  122. }
  123. /*!
  124. \brief Returns the current state index
  125. */
  126. UInt16 GetCurrentIndex()
  127. {
  128. return CurIndex(fCounter);
  129. }
  130. /*!
  131. \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)
  132. */
  133. T* TrySwitchState()
  134. {
  135. AtomicCounter old_val;
  136. AtomicCounter new_val;
  137. do {
  138. old_val = fCounter;
  139. new_val = old_val;
  140. CurIndex(new_val) = NextIndex(new_val); // Prepare switch
  141. } while (!CAS(Counter(old_val), Counter(new_val), (UInt32*)&fCounter));
  142. return &fState[CurArrayIndex(fCounter)]; // Read the counter again
  143. }
  144. /*!
  145. \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)
  146. */
  147. T* TrySwitchState(bool* result)
  148. {
  149. AtomicCounter old_val;
  150. AtomicCounter new_val;
  151. do {
  152. old_val = fCounter;
  153. new_val = old_val;
  154. *result = (CurIndex(new_val) != NextIndex(new_val));
  155. CurIndex(new_val) = NextIndex(new_val); // Prepare switch
  156. } while (!CAS(Counter(old_val), Counter(new_val), (UInt32*)&fCounter));
  157. return &fState[CurArrayIndex(fCounter)]; // Read the counter again
  158. }
  159. /*!
  160. \brief Start write operation : setup and returns the next state to update, check for recursive write calls.
  161. */
  162. T* WriteNextStateStart()
  163. {
  164. UInt32 next_index = (fCallWriteCounter++ == 0)
  165. ? WriteNextStateStartAux()
  166. : NextArrayIndex(fCounter); // We are inside a wrapping WriteNextStateStart call, NextArrayIndex can be read safely
  167. return &fState[next_index];
  168. }
  169. /*!
  170. \brief Stop write operation : make the next state ready to be used by the RT thread
  171. */
  172. void WriteNextStateStop()
  173. {
  174. if (--fCallWriteCounter == 0)
  175. WriteNextStateStopAux();
  176. }
  177. bool IsPendingChange()
  178. {
  179. return CurIndex(fCounter) != NextIndex(fCounter);
  180. }
  181. /*
  182. // Single writer : write methods get the *next* state to be updated
  183. void TestWriteMethod()
  184. {
  185. T* state = WriteNextStateStart();
  186. ......
  187. ......
  188. WriteNextStateStop();
  189. }
  190. // First RT call possibly switch state
  191. void TestReadRTMethod1()
  192. {
  193. T* state = TrySwitchState();
  194. ......
  195. ......
  196. }
  197. // Other RT methods can safely use the current state during the *same* RT cycle
  198. void TestReadRTMethod2()
  199. {
  200. T* state = ReadCurrentState();
  201. ......
  202. ......
  203. }
  204. // Non RT read methods : must check state coherency
  205. void TestReadMethod()
  206. {
  207. T* state;
  208. UInt16 cur_index;
  209. UInt16 next_index = GetCurrentIndex();
  210. do {
  211. cur_index = next_index;
  212. state = ReadCurrentState();
  213. ......
  214. ......
  215. next_index = GetCurrentIndex();
  216. } while (cur_index != next_index);
  217. }
  218. */
  219. } POST_PACKED_STRUCTURE;
  220. } // end of namespace
  221. #endif