windows-rwlock.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377
  1. /* Read-write locks (native Windows implementation).
  2. Copyright (C) 2005-2021 Free Software Foundation, Inc.
  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 3, or (at your option)
  6. 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, see <https://www.gnu.org/licenses/>. */
  13. /* Written by Bruno Haible <bruno@clisp.org>, 2005.
  14. Based on GCC's gthr-win32.h. */
  15. #include <config.h>
  16. /* Specification. */
  17. #include "windows-rwlock.h"
  18. #include <errno.h>
  19. #include <stdlib.h>
  20. /* Don't assume that UNICODE is not defined. */
  21. #undef CreateEvent
  22. #define CreateEvent CreateEventA
  23. /* In this file, the waitqueues are implemented as circular arrays. */
  24. #define glwthread_waitqueue_t glwthread_carray_waitqueue_t
  25. static void
  26. glwthread_waitqueue_init (glwthread_waitqueue_t *wq)
  27. {
  28. wq->array = NULL;
  29. wq->count = 0;
  30. wq->alloc = 0;
  31. wq->offset = 0;
  32. }
  33. /* Enqueues the current thread, represented by an event, in a wait queue.
  34. Returns INVALID_HANDLE_VALUE if an allocation failure occurs. */
  35. static HANDLE
  36. glwthread_waitqueue_add (glwthread_waitqueue_t *wq)
  37. {
  38. HANDLE event;
  39. unsigned int index;
  40. if (wq->count == wq->alloc)
  41. {
  42. unsigned int new_alloc = 2 * wq->alloc + 1;
  43. HANDLE *new_array =
  44. (HANDLE *) realloc (wq->array, new_alloc * sizeof (HANDLE));
  45. if (new_array == NULL)
  46. /* No more memory. */
  47. return INVALID_HANDLE_VALUE;
  48. /* Now is a good opportunity to rotate the array so that its contents
  49. starts at offset 0. */
  50. if (wq->offset > 0)
  51. {
  52. unsigned int old_count = wq->count;
  53. unsigned int old_alloc = wq->alloc;
  54. unsigned int old_offset = wq->offset;
  55. unsigned int i;
  56. if (old_offset + old_count > old_alloc)
  57. {
  58. unsigned int limit = old_offset + old_count - old_alloc;
  59. for (i = 0; i < limit; i++)
  60. new_array[old_alloc + i] = new_array[i];
  61. }
  62. for (i = 0; i < old_count; i++)
  63. new_array[i] = new_array[old_offset + i];
  64. wq->offset = 0;
  65. }
  66. wq->array = new_array;
  67. wq->alloc = new_alloc;
  68. }
  69. /* Whether the created event is a manual-reset one or an auto-reset one,
  70. does not matter, since we will wait on it only once. */
  71. event = CreateEvent (NULL, TRUE, FALSE, NULL);
  72. if (event == INVALID_HANDLE_VALUE)
  73. /* No way to allocate an event. */
  74. return INVALID_HANDLE_VALUE;
  75. index = wq->offset + wq->count;
  76. if (index >= wq->alloc)
  77. index -= wq->alloc;
  78. wq->array[index] = event;
  79. wq->count++;
  80. return event;
  81. }
  82. /* Notifies the first thread from a wait queue and dequeues it. */
  83. static void
  84. glwthread_waitqueue_notify_first (glwthread_waitqueue_t *wq)
  85. {
  86. SetEvent (wq->array[wq->offset + 0]);
  87. wq->offset++;
  88. wq->count--;
  89. if (wq->count == 0 || wq->offset == wq->alloc)
  90. wq->offset = 0;
  91. }
  92. /* Notifies all threads from a wait queue and dequeues them all. */
  93. static void
  94. glwthread_waitqueue_notify_all (glwthread_waitqueue_t *wq)
  95. {
  96. unsigned int i;
  97. for (i = 0; i < wq->count; i++)
  98. {
  99. unsigned int index = wq->offset + i;
  100. if (index >= wq->alloc)
  101. index -= wq->alloc;
  102. SetEvent (wq->array[index]);
  103. }
  104. wq->count = 0;
  105. wq->offset = 0;
  106. }
  107. void
  108. glwthread_rwlock_init (glwthread_rwlock_t *lock)
  109. {
  110. InitializeCriticalSection (&lock->lock);
  111. glwthread_waitqueue_init (&lock->waiting_readers);
  112. glwthread_waitqueue_init (&lock->waiting_writers);
  113. lock->runcount = 0;
  114. lock->guard.done = 1;
  115. }
  116. int
  117. glwthread_rwlock_rdlock (glwthread_rwlock_t *lock)
  118. {
  119. if (!lock->guard.done)
  120. {
  121. if (InterlockedIncrement (&lock->guard.started) == 0)
  122. /* This thread is the first one to need this lock. Initialize it. */
  123. glwthread_rwlock_init (lock);
  124. else
  125. {
  126. /* Don't let lock->guard.started grow and wrap around. */
  127. InterlockedDecrement (&lock->guard.started);
  128. /* Yield the CPU while waiting for another thread to finish
  129. initializing this lock. */
  130. while (!lock->guard.done)
  131. Sleep (0);
  132. }
  133. }
  134. EnterCriticalSection (&lock->lock);
  135. /* Test whether only readers are currently running, and whether the runcount
  136. field will not overflow, and whether no writer is waiting. The latter
  137. condition is because POSIX recommends that "write locks shall take
  138. precedence over read locks", to avoid "writer starvation". */
  139. if (!(lock->runcount + 1 > 0 && lock->waiting_writers.count == 0))
  140. {
  141. /* This thread has to wait for a while. Enqueue it among the
  142. waiting_readers. */
  143. HANDLE event = glwthread_waitqueue_add (&lock->waiting_readers);
  144. if (event != INVALID_HANDLE_VALUE)
  145. {
  146. DWORD result;
  147. LeaveCriticalSection (&lock->lock);
  148. /* Wait until another thread signals this event. */
  149. result = WaitForSingleObject (event, INFINITE);
  150. if (result == WAIT_FAILED || result == WAIT_TIMEOUT)
  151. abort ();
  152. CloseHandle (event);
  153. /* The thread which signalled the event already did the bookkeeping:
  154. removed us from the waiting_readers, incremented lock->runcount. */
  155. if (!(lock->runcount > 0))
  156. abort ();
  157. return 0;
  158. }
  159. else
  160. {
  161. /* Allocation failure. Weird. */
  162. do
  163. {
  164. LeaveCriticalSection (&lock->lock);
  165. Sleep (1);
  166. EnterCriticalSection (&lock->lock);
  167. }
  168. while (!(lock->runcount + 1 > 0));
  169. }
  170. }
  171. lock->runcount++;
  172. LeaveCriticalSection (&lock->lock);
  173. return 0;
  174. }
  175. int
  176. glwthread_rwlock_wrlock (glwthread_rwlock_t *lock)
  177. {
  178. if (!lock->guard.done)
  179. {
  180. if (InterlockedIncrement (&lock->guard.started) == 0)
  181. /* This thread is the first one to need this lock. Initialize it. */
  182. glwthread_rwlock_init (lock);
  183. else
  184. {
  185. /* Don't let lock->guard.started grow and wrap around. */
  186. InterlockedDecrement (&lock->guard.started);
  187. /* Yield the CPU while waiting for another thread to finish
  188. initializing this lock. */
  189. while (!lock->guard.done)
  190. Sleep (0);
  191. }
  192. }
  193. EnterCriticalSection (&lock->lock);
  194. /* Test whether no readers or writers are currently running. */
  195. if (!(lock->runcount == 0))
  196. {
  197. /* This thread has to wait for a while. Enqueue it among the
  198. waiting_writers. */
  199. HANDLE event = glwthread_waitqueue_add (&lock->waiting_writers);
  200. if (event != INVALID_HANDLE_VALUE)
  201. {
  202. DWORD result;
  203. LeaveCriticalSection (&lock->lock);
  204. /* Wait until another thread signals this event. */
  205. result = WaitForSingleObject (event, INFINITE);
  206. if (result == WAIT_FAILED || result == WAIT_TIMEOUT)
  207. abort ();
  208. CloseHandle (event);
  209. /* The thread which signalled the event already did the bookkeeping:
  210. removed us from the waiting_writers, set lock->runcount = -1. */
  211. if (!(lock->runcount == -1))
  212. abort ();
  213. return 0;
  214. }
  215. else
  216. {
  217. /* Allocation failure. Weird. */
  218. do
  219. {
  220. LeaveCriticalSection (&lock->lock);
  221. Sleep (1);
  222. EnterCriticalSection (&lock->lock);
  223. }
  224. while (!(lock->runcount == 0));
  225. }
  226. }
  227. lock->runcount--; /* runcount becomes -1 */
  228. LeaveCriticalSection (&lock->lock);
  229. return 0;
  230. }
  231. int
  232. glwthread_rwlock_tryrdlock (glwthread_rwlock_t *lock)
  233. {
  234. if (!lock->guard.done)
  235. {
  236. if (InterlockedIncrement (&lock->guard.started) == 0)
  237. /* This thread is the first one to need this lock. Initialize it. */
  238. glwthread_rwlock_init (lock);
  239. else
  240. {
  241. /* Don't let lock->guard.started grow and wrap around. */
  242. InterlockedDecrement (&lock->guard.started);
  243. /* Yield the CPU while waiting for another thread to finish
  244. initializing this lock. */
  245. while (!lock->guard.done)
  246. Sleep (0);
  247. }
  248. }
  249. /* It's OK to wait for this critical section, because it is never taken for a
  250. long time. */
  251. EnterCriticalSection (&lock->lock);
  252. /* Test whether only readers are currently running, and whether the runcount
  253. field will not overflow, and whether no writer is waiting. The latter
  254. condition is because POSIX recommends that "write locks shall take
  255. precedence over read locks", to avoid "writer starvation". */
  256. if (!(lock->runcount + 1 > 0 && lock->waiting_writers.count == 0))
  257. {
  258. /* This thread would have to wait for a while. Return instead. */
  259. LeaveCriticalSection (&lock->lock);
  260. return EBUSY;
  261. }
  262. lock->runcount++;
  263. LeaveCriticalSection (&lock->lock);
  264. return 0;
  265. }
  266. int
  267. glwthread_rwlock_trywrlock (glwthread_rwlock_t *lock)
  268. {
  269. if (!lock->guard.done)
  270. {
  271. if (InterlockedIncrement (&lock->guard.started) == 0)
  272. /* This thread is the first one to need this lock. Initialize it. */
  273. glwthread_rwlock_init (lock);
  274. else
  275. {
  276. /* Don't let lock->guard.started grow and wrap around. */
  277. InterlockedDecrement (&lock->guard.started);
  278. /* Yield the CPU while waiting for another thread to finish
  279. initializing this lock. */
  280. while (!lock->guard.done)
  281. Sleep (0);
  282. }
  283. }
  284. /* It's OK to wait for this critical section, because it is never taken for a
  285. long time. */
  286. EnterCriticalSection (&lock->lock);
  287. /* Test whether no readers or writers are currently running. */
  288. if (!(lock->runcount == 0))
  289. {
  290. /* This thread would have to wait for a while. Return instead. */
  291. LeaveCriticalSection (&lock->lock);
  292. return EBUSY;
  293. }
  294. lock->runcount--; /* runcount becomes -1 */
  295. LeaveCriticalSection (&lock->lock);
  296. return 0;
  297. }
  298. int
  299. glwthread_rwlock_unlock (glwthread_rwlock_t *lock)
  300. {
  301. if (!lock->guard.done)
  302. return EINVAL;
  303. EnterCriticalSection (&lock->lock);
  304. if (lock->runcount < 0)
  305. {
  306. /* Drop a writer lock. */
  307. if (!(lock->runcount == -1))
  308. abort ();
  309. lock->runcount = 0;
  310. }
  311. else
  312. {
  313. /* Drop a reader lock. */
  314. if (!(lock->runcount > 0))
  315. {
  316. LeaveCriticalSection (&lock->lock);
  317. return EPERM;
  318. }
  319. lock->runcount--;
  320. }
  321. if (lock->runcount == 0)
  322. {
  323. /* POSIX recommends that "write locks shall take precedence over read
  324. locks", to avoid "writer starvation". */
  325. if (lock->waiting_writers.count > 0)
  326. {
  327. /* Wake up one of the waiting writers. */
  328. lock->runcount--;
  329. glwthread_waitqueue_notify_first (&lock->waiting_writers);
  330. }
  331. else
  332. {
  333. /* Wake up all waiting readers. */
  334. lock->runcount += lock->waiting_readers.count;
  335. glwthread_waitqueue_notify_all (&lock->waiting_readers);
  336. }
  337. }
  338. LeaveCriticalSection (&lock->lock);
  339. return 0;
  340. }
  341. int
  342. glwthread_rwlock_destroy (glwthread_rwlock_t *lock)
  343. {
  344. if (!lock->guard.done)
  345. return EINVAL;
  346. if (lock->runcount != 0)
  347. return EBUSY;
  348. DeleteCriticalSection (&lock->lock);
  349. if (lock->waiting_readers.array != NULL)
  350. free (lock->waiting_readers.array);
  351. if (lock->waiting_writers.array != NULL)
  352. free (lock->waiting_writers.array);
  353. lock->guard.done = 0;
  354. return 0;
  355. }