9 #include <libkern/OSAtomic.h>
11 #include "darwintest_defaults.h"
12 #include <darwintest_utils.h>
16 pthread_mutex_t mutex
;
17 pthread_cond_t ready_cond
;
25 static void *wait_thread(void *ptr
) {
26 struct context
*context
= ptr
;
28 // tell producer thread that we are ready
30 T_ASSERT_POSIX_ZERO(pthread_mutex_lock(&context
->mutex
), "pthread_mutex_lock");
31 context
->ready
= true;
33 T_ASSERT_POSIX_ZERO(pthread_cond_signal(&context
->ready_cond
), "pthread_cond_signal");
38 if (context
->count
> 0) {
41 T_ASSERT_POSIX_ZERO(pthread_cond_wait(&context
->cond
, &context
->mutex
), "[%ld] pthread_rwlock_unlock", context
->count
);
51 T_ASSERT_POSIX_ZERO(pthread_mutex_unlock(&context
->mutex
), "[%ld] pthread_mutex_unlock", context
->count
);
56 T_DECL(cond
, "pthread_cond",
57 T_META_ALL_VALID_ARCHS(YES
), T_META_TIMEOUT(120), T_META_CHECK_LEAKS(NO
))
59 struct context context
= {
60 .cond
= PTHREAD_COND_INITIALIZER
,
61 .mutex
= PTHREAD_MUTEX_INITIALIZER
,
62 .ready_cond
= PTHREAD_COND_INITIALIZER
,
64 .count
= 50000 * dt_ncpu(),
71 for (i
= 0; i
< threads
; ++i
) {
73 T_ASSERT_POSIX_ZERO(pthread_mutex_lock(&context
.mutex
), "pthread_mutex_lock");
75 context
.ready
= false;
78 T_ASSERT_POSIX_ZERO(pthread_create(&p
[i
], NULL
, wait_thread
, &context
), "pthread_create");
81 // mutex will be dropped and allow consumer thread to acquire
83 T_ASSERT_POSIX_ZERO(pthread_cond_wait(&context
.ready_cond
, &context
.mutex
), "pthread_cond_wait");
84 } while (context
.ready
== false);
87 T_ASSERT_POSIX_ZERO(pthread_mutex_unlock(&context
.mutex
), "pthread_mutex_lock");
89 T_LOG("Thread %d ready.", i
);
92 T_LOG("All threads ready.");
94 long half
= context
.count
/ 2;
99 T_ASSERT_POSIX_ZERO(pthread_mutex_lock(&context
.mutex
), "[%ld] pthread_mutex_lock", context
.count
);
100 if (context
.waiters
) {
102 if (context
.count
> half
) {
103 str
= "pthread_cond_broadcast";
104 res
= pthread_cond_broadcast(&context
.cond
);
106 str
= "pthread_cond_signal";
107 res
= pthread_cond_signal(&context
.cond
);
110 T_ASSERT_POSIX_ZERO(res
, "[%ld] %s", context
.count
, str
);
112 if (context
.count
<= 0) {
114 T_PASS("Completed stres test successfully.");
118 T_ASSERT_POSIX_ZERO(pthread_mutex_unlock(&context
.mutex
),
119 "[%ld] pthread_mutex_unlock", context
.count
);
122 for (i
= 0; i
< threads
; ++i
) {
123 T_ASSERT_POSIX_ZERO(pthread_join(p
[i
], NULL
), NULL
);
127 #pragma mark invalid concurrent mutex use
129 // XXX ulock-based condvars don't detect concurrent waiting for now
132 static pthread_cond_t concurrent_cond
= PTHREAD_COND_INITIALIZER
;
135 invalid_wait_thread(void *arg
)
137 pthread_mutex_t
*mutex
= arg
;
139 int rc
= pthread_mutex_lock(mutex
);
140 T_ASSERT_POSIX_ZERO(rc
, "lock mutex");
143 rc
= pthread_cond_wait(&concurrent_cond
, mutex
);
145 T_PASS("Detected EINVAL");
148 T_ASSERT_POSIX_ZERO(rc
, "cond_wait");
153 T_DECL(cond_invalid_concurrent_mutex
, "Detect concurrent waits with different mutexes as invalid")
156 pthread_t threads
[2];
157 pthread_mutex_t mutexes
[2] = {
158 PTHREAD_MUTEX_INITIALIZER
,
159 PTHREAD_MUTEX_INITIALIZER
,
162 for (int i
= 0; i
< 2; i
++) {
163 rc
= pthread_create(&threads
[i
], NULL
, invalid_wait_thread
,
165 T_ASSERT_POSIX_ZERO(rc
, "pthread_create");
169 pthread_join(threads
[0], NULL
);
174 #pragma mark mutex ping pong test
176 struct cond_mutex_ping_pong_ctx_s
{
177 pthread_mutex_t mutex
;
183 pthread_mutex_t sync_mutex
;
184 pthread_cond_t sync_cond
;
186 pthread_cond_t shared_cond
;
188 .sync_mutex
= PTHREAD_MUTEX_INITIALIZER
,
189 .sync_cond
= PTHREAD_COND_INITIALIZER
,
191 .shared_cond
= PTHREAD_COND_INITIALIZER
,
194 #define PING_PONG_NGROUPS 2
195 #define PING_PONG_GROUP_NTHREADS 3
196 #define PING_PONG_ITERATIONS 5000
199 ping_pong_thread(void *arg
)
202 struct cond_mutex_ping_pong_ctx_s
*ctx
= arg
;
204 for (int i
= 1; i
< PING_PONG_ITERATIONS
; i
++) {
206 T_LOG("Iteration %d", i
);
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");
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");
218 rc
= pthread_mutex_unlock(&ping_pong
.sync_mutex
);
219 T_QUIET
; T_ASSERT_POSIX_ZERO(rc
, "unlock sync_mutex");
221 rc
= pthread_mutex_lock(&ctx
->mutex
);
222 T_QUIET
; T_ASSERT_POSIX_ZERO(rc
, "lock mutex");
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");
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");
235 ping_pong
.group
= (ping_pong
.group
+ 1) % PING_PONG_NGROUPS
;
237 rc
= pthread_mutex_unlock(&ping_pong
.sync_mutex
);
238 T_QUIET
; T_ASSERT_POSIX_ZERO(rc
, "unlock sync_mutex");
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");
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");
249 // TODO: assert that you now hold the correct group mutex
253 rc
= pthread_mutex_unlock(&ctx
->mutex
);
254 T_QUIET
; T_ASSERT_POSIX_ZERO(rc
, "unlock mutex");
260 T_DECL(cond_mutex_ping_pong
, "Wait on the same condition variable with different mutexes",
261 T_META_ENVVAR("PTHREAD_MUTEX_USE_ULOCK=1"))
264 pthread_t threads
[PING_PONG_NGROUPS
][PING_PONG_GROUP_NTHREADS
];
265 struct cond_mutex_ping_pong_ctx_s ctxs
[PING_PONG_NGROUPS
];
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
,
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");
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");
288 #pragma mark signal_thread_np tests
290 static struct signal_thread_ctx_s
{
291 pthread_mutex_t mutex
;
294 } signal_thread_ctx
= {
295 .mutex
= PTHREAD_MUTEX_INITIALIZER
,
296 .cond
= PTHREAD_COND_INITIALIZER
,
300 chosen_waiter(void *arg __unused
)
302 struct signal_thread_ctx_s
*ctx
= &signal_thread_ctx
;
304 int rc
= pthread_mutex_lock(&ctx
->mutex
);
305 T_ASSERT_POSIX_ZERO(rc
, "chosen waiter lock");
307 while (!ctx
->signaled
) {
308 rc
= pthread_cond_wait(&ctx
->cond
, &ctx
->mutex
);
309 T_ASSERT_POSIX_ZERO(rc
, "chosen waiter cond_wait");
312 T_PASS("chosen waiter woke");
317 other_waiter_thread(void *arg __unused
)
319 struct signal_thread_ctx_s
*ctx
= &signal_thread_ctx
;
321 int rc
= pthread_mutex_lock(&ctx
->mutex
);
322 T_ASSERT_POSIX_ZERO(rc
, "other waiter lock");
325 rc
= pthread_cond_wait(&ctx
->cond
, &ctx
->mutex
);
326 T_ASSERT_POSIX_ZERO(rc
, "other waiter cond_wait");
329 T_ASSERT_FAIL("Not reached");
333 T_DECL(cond_signal_thread_np_waiting
, "signal a specific thread that's waiting")
336 struct signal_thread_ctx_s
*ctx
= &signal_thread_ctx
;
338 pthread_attr_t other_attr
;
339 rc
= pthread_attr_init(&other_attr
);
340 T_QUIET
; T_ASSERT_POSIX_ZERO(rc
, "pthread_attr_init");
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");
347 rc
= pthread_create(&other
, &other_attr
, other_waiter_thread
, NULL
);
348 T_ASSERT_POSIX_ZERO(rc
, "create other thread");
351 rc
= pthread_create(&chosen
, NULL
, chosen_waiter
, NULL
);
352 T_ASSERT_POSIX_ZERO(rc
, "create chosen thread");
354 T_LOG("Waiting for threads to wait");
357 rc
= pthread_mutex_lock(&ctx
->mutex
);
358 T_ASSERT_POSIX_ZERO(rc
, "lock mutex");
360 ctx
->signaled
= true;
362 rc
= pthread_mutex_unlock(&ctx
->mutex
);
363 T_ASSERT_POSIX_ZERO(rc
, "unlock mutex");
365 rc
= pthread_cond_signal_thread_np(&ctx
->cond
, chosen
);
366 T_ASSERT_POSIX_ZERO(rc
, "cond_signal_thread_np");
368 pthread_join(chosen
, NULL
);
372 absent_chosen_waiter(void *arg __unused
)
374 T_LOG("chosen thread doing nothing forever");
379 T_ASSERT_FAIL("Not reached");
384 not_absent_waiter(void *arg __unused
)
386 struct signal_thread_ctx_s
*ctx
= &signal_thread_ctx
;
388 int rc
= pthread_mutex_lock(&ctx
->mutex
);
389 T_ASSERT_POSIX_ZERO(rc
, "other waiter lock");
391 while (!ctx
->signaled
) {
392 rc
= pthread_cond_wait(&ctx
->cond
, &ctx
->mutex
);
393 T_ASSERT_POSIX_ZERO(rc
, "other waiter cond_wait");
396 T_PASS("other waiter woke");
400 T_DECL(cond_signal_thread_np_not_waiting
, "signal a specific thread that isn't waiting")
403 struct signal_thread_ctx_s
*ctx
= &signal_thread_ctx
;
406 rc
= pthread_create(&other
, NULL
, not_absent_waiter
, NULL
);
407 T_ASSERT_POSIX_ZERO(rc
, "create other thread");
410 rc
= pthread_create(&chosen
, NULL
, absent_chosen_waiter
, NULL
);
411 T_ASSERT_POSIX_ZERO(rc
, "create chosen thread");
413 T_LOG("Waiting for threads to wait");
416 rc
= pthread_mutex_lock(&ctx
->mutex
);
417 T_ASSERT_POSIX_ZERO(rc
, "lock mutex");
419 ctx
->signaled
= true;
421 rc
= pthread_mutex_unlock(&ctx
->mutex
);
422 T_ASSERT_POSIX_ZERO(rc
, "unlock mutex");
424 rc
= pthread_cond_signal_thread_np(&ctx
->cond
, chosen
);
425 T_ASSERT_POSIX_ZERO(rc
, "cond_signal_thread_np");
427 pthread_join(other
, NULL
);
430 #pragma mark cancel signal race test
432 static struct cancel_signal_race_context_s
{
433 pthread_mutex_t mutex
;
435 } cancel_signal_race_context
= {
436 .mutex
= PTHREAD_MUTEX_INITIALIZER
,
437 .cond
= PTHREAD_COND_INITIALIZER
,
441 cancelee_cleanup_handler(void *arg __unused
)
443 T_LOG("cancelee cleanup handler");
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");
451 cancelee_thread(void *arg __unused
)
453 struct cancel_signal_race_context_s
*ctx
= &cancel_signal_race_context
;
455 int rc
= pthread_setcancelstate(PTHREAD_CANCEL_DISABLE
, NULL
);
456 T_QUIET
; T_ASSERT_POSIX_ZERO(rc
, "disabled cancelation of cancelee thread");
458 rc
= pthread_mutex_lock(&ctx
->mutex
);
459 T_ASSERT_POSIX_ZERO(rc
, "cancelee lock");
461 rc
= pthread_setcancelstate(PTHREAD_CANCEL_ENABLE
, NULL
);
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");
468 pthread_cleanup_push(cancelee_cleanup_handler
, NULL
);
470 rc
= pthread_cond_wait(&ctx
->cond
, &ctx
->mutex
);
472 pthread_cleanup_pop(0);
474 int rc2
= pthread_setcancelstate(PTHREAD_CANCEL_DISABLE
, NULL
);
475 T_QUIET
; T_ASSERT_POSIX_ZERO(rc2
, "re-disabled cancelation of cancelee thread");
477 // If we make it here we didn't manage to exercise the race, but that's
479 T_ASSERT_POSIX_ZERO(rc
, "cancelee woke from cond_wait");
481 rc
= pthread_mutex_unlock(&ctx
->mutex
);
482 T_ASSERT_POSIX_ZERO(rc
, "cancelee unlocked");
489 } other_thread_timed_out
;
492 other_racing_thread(void *arg __unused
)
494 struct cancel_signal_race_context_s
*ctx
= &cancel_signal_race_context
;
496 int rc
= pthread_mutex_lock(&ctx
->mutex
);
497 T_ASSERT_POSIX_ZERO(rc
, "other lock");
499 struct timespec ts
= {
503 rc
= pthread_cond_timedwait_relative_np(&ctx
->cond
, &ctx
->mutex
, &ts
);
505 int rc2
= pthread_mutex_unlock(&ctx
->mutex
);
506 T_ASSERT_POSIX_ZERO(rc2
, "other thread unlocked");
508 if (rc
== ETIMEDOUT
) {
509 T_LOG("other thread timed out");
510 return &other_thread_timed_out
;
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
515 T_ASSERT_POSIX_ZERO(rc
, "other thread woke from wait");
520 T_DECL(cond_cancel_signal_race
, "Validate waiter cancelation does not eat wakes",
521 T_META_ENVVAR("PTHREAD_MUTEX_USE_ULOCK=1"))
524 struct cancel_signal_race_context_s
*ctx
= &cancel_signal_race_context
;
526 pthread_attr_t cancelee_attr
;
527 rc
= pthread_attr_init(&cancelee_attr
);
528 T_QUIET
; T_ASSERT_POSIX_ZERO(rc
, "pthread_attr_init");
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");
535 rc
= pthread_create(&cancelee
, &cancelee_attr
, cancelee_thread
, NULL
);
536 T_ASSERT_POSIX_SUCCESS(rc
, "create cancelee");
538 pthread_attr_t other_attr
;
539 rc
= pthread_attr_init(&other_attr
);
540 T_QUIET
; T_ASSERT_POSIX_ZERO(rc
, "pthread_attr_init");
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");
547 rc
= pthread_create(&other
, &other_attr
, other_racing_thread
, NULL
);
548 T_ASSERT_POSIX_SUCCESS(rc
, "create other thread");
550 // Give them time to wait
551 // TODO: find some reliable way of waiting until they're really blocked?
554 rc
= pthread_cond_signal(&ctx
->cond
);
556 // Now quickly cancel, hopefully before they make it to userspace
557 (void)pthread_cancel(cancelee
);
559 T_ASSERT_POSIX_ZERO(rc
, "signal cancelee");
561 void *cancelee_retval
, *other_retval
;
563 rc
= pthread_join(cancelee
, &cancelee_retval
);
564 T_ASSERT_POSIX_ZERO(rc
, "join cancelee");
566 rc
= pthread_join(other
, &other_retval
);
567 T_ASSERT_POSIX_ZERO(rc
, "join other");
569 if (cancelee_retval
== PTHREAD_CANCELED
) {
570 T_LOG("cancelee was canceled");
571 T_ASSERT_EQ(other_retval
, NULL
, "other thread must have woken");
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");