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.

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