ptw32_MCS_lock.c 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278
  1. /*
  2. * ptw32_MCS_lock.c
  3. *
  4. * Description:
  5. * This translation unit implements queue-based locks.
  6. *
  7. * --------------------------------------------------------------------------
  8. *
  9. * Pthreads-win32 - POSIX Threads Library for Win32
  10. * Copyright(C) 1998 John E. Bossom
  11. * Copyright(C) 1999,2005 Pthreads-win32 contributors
  12. *
  13. * Contact Email: [email protected]
  14. *
  15. * The current list of contributors is contained
  16. * in the file CONTRIBUTORS included with the source
  17. * code distribution. The list can also be seen at the
  18. * following World Wide Web location:
  19. * http://sources.redhat.com/pthreads-win32/contributors.html
  20. *
  21. * This library is free software; you can redistribute it and/or
  22. * modify it under the terms of the GNU Lesser General Public
  23. * License as published by the Free Software Foundation; either
  24. * version 2 of the License, or (at your option) any later version.
  25. *
  26. * This library is distributed in the hope that it will be useful,
  27. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  28. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  29. * Lesser General Public License for more details.
  30. *
  31. * You should have received a copy of the GNU Lesser General Public
  32. * License along with this library in the file COPYING.LIB;
  33. * if not, write to the Free Software Foundation, Inc.,
  34. * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
  35. */
  36. /*
  37. * About MCS locks:
  38. *
  39. * MCS locks are queue-based locks, where the queue nodes are local to the
  40. * thread. The 'lock' is nothing more than a global pointer that points to
  41. * the last node in the queue, or is NULL if the queue is empty.
  42. *
  43. * Originally designed for use as spin locks requiring no kernel resources
  44. * for synchronisation or blocking, the implementation below has adapted
  45. * the MCS spin lock for use as a general mutex that will suspend threads
  46. * when there is lock contention.
  47. *
  48. * Because the queue nodes are thread-local, most of the memory read/write
  49. * operations required to add or remove nodes from the queue do not trigger
  50. * cache-coherence updates.
  51. *
  52. * Like 'named' mutexes, MCS locks consume system resources transiently -
  53. * they are able to acquire and free resources automatically - but MCS
  54. * locks do not require any unique 'name' to identify the lock to all
  55. * threads using it.
  56. *
  57. * Usage of MCS locks:
  58. *
  59. * - you need a global ptw32_mcs_lock_t instance initialised to 0 or NULL.
  60. * - you need a local thread-scope ptw32_mcs_local_node_t instance, which
  61. * may serve several different locks but you need at least one node for
  62. * every lock held concurrently by a thread.
  63. *
  64. * E.g.:
  65. *
  66. * ptw32_mcs_lock_t lock1 = 0;
  67. * ptw32_mcs_lock_t lock2 = 0;
  68. *
  69. * void *mythread(void *arg)
  70. * {
  71. * ptw32_mcs_local_node_t node;
  72. *
  73. * ptw32_mcs_acquire (&lock1, &node);
  74. * ptw32_mcs_lock_release (&node);
  75. *
  76. * ptw32_mcs_lock_acquire (&lock2, &node);
  77. * ptw32_mcs_lock_release (&node);
  78. * {
  79. * ptw32_mcs_local_node_t nodex;
  80. *
  81. * ptw32_mcs_lock_acquire (&lock1, &node);
  82. * ptw32_mcs_lock_acquire (&lock2, &nodex);
  83. *
  84. * ptw32_mcs_lock_release (&nodex);
  85. * ptw32_mcs_lock_release (&node);
  86. * }
  87. * return (void *)0;
  88. * }
  89. */
  90. #include "pthread.h"
  91. #include "sched.h"
  92. #include "implement.h"
  93. /*
  94. * ptw32_mcs_flag_set -- notify another thread about an event.
  95. *
  96. * Set event if an event handle has been stored in the flag, and
  97. * set flag to -1 otherwise. Note that -1 cannot be a valid handle value.
  98. */
  99. INLINE void
  100. ptw32_mcs_flag_set (HANDLE * flag)
  101. {
  102. HANDLE e = (HANDLE)(PTW32_INTERLOCKED_SIZE)PTW32_INTERLOCKED_COMPARE_EXCHANGE_SIZE(
  103. (PTW32_INTERLOCKED_SIZEPTR)flag,
  104. (PTW32_INTERLOCKED_SIZE)-1,
  105. (PTW32_INTERLOCKED_SIZE)0);
  106. if ((HANDLE)0 != e)
  107. {
  108. /* another thread has already stored an event handle in the flag */
  109. SetEvent(e);
  110. }
  111. }
  112. /*
  113. * ptw32_mcs_flag_set -- wait for notification from another.
  114. *
  115. * Store an event handle in the flag and wait on it if the flag has not been
  116. * set, and proceed without creating an event otherwise.
  117. */
  118. INLINE void
  119. ptw32_mcs_flag_wait (HANDLE * flag)
  120. {
  121. if ((PTW32_INTERLOCKED_LONG)0 ==
  122. PTW32_INTERLOCKED_EXCHANGE_ADD_SIZE((PTW32_INTERLOCKED_SIZEPTR)flag,
  123. (PTW32_INTERLOCKED_SIZE)0)) /* MBR fence */
  124. {
  125. /* the flag is not set. create event. */
  126. HANDLE e = CreateEvent(NULL, PTW32_FALSE, PTW32_FALSE, NULL);
  127. if ((PTW32_INTERLOCKED_SIZE)0 == PTW32_INTERLOCKED_COMPARE_EXCHANGE_SIZE(
  128. (PTW32_INTERLOCKED_SIZEPTR)flag,
  129. (PTW32_INTERLOCKED_SIZE)e,
  130. (PTW32_INTERLOCKED_SIZE)0))
  131. {
  132. /* stored handle in the flag. wait on it now. */
  133. WaitForSingleObject(e, INFINITE);
  134. }
  135. CloseHandle(e);
  136. }
  137. }
  138. /*
  139. * ptw32_mcs_lock_acquire -- acquire an MCS lock.
  140. *
  141. * See:
  142. * J. M. Mellor-Crummey and M. L. Scott.
  143. * Algorithms for Scalable Synchronization on Shared-Memory Multiprocessors.
  144. * ACM Transactions on Computer Systems, 9(1):21-65, Feb. 1991.
  145. */
  146. #if defined(PTW32_BUILD_INLINED)
  147. INLINE
  148. #endif /* PTW32_BUILD_INLINED */
  149. void
  150. ptw32_mcs_lock_acquire (ptw32_mcs_lock_t * lock, ptw32_mcs_local_node_t * node)
  151. {
  152. ptw32_mcs_local_node_t *pred;
  153. node->lock = lock;
  154. node->nextFlag = 0;
  155. node->readyFlag = 0;
  156. node->next = 0; /* initially, no successor */
  157. /* queue for the lock */
  158. pred = (ptw32_mcs_local_node_t *)PTW32_INTERLOCKED_EXCHANGE_PTR((PTW32_INTERLOCKED_PVOID_PTR)lock,
  159. (PTW32_INTERLOCKED_PVOID)node);
  160. if (0 != pred)
  161. {
  162. /* the lock was not free. link behind predecessor. */
  163. pred->next = node;
  164. ptw32_mcs_flag_set(&pred->nextFlag);
  165. ptw32_mcs_flag_wait(&node->readyFlag);
  166. }
  167. }
  168. /*
  169. * ptw32_mcs_lock_release -- release an MCS lock.
  170. *
  171. * See:
  172. * J. M. Mellor-Crummey and M. L. Scott.
  173. * Algorithms for Scalable Synchronization on Shared-Memory Multiprocessors.
  174. * ACM Transactions on Computer Systems, 9(1):21-65, Feb. 1991.
  175. */
  176. #if defined(PTW32_BUILD_INLINED)
  177. INLINE
  178. #endif /* PTW32_BUILD_INLINED */
  179. void
  180. ptw32_mcs_lock_release (ptw32_mcs_local_node_t * node)
  181. {
  182. ptw32_mcs_lock_t *lock = node->lock;
  183. ptw32_mcs_local_node_t *next =
  184. (ptw32_mcs_local_node_t *)
  185. PTW32_INTERLOCKED_EXCHANGE_ADD_SIZE((PTW32_INTERLOCKED_SIZEPTR)&node->next, (PTW32_INTERLOCKED_SIZE)0); /* MBR fence */
  186. if (0 == next)
  187. {
  188. /* no known successor */
  189. if (node == (ptw32_mcs_local_node_t *)
  190. PTW32_INTERLOCKED_COMPARE_EXCHANGE_PTR((PTW32_INTERLOCKED_PVOID_PTR)lock,
  191. (PTW32_INTERLOCKED_PVOID)0,
  192. (PTW32_INTERLOCKED_PVOID)node))
  193. {
  194. /* no successor, lock is free now */
  195. return;
  196. }
  197. /* A successor has started enqueueing behind us so wait for them to link to us */
  198. ptw32_mcs_flag_wait(&node->nextFlag);
  199. next = (ptw32_mcs_local_node_t *)
  200. PTW32_INTERLOCKED_EXCHANGE_ADD_SIZE((PTW32_INTERLOCKED_SIZEPTR)&node->next, (PTW32_INTERLOCKED_SIZE)0); /* MBR fence */
  201. }
  202. /* pass the lock */
  203. ptw32_mcs_flag_set(&next->readyFlag);
  204. }
  205. /*
  206. * ptw32_mcs_lock_try_acquire
  207. */
  208. #if defined(PTW32_BUILD_INLINED)
  209. INLINE
  210. #endif /* PTW32_BUILD_INLINED */
  211. int
  212. ptw32_mcs_lock_try_acquire (ptw32_mcs_lock_t * lock, ptw32_mcs_local_node_t * node)
  213. {
  214. node->lock = lock;
  215. node->nextFlag = 0;
  216. node->readyFlag = 0;
  217. node->next = 0; /* initially, no successor */
  218. return ((PTW32_INTERLOCKED_PVOID)PTW32_INTERLOCKED_COMPARE_EXCHANGE_PTR((PTW32_INTERLOCKED_PVOID_PTR)lock,
  219. (PTW32_INTERLOCKED_PVOID)node,
  220. (PTW32_INTERLOCKED_PVOID)0)
  221. == (PTW32_INTERLOCKED_PVOID)0) ? 0 : EBUSY;
  222. }
  223. /*
  224. * ptw32_mcs_node_transfer -- move an MCS lock local node, usually from thread
  225. * space to, for example, global space so that another thread can release
  226. * the lock on behalf of the current lock owner.
  227. *
  228. * Example: used in pthread_barrier_wait where we want the last thread out of
  229. * the barrier to release the lock owned by the last thread to enter the barrier
  230. * (the one that releases all threads but not necessarily the last to leave).
  231. *
  232. * Should only be called by the thread that has the lock.
  233. */
  234. #if defined(PTW32_BUILD_INLINED)
  235. INLINE
  236. #endif /* PTW32_BUILD_INLINED */
  237. void
  238. ptw32_mcs_node_transfer (ptw32_mcs_local_node_t * new_node, ptw32_mcs_local_node_t * old_node)
  239. {
  240. new_node->lock = old_node->lock;
  241. new_node->nextFlag = 0; /* Not needed - used only in initial Acquire */
  242. new_node->readyFlag = 0; /* Not needed - we were waiting on this */
  243. new_node->next = 0;
  244. if ((ptw32_mcs_local_node_t *)PTW32_INTERLOCKED_COMPARE_EXCHANGE_PTR((PTW32_INTERLOCKED_PVOID_PTR)new_node->lock,
  245. (PTW32_INTERLOCKED_PVOID)new_node,
  246. (PTW32_INTERLOCKED_PVOID)old_node)
  247. != old_node)
  248. {
  249. /*
  250. * A successor has queued after us, so wait for them to link to us
  251. */
  252. while (old_node->next == 0)
  253. {
  254. sched_yield();
  255. }
  256. new_node->next = old_node->next;
  257. }
  258. }