]> git.saurik.com Git - apple/libpthread.git/blame - tests/cond.c
libpthread-454.100.8.tar.gz
[apple/libpthread.git] / tests / cond.c
CommitLineData
964d3577
A
1#include <assert.h>
2#include <pthread.h>
3#include <stdio.h>
4#include <stdlib.h>
5#include <string.h>
6#include <unistd.h>
7#include <stdbool.h>
8#include <errno.h>
9#include <libkern/OSAtomic.h>
10
a0619f9c 11#include "darwintest_defaults.h"
2546420a
A
12#include <darwintest_utils.h>
13
964d3577
A
14struct context {
15 pthread_cond_t cond;
16 pthread_mutex_t mutex;
a0619f9c 17 pthread_cond_t ready_cond;
964d3577
A
18 long waiters;
19 long count;
a0619f9c
A
20 bool ready;
21 char _padding[7];
964d3577
A
22};
23
a0619f9c 24
2546420a 25static void *wait_thread(void *ptr) {
964d3577
A
26 struct context *context = ptr;
27
a0619f9c
A
28 // tell producer thread that we are ready
29 T_QUIET;
30 T_ASSERT_POSIX_ZERO(pthread_mutex_lock(&context->mutex), "pthread_mutex_lock");
31 context->ready = true;
32 T_QUIET;
33 T_ASSERT_POSIX_ZERO(pthread_cond_signal(&context->ready_cond), "pthread_cond_signal");
34
964d3577
A
35 bool loop = true;
36 while (loop) {
a0619f9c 37
964d3577
A
38 if (context->count > 0) {
39 ++context->waiters;
a0619f9c
A
40 T_QUIET;
41 T_ASSERT_POSIX_ZERO(pthread_cond_wait(&context->cond, &context->mutex), "[%ld] pthread_rwlock_unlock", context->count);
964d3577
A
42 --context->waiters;
43 --context->count;
44 } else {
45 loop = false;
46 }
a0619f9c 47
964d3577
A
48 }
49
a0619f9c
A
50 T_QUIET;
51 T_ASSERT_POSIX_ZERO(pthread_mutex_unlock(&context->mutex), "[%ld] pthread_mutex_unlock", context->count);
52
964d3577
A
53 return NULL;
54}
55
2546420a 56T_DECL(cond, "pthread_cond",
a0619f9c 57 T_META_ALL_VALID_ARCHS(YES), T_META_TIMEOUT(120), T_META_CHECK_LEAKS(NO))
964d3577
A
58{
59 struct context context = {
60 .cond = PTHREAD_COND_INITIALIZER,
61 .mutex = PTHREAD_MUTEX_INITIALIZER,
a0619f9c 62 .ready_cond = PTHREAD_COND_INITIALIZER,
964d3577 63 .waiters = 0,
a0619f9c
A
64 .count = 50000 * dt_ncpu(),
65 .ready = false,
964d3577
A
66 };
67 int i;
68 int res;
69 int threads = 2;
70 pthread_t p[threads];
71 for (i = 0; i < threads; ++i) {
a0619f9c
A
72 T_QUIET;
73 T_ASSERT_POSIX_ZERO(pthread_mutex_lock(&context.mutex), "pthread_mutex_lock");
74
75 context.ready = false;
76
77 T_QUIET;
78 T_ASSERT_POSIX_ZERO(pthread_create(&p[i], NULL, wait_thread, &context), "pthread_create");
79
80 do {
81 // mutex will be dropped and allow consumer thread to acquire
82 T_QUIET;
83 T_ASSERT_POSIX_ZERO(pthread_cond_wait(&context.ready_cond, &context.mutex), "pthread_cond_wait");
84 } while (context.ready == false);
85
86 T_QUIET;
87 T_ASSERT_POSIX_ZERO(pthread_mutex_unlock(&context.mutex), "pthread_mutex_lock");
88
89 T_LOG("Thread %d ready.", i);
964d3577
A
90 }
91
a0619f9c
A
92 T_LOG("All threads ready.");
93
964d3577
A
94 long half = context.count / 2;
95
96 bool loop = true;
97 while (loop) {
a0619f9c
A
98 T_QUIET;
99 T_ASSERT_POSIX_ZERO(pthread_mutex_lock(&context.mutex), "[%ld] pthread_mutex_lock", context.count);
964d3577
A
100 if (context.waiters) {
101 char *str;
102 if (context.count > half) {
103 str = "pthread_cond_broadcast";
104 res = pthread_cond_broadcast(&context.cond);
105 } else {
106 str = "pthread_cond_signal";
107 res = pthread_cond_signal(&context.cond);
108 }
a0619f9c
A
109 T_QUIET;
110 T_ASSERT_POSIX_ZERO(res, "[%ld] %s", context.count, str);
964d3577
A
111 }
112 if (context.count <= 0) {
113 loop = false;
2546420a 114 T_PASS("Completed stres test successfully.");
964d3577 115 }
a0619f9c
A
116
117 T_QUIET;
118 T_ASSERT_POSIX_ZERO(pthread_mutex_unlock(&context.mutex),
119 "[%ld] pthread_mutex_unlock", context.count);
964d3577 120 }
a0619f9c 121
964d3577 122 for (i = 0; i < threads; ++i) {
2546420a 123 T_ASSERT_POSIX_ZERO(pthread_join(p[i], NULL), NULL);
964d3577 124 }
964d3577 125}
c1f56ec9
A
126
127#pragma mark invalid concurrent mutex use
128
129// XXX ulock-based condvars don't detect concurrent waiting for now
130#if 0
131
132static pthread_cond_t concurrent_cond = PTHREAD_COND_INITIALIZER;
133
134static void *
135invalid_wait_thread(void *arg)
136{
137 pthread_mutex_t *mutex = arg;
138
139 int rc = pthread_mutex_lock(mutex);
140 T_ASSERT_POSIX_ZERO(rc, "lock mutex");
141
142 while (true) {
143 rc = pthread_cond_wait(&concurrent_cond, mutex);
144 if (rc == EINVAL) {
145 T_PASS("Detected EINVAL");
146 T_END;
147 } else {
148 T_ASSERT_POSIX_ZERO(rc, "cond_wait");
149 }
150 }
151}
152
153T_DECL(cond_invalid_concurrent_mutex, "Detect concurrent waits with different mutexes as invalid")
154{
155 int rc;
156 pthread_t threads[2];
157 pthread_mutex_t mutexes[2] = {
158 PTHREAD_MUTEX_INITIALIZER,
159 PTHREAD_MUTEX_INITIALIZER,
160 };
161
162 for (int i = 0; i < 2; i++) {
163 rc = pthread_create(&threads[i], NULL, invalid_wait_thread,
164 &mutexes[i]);
165 T_ASSERT_POSIX_ZERO(rc, "pthread_create");
166 }
167
168 // will not return
169 pthread_join(threads[0], NULL);
170}
171
172#endif
173
174#pragma mark mutex ping pong test
175
176struct cond_mutex_ping_pong_ctx_s {
177 pthread_mutex_t mutex;
178 int arrived;
179 int group;
180};
181
182static struct {
183 pthread_mutex_t sync_mutex;
184 pthread_cond_t sync_cond;
185 int group;
186 pthread_cond_t shared_cond;
187} ping_pong = {
188 .sync_mutex = PTHREAD_MUTEX_INITIALIZER,
189 .sync_cond = PTHREAD_COND_INITIALIZER,
190 .group = 0,
191 .shared_cond = PTHREAD_COND_INITIALIZER,
192};
193
194#define PING_PONG_NGROUPS 2
195#define PING_PONG_GROUP_NTHREADS 3
196#define PING_PONG_ITERATIONS 5000
197
198static void *
199ping_pong_thread(void *arg)
200{
201 int rc;
202 struct cond_mutex_ping_pong_ctx_s *ctx = arg;
203
204 for (int i = 1; i < PING_PONG_ITERATIONS; i++) {
205 if (i % 5000 == 0) {
206 T_LOG("Iteration %d", i);
207 }
208
209 // wait for our turn to synchronize on the shared_cond barrier
210 rc = pthread_mutex_lock(&ping_pong.sync_mutex);
211 T_QUIET; T_ASSERT_POSIX_ZERO(rc, "lock sync_mutex");
212
213 while (ping_pong.group != ctx->group) {
214 rc = pthread_cond_wait(&ping_pong.sync_cond, &ping_pong.sync_mutex);
215 T_QUIET; T_ASSERT_POSIX_ZERO(rc, "sync cond_wait");
216 }
217
218 rc = pthread_mutex_unlock(&ping_pong.sync_mutex);
219 T_QUIET; T_ASSERT_POSIX_ZERO(rc, "unlock sync_mutex");
220
221 rc = pthread_mutex_lock(&ctx->mutex);
222 T_QUIET; T_ASSERT_POSIX_ZERO(rc, "lock mutex");
223
224 ctx->arrived++;
225
226 if (ctx->arrived == i * PING_PONG_GROUP_NTHREADS) {
227 // end our turn with shared_cond
228 rc = pthread_cond_broadcast(&ping_pong.shared_cond);
229 T_QUIET; T_ASSERT_POSIX_ZERO(rc, "shared cond_broadcast");
230
231 // let the next group begin
232 rc = pthread_mutex_lock(&ping_pong.sync_mutex);
233 T_QUIET; T_ASSERT_POSIX_ZERO(rc, "lock sync_mutex");
234
235 ping_pong.group = (ping_pong.group + 1) % PING_PONG_NGROUPS;
236
237 rc = pthread_mutex_unlock(&ping_pong.sync_mutex);
238 T_QUIET; T_ASSERT_POSIX_ZERO(rc, "unlock sync_mutex");
239
240 // for fun, do this broadcast outside the mutex
241 rc = pthread_cond_broadcast(&ping_pong.sync_cond);
242 T_QUIET; T_ASSERT_POSIX_ZERO(rc, "sync cond_broadcast");
243
244 } else {
245 while (ctx->arrived < i * PING_PONG_GROUP_NTHREADS) {
246 rc = pthread_cond_wait(&ping_pong.shared_cond, &ctx->mutex);
247 T_QUIET; T_ASSERT_POSIX_ZERO(rc, "shared cond_wait");
248
249 // TODO: assert that you now hold the correct group mutex
250 }
251 }
252
253 rc = pthread_mutex_unlock(&ctx->mutex);
254 T_QUIET; T_ASSERT_POSIX_ZERO(rc, "unlock mutex");
255 }
256
257 return NULL;
258}
259
260T_DECL(cond_mutex_ping_pong, "Wait on the same condition variable with different mutexes",
261 T_META_ENVVAR("PTHREAD_MUTEX_USE_ULOCK=1"))
262{
263 int rc;
264 pthread_t threads[PING_PONG_NGROUPS][PING_PONG_GROUP_NTHREADS];
265 struct cond_mutex_ping_pong_ctx_s ctxs[PING_PONG_NGROUPS];
266
267 for (int i = 0; i < PING_PONG_NGROUPS; i++) {
268 struct cond_mutex_ping_pong_ctx_s *ctx = &ctxs[i];
269 *ctx = (struct cond_mutex_ping_pong_ctx_s){
270 .mutex = PTHREAD_MUTEX_INITIALIZER,
271 .group = i,
272 };
273
274 for (int j = 0; j < PING_PONG_GROUP_NTHREADS; j++) {
275 rc = pthread_create(&threads[i][j], NULL, ping_pong_thread, ctx);
276 T_ASSERT_POSIX_ZERO(rc, "pthread_create");
277 }
278 }
279
280 for (int i = 0; i < PING_PONG_NGROUPS; i++) {
281 for (int j = 0; j < PING_PONG_GROUP_NTHREADS; j++) {
282 rc = pthread_join(threads[i][j], NULL);
283 T_ASSERT_POSIX_ZERO(rc, "pthread_join");
284 }
285 }
286}
287
288#pragma mark signal_thread_np tests
289
290static struct signal_thread_ctx_s {
291 pthread_mutex_t mutex;
292 pthread_cond_t cond;
293 bool signaled;
294} signal_thread_ctx = {
295 .mutex = PTHREAD_MUTEX_INITIALIZER,
296 .cond = PTHREAD_COND_INITIALIZER,
297};
298
299static void *
300chosen_waiter(void *arg __unused)
301{
302 struct signal_thread_ctx_s *ctx = &signal_thread_ctx;
303
304 int rc = pthread_mutex_lock(&ctx->mutex);
305 T_ASSERT_POSIX_ZERO(rc, "chosen waiter lock");
306
307 while (!ctx->signaled) {
308 rc = pthread_cond_wait(&ctx->cond, &ctx->mutex);
309 T_ASSERT_POSIX_ZERO(rc, "chosen waiter cond_wait");
310 }
311
312 T_PASS("chosen waiter woke");
313 T_END;
314}
315
316static void *
317other_waiter_thread(void *arg __unused)
318{
319 struct signal_thread_ctx_s *ctx = &signal_thread_ctx;
320
321 int rc = pthread_mutex_lock(&ctx->mutex);
322 T_ASSERT_POSIX_ZERO(rc, "other waiter lock");
323
324 while (true) {
325 rc = pthread_cond_wait(&ctx->cond, &ctx->mutex);
326 T_ASSERT_POSIX_ZERO(rc, "other waiter cond_wait");
327 }
328
329 T_ASSERT_FAIL("Not reached");
330 return NULL;
331}
332
333T_DECL(cond_signal_thread_np_waiting, "signal a specific thread that's waiting")
334{
335 int rc;
336 struct signal_thread_ctx_s *ctx = &signal_thread_ctx;
337
338 pthread_attr_t other_attr;
339 rc = pthread_attr_init(&other_attr);
340 T_QUIET; T_ASSERT_POSIX_ZERO(rc, "pthread_attr_init");
341
342 rc = pthread_attr_set_qos_class_np(&other_attr,
343 QOS_CLASS_USER_INTERACTIVE, 0);
344 T_ASSERT_POSIX_ZERO(rc, "pthread_attr_set_qos_class_np");
345
346 pthread_t other;
347 rc = pthread_create(&other, &other_attr, other_waiter_thread, NULL);
348 T_ASSERT_POSIX_ZERO(rc, "create other thread");
349
350 pthread_t chosen;
351 rc = pthread_create(&chosen, NULL, chosen_waiter, NULL);
352 T_ASSERT_POSIX_ZERO(rc, "create chosen thread");
353
354 T_LOG("Waiting for threads to wait");
355 sleep(5);
356
357 rc = pthread_mutex_lock(&ctx->mutex);
358 T_ASSERT_POSIX_ZERO(rc, "lock mutex");
359
360 ctx->signaled = true;
361
362 rc = pthread_mutex_unlock(&ctx->mutex);
363 T_ASSERT_POSIX_ZERO(rc, "unlock mutex");
364
365 rc = pthread_cond_signal_thread_np(&ctx->cond, chosen);
366 T_ASSERT_POSIX_ZERO(rc, "cond_signal_thread_np");
367
368 pthread_join(chosen, NULL);
369}
370
371static void *
372absent_chosen_waiter(void *arg __unused)
373{
374 T_LOG("chosen thread doing nothing forever");
375 while (true) {
376 sleep(100);
377 }
378
379 T_ASSERT_FAIL("Not reached");
380 return NULL;
381}
382
383static void *
384not_absent_waiter(void *arg __unused)
385{
386 struct signal_thread_ctx_s *ctx = &signal_thread_ctx;
387
388 int rc = pthread_mutex_lock(&ctx->mutex);
389 T_ASSERT_POSIX_ZERO(rc, "other waiter lock");
390
391 while (!ctx->signaled) {
392 rc = pthread_cond_wait(&ctx->cond, &ctx->mutex);
393 T_ASSERT_POSIX_ZERO(rc, "other waiter cond_wait");
394 }
395
396 T_PASS("other waiter woke");
397 T_END;
398}
399
400T_DECL(cond_signal_thread_np_not_waiting, "signal a specific thread that isn't waiting")
401{
402 int rc;
403 struct signal_thread_ctx_s *ctx = &signal_thread_ctx;
404
405 pthread_t other;
406 rc = pthread_create(&other, NULL, not_absent_waiter, NULL);
407 T_ASSERT_POSIX_ZERO(rc, "create other thread");
408
409 pthread_t chosen;
410 rc = pthread_create(&chosen, NULL, absent_chosen_waiter, NULL);
411 T_ASSERT_POSIX_ZERO(rc, "create chosen thread");
412
413 T_LOG("Waiting for threads to wait");
414 sleep(5);
415
416 rc = pthread_mutex_lock(&ctx->mutex);
417 T_ASSERT_POSIX_ZERO(rc, "lock mutex");
418
419 ctx->signaled = true;
420
421 rc = pthread_mutex_unlock(&ctx->mutex);
422 T_ASSERT_POSIX_ZERO(rc, "unlock mutex");
423
424 rc = pthread_cond_signal_thread_np(&ctx->cond, chosen);
425 T_ASSERT_POSIX_ZERO(rc, "cond_signal_thread_np");
426
427 pthread_join(other, NULL);
428}
429
430#pragma mark cancel signal race test
431
432static struct cancel_signal_race_context_s {
433 pthread_mutex_t mutex;
434 pthread_cond_t cond;
435} cancel_signal_race_context = {
436 .mutex = PTHREAD_MUTEX_INITIALIZER,
437 .cond = PTHREAD_COND_INITIALIZER,
438};
439
440static void
441cancelee_cleanup_handler(void *arg __unused)
442{
443 T_LOG("cancelee cleanup handler");
444
445 struct cancel_signal_race_context_s *ctx = &cancel_signal_race_context;
446 int rc = pthread_mutex_unlock(&ctx->mutex);
447 T_ASSERT_POSIX_ZERO(rc, "cleanup mutex unlock");
448}
449
450static void *
451cancelee_thread(void *arg __unused)
452{
453 struct cancel_signal_race_context_s *ctx = &cancel_signal_race_context;
454
455 int rc = pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
456 T_QUIET; T_ASSERT_POSIX_ZERO(rc, "disabled cancelation of cancelee thread");
457
458 rc = pthread_mutex_lock(&ctx->mutex);
459 T_ASSERT_POSIX_ZERO(rc, "cancelee lock");
460
461 rc = pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
462 if (rc) {
463 // manual T_QUIET since we can't safely call into libdarwintest with
464 // cancelation enabled
465 T_ASSERT_POSIX_ZERO(rc, "cancelation re-enabled");
466 }
467
468 pthread_cleanup_push(cancelee_cleanup_handler, NULL);
469
470 rc = pthread_cond_wait(&ctx->cond, &ctx->mutex);
471
472 pthread_cleanup_pop(0);
473
474 int rc2 = pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
475 T_QUIET; T_ASSERT_POSIX_ZERO(rc2, "re-disabled cancelation of cancelee thread");
476
477 // If we make it here we didn't manage to exercise the race, but that's
478 // legal.
479 T_ASSERT_POSIX_ZERO(rc, "cancelee woke from cond_wait");
480
481 rc = pthread_mutex_unlock(&ctx->mutex);
482 T_ASSERT_POSIX_ZERO(rc, "cancelee unlocked");
483
484 return NULL;
485}
486
487static struct {
488 int dummy;
489} other_thread_timed_out;
490
491static void *
492other_racing_thread(void *arg __unused)
493{
494 struct cancel_signal_race_context_s *ctx = &cancel_signal_race_context;
495
496 int rc = pthread_mutex_lock(&ctx->mutex);
497 T_ASSERT_POSIX_ZERO(rc, "other lock");
498
499 struct timespec ts = {
500 .tv_sec = 10,
501 };
502
503 rc = pthread_cond_timedwait_relative_np(&ctx->cond, &ctx->mutex, &ts);
504
505 int rc2 = pthread_mutex_unlock(&ctx->mutex);
506 T_ASSERT_POSIX_ZERO(rc2, "other thread unlocked");
507
508 if (rc == ETIMEDOUT) {
509 T_LOG("other thread timed out");
510 return &other_thread_timed_out;
511 } else {
512 // XXX if we change the algorithm in a way that can lead to spurious
513 // wakeups then this logic might become invalid, but at this point it's
514 // not possible
515 T_ASSERT_POSIX_ZERO(rc, "other thread woke from wait");
516 return NULL;
517 }
518}
519
520T_DECL(cond_cancel_signal_race, "Validate waiter cancelation does not eat wakes",
521 T_META_ENVVAR("PTHREAD_MUTEX_USE_ULOCK=1"))
522{
523 int rc;
524 struct cancel_signal_race_context_s *ctx = &cancel_signal_race_context;
525
526 pthread_attr_t cancelee_attr;
527 rc = pthread_attr_init(&cancelee_attr);
528 T_QUIET; T_ASSERT_POSIX_ZERO(rc, "pthread_attr_init");
529
530 rc = pthread_attr_set_qos_class_np(&cancelee_attr,
531 QOS_CLASS_USER_INTERACTIVE, 0);
532 T_ASSERT_POSIX_ZERO(rc, "pthread_attr_set_qos_class_np");
533
534 pthread_t cancelee;
535 rc = pthread_create(&cancelee, &cancelee_attr, cancelee_thread, NULL);
536 T_ASSERT_POSIX_SUCCESS(rc, "create cancelee");
537
538 pthread_attr_t other_attr;
539 rc = pthread_attr_init(&other_attr);
540 T_QUIET; T_ASSERT_POSIX_ZERO(rc, "pthread_attr_init");
541
542 rc = pthread_attr_set_qos_class_np(&other_attr,
543 QOS_CLASS_USER_INITIATED, 0);
544 T_ASSERT_POSIX_ZERO(rc, "pthread_attr_set_qos_class_np");
545
546 pthread_t other;
547 rc = pthread_create(&other, &other_attr, other_racing_thread, NULL);
548 T_ASSERT_POSIX_SUCCESS(rc, "create other thread");
549
550 // Give them time to wait
551 // TODO: find some reliable way of waiting until they're really blocked?
552 sleep(2);
553
554 rc = pthread_cond_signal(&ctx->cond);
555
556 // Now quickly cancel, hopefully before they make it to userspace
557 (void)pthread_cancel(cancelee);
558
559 T_ASSERT_POSIX_ZERO(rc, "signal cancelee");
560
561 void *cancelee_retval, *other_retval;
562
563 rc = pthread_join(cancelee, &cancelee_retval);
564 T_ASSERT_POSIX_ZERO(rc, "join cancelee");
565
566 rc = pthread_join(other, &other_retval);
567 T_ASSERT_POSIX_ZERO(rc, "join other");
568
569 if (cancelee_retval == PTHREAD_CANCELED) {
570 T_LOG("cancelee was canceled");
571 T_ASSERT_EQ(other_retval, NULL, "other thread must have woken");
572 } else {
573 T_LOG("cancelee was not canceled quickly enough");
574 T_ASSERT_EQ(cancelee_retval, NULL, "cancelee returned success");
575 T_ASSERT_EQ(other_retval, &other_thread_timed_out, "other thread timed out");
576 }
577}