test-condvar.c 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. /* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
  2. *
  3. * Permission is hereby granted, free of charge, to any person obtaining a copy
  4. * of this software and associated documentation files (the "Software"), to
  5. * deal in the Software without restriction, including without limitation the
  6. * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  7. * sell copies of the Software, and to permit persons to whom the Software is
  8. * furnished to do so, subject to the following conditions:
  9. *
  10. * The above copyright notice and this permission notice shall be included in
  11. * all copies or substantial portions of the Software.
  12. *
  13. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  14. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  15. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  16. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  17. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  18. * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  19. * IN THE SOFTWARE.
  20. */
  21. #include "uv.h"
  22. #include "task.h"
  23. #include <string.h>
  24. #include <errno.h>
  25. struct worker_config;
  26. typedef void (*signal_func)(struct worker_config* c, int* flag);
  27. typedef int (*wait_func)(struct worker_config* c, const int* flag);
  28. typedef struct worker_config {
  29. uv_sem_t sem_waiting; /* post before waiting. */
  30. uv_sem_t sem_signaled; /* post after signaling. */
  31. uv_mutex_t mutex;
  32. uv_cond_t cond;
  33. int use_broadcast;
  34. int posted_1;
  35. int posted_2;
  36. signal_func signal_cond;
  37. wait_func wait_cond;
  38. } worker_config;
  39. void worker_config_init(worker_config* wc,
  40. int use_broadcast,
  41. signal_func signal_f,
  42. wait_func wait_f) {
  43. /* Wipe. */
  44. memset(wc, 0, sizeof(*wc));
  45. /* Copy vars. */
  46. wc->signal_cond = signal_f;
  47. wc->wait_cond = wait_f;
  48. wc->use_broadcast = use_broadcast;
  49. /* Init. */
  50. ASSERT(0 == uv_sem_init(&wc->sem_waiting, 0));
  51. ASSERT(0 == uv_sem_init(&wc->sem_signaled, 0));
  52. ASSERT(0 == uv_cond_init(&wc->cond));
  53. ASSERT(0 == uv_mutex_init(&wc->mutex));
  54. }
  55. void worker_config_destroy(worker_config* wc) {
  56. uv_mutex_destroy(&wc->mutex);
  57. uv_cond_destroy(&wc->cond);
  58. uv_sem_destroy(&wc->sem_signaled);
  59. uv_sem_destroy(&wc->sem_waiting);
  60. }
  61. /* arg is a worker_config.
  62. * Call signal_cond then wait_cond.
  63. * Partner should call wait then signal. */
  64. static void worker(void* arg) {
  65. worker_config* c = arg;
  66. c->signal_cond(c, &c->posted_1);
  67. c->wait_cond(c, &c->posted_2);
  68. }
  69. /* 1. Signal a waiting waiter.
  70. * 2. Tell waiter we finished. */
  71. static void condvar_signal(worker_config* c, int* flag) {
  72. /* Wait until waiter holds mutex and is preparing to wait. */
  73. uv_sem_wait(&c->sem_waiting);
  74. /* Make sure waiter has begun waiting. */
  75. uv_mutex_lock(&c->mutex);
  76. /* Help waiter differentiate between spurious and legitimate wakeup. */
  77. ASSERT(*flag == 0);
  78. *flag = 1;
  79. if (c->use_broadcast)
  80. uv_cond_broadcast(&c->cond);
  81. else
  82. uv_cond_signal(&c->cond);
  83. uv_mutex_unlock(&c->mutex);
  84. /* Done signaling. */
  85. uv_sem_post(&c->sem_signaled);
  86. }
  87. /* 1. Wait on a signal.
  88. * 2. Ensure that the signaler finished. */
  89. static int condvar_wait(worker_config* c, const int* flag) {
  90. uv_mutex_lock(&c->mutex);
  91. /* Tell signal'er that I am waiting. */
  92. uv_sem_post(&c->sem_waiting);
  93. /* Wait until I get a non-spurious signal. */
  94. do {
  95. uv_cond_wait(&c->cond, &c->mutex);
  96. } while (*flag == 0);
  97. ASSERT(*flag == 1);
  98. uv_mutex_unlock(&c->mutex);
  99. /* Wait for my signal'er to finish. */
  100. uv_sem_wait(&c->sem_signaled);
  101. return 0;
  102. }
  103. /* uv_cond_wait: One thread signals, the other waits. */
  104. TEST_IMPL(condvar_1) {
  105. worker_config wc;
  106. uv_thread_t thread;
  107. /* Helper signal-then-wait. */
  108. worker_config_init(&wc, 0, condvar_signal, condvar_wait);
  109. ASSERT(0 == uv_thread_create(&thread, worker, &wc));
  110. /* We wait-then-signal. */
  111. ASSERT(0 == wc.wait_cond(&wc, &wc.posted_1));
  112. wc.signal_cond(&wc, &wc.posted_2);
  113. ASSERT(0 == uv_thread_join(&thread));
  114. worker_config_destroy(&wc);
  115. return 0;
  116. }
  117. /* uv_cond_wait: One thread broadcasts, the other waits. */
  118. TEST_IMPL(condvar_2) {
  119. worker_config wc;
  120. uv_thread_t thread;
  121. /* Helper to signal-then-wait. */
  122. worker_config_init(&wc, 1, condvar_signal, condvar_wait);
  123. ASSERT(0 == uv_thread_create(&thread, worker, &wc));
  124. /* We wait-then-signal. */
  125. ASSERT(0 == wc.wait_cond(&wc, &wc.posted_1));
  126. wc.signal_cond(&wc, &wc.posted_2);
  127. ASSERT(0 == uv_thread_join(&thread));
  128. worker_config_destroy(&wc);
  129. return 0;
  130. }
  131. /* 1. Wait on a signal (hopefully not timeout, else we'll hang).
  132. * 2. Ensure that the signaler finished. */
  133. static int condvar_timedwait(worker_config* c, const int* flag) {
  134. int r;
  135. r = 0;
  136. uv_mutex_lock(&c->mutex);
  137. /* Tell signal'er that I am waiting. */
  138. uv_sem_post(&c->sem_waiting);
  139. /* Wait until I get a non-spurious signal. */
  140. do {
  141. r = uv_cond_timedwait(&c->cond, &c->mutex, (uint64_t)(1 * 1e9)); /* 1 s */
  142. ASSERT(r == 0); /* Should not time out. */
  143. } while (*flag == 0);
  144. ASSERT(*flag == 1);
  145. uv_mutex_unlock(&c->mutex);
  146. /* Wait for my signal'er to finish. */
  147. uv_sem_wait(&c->sem_signaled);
  148. return r;
  149. }
  150. /* uv_cond_timedwait: One thread signals, the other timedwaits. */
  151. TEST_IMPL(condvar_3) {
  152. worker_config wc;
  153. uv_thread_t thread;
  154. /* Helper to signal-then-wait. */
  155. worker_config_init(&wc, 0, condvar_signal, condvar_timedwait);
  156. ASSERT(0 == uv_thread_create(&thread, worker, &wc));
  157. /* We wait-then-signal. */
  158. wc.wait_cond(&wc, &wc.posted_1);
  159. wc.signal_cond(&wc, &wc.posted_2);
  160. ASSERT(0 == uv_thread_join(&thread));
  161. worker_config_destroy(&wc);
  162. return 0;
  163. }
  164. /* uv_cond_timedwait: One thread broadcasts, the other waits. */
  165. TEST_IMPL(condvar_4) {
  166. worker_config wc;
  167. uv_thread_t thread;
  168. /* Helper to signal-then-wait. */
  169. worker_config_init(&wc, 1, condvar_signal, condvar_timedwait);
  170. ASSERT(0 == uv_thread_create(&thread, worker, &wc));
  171. /* We wait-then-signal. */
  172. wc.wait_cond(&wc, &wc.posted_1);
  173. wc.signal_cond(&wc, &wc.posted_2);
  174. ASSERT(0 == uv_thread_join(&thread));
  175. worker_config_destroy(&wc);
  176. return 0;
  177. }
  178. /* uv_cond_timedwait: One thread waits, no signal. Timeout should be delivered. */
  179. TEST_IMPL(condvar_5) {
  180. worker_config wc;
  181. int r;
  182. /* ns */
  183. uint64_t before;
  184. uint64_t after;
  185. uint64_t elapsed;
  186. uint64_t timeout;
  187. timeout = 100 * 1000 * 1000; /* 100 ms in ns */
  188. /* Mostly irrelevant. We need cond and mutex initialized. */
  189. worker_config_init(&wc, 0, NULL, NULL);
  190. uv_mutex_lock(&wc.mutex);
  191. /* We wait.
  192. * No signaler, so this will only return if timeout is delivered. */
  193. before = uv_hrtime();
  194. r = uv_cond_timedwait(&wc.cond, &wc.mutex, timeout);
  195. after = uv_hrtime();
  196. uv_mutex_unlock(&wc.mutex);
  197. /* It timed out. */
  198. ASSERT(r == UV_ETIMEDOUT);
  199. /* It must have taken at least timeout, modulo system timer ticks.
  200. * But it should not take too much longer.
  201. * cf. MSDN docs:
  202. * https://msdn.microsoft.com/en-us/library/ms687069(VS.85).aspx */
  203. elapsed = after - before;
  204. ASSERT(0.75 * timeout <= elapsed); /* 1.0 too large for Windows. */
  205. ASSERT(elapsed <= 5.0 * timeout); /* MacOS has reported failures up to 1.75. */
  206. worker_config_destroy(&wc);
  207. return 0;
  208. }