7 #include <TargetConditionals.h>
9 #include <pthread/pthread_spis.h>
11 #include <sys/sysctl.h>
13 #include "darwintest_defaults.h"
14 #include <darwintest_multiprocess.h>
16 // <rdar://problem/38810583> this test case is intended to test for the
17 // specific issue found in this radar. That is, if:
19 // 1. A mutex is in first-fit policy mode, and
20 // 2. is used as the mutex in a pthread_cond_wait (or timedwait), and
21 // 3. the mutex has the K-bit set but has no kernel waiters, and
22 // 4. the cvwait call preposts an unlock to the mutex
24 // Under these conditions, the fact that the cvwait preposted an unlock to
25 // the paired mutex is lost during the call. The P-bit was never returned to
26 // userspace and the kwq in the kernel would continue to exist. If the same
27 // uaddr is then reused as another synchroniser type then we would often
28 // return EINVAL from the wait/lock function.
30 // So this test is attempting to:
32 // 1. Repeatedly bang on a mutex+cvar for a number of iterations in the
33 // hope of triggering a cvwait prepost situation.
34 // 2. Then destroy both the mutex and cvar, and reinitialise each memory
35 // location as the opposite type of synchroniser. Then cvwait once to
36 // trigger the failure condition.
40 pthread_mutex_t mutex
;
44 pthread_mutex_t mutex2
;
52 static void *test_cond(void *ptr
) {
53 struct context
*context
= ptr
;
56 res
= pthread_cond_wait(&context
->cond
, &context
->mutex2
);
57 T_ASSERT_POSIX_ZERO(res
, "condition wait on condvar completed");
58 res
= pthread_mutex_unlock(&context
->mutex2
);
59 T_ASSERT_POSIX_ZERO(res
, "unlock condvar mutex");
63 static void *test_cond_wake(void *ptr
) {
64 struct context
*context
= ptr
;
67 res
= pthread_mutex_lock(&context
->mutex2
);
68 T_ASSERT_POSIX_ZERO(res
, "locked condvar mutex");
69 res
= pthread_cond_signal(&context
->cond
);
70 T_ASSERT_POSIX_ZERO(res
, "condvar signalled");
71 res
= pthread_mutex_unlock(&context
->mutex2
);
72 T_ASSERT_POSIX_ZERO(res
, "dropped condvar mutex");
77 static void *test_thread(void *ptr
) {
80 struct context
*context
= ptr
;
90 str
= "pthread_mutex_lock";
91 res
= pthread_mutex_lock(&context
->mutex
);
93 str
= "pthread_mutex_trylock";
94 res
= pthread_mutex_trylock(&context
->mutex
);
97 if (try && res
== EBUSY
) {
100 T_ASSERT_POSIX_ZERO(res
, "[%ld] %s", context
->count
, str
);
103 old
= __sync_fetch_and_or(&context
->value
, 1);
104 if ((old
& 1) != 0) {
105 T_FAIL("[%ld] OR %lx\n", context
->count
, old
);
108 old
= __sync_fetch_and_and(&context
->value
, 0);
109 if ((old
& 1) == 0) {
110 T_FAIL("[%ld] AND %lx\n", context
->count
, old
);
113 if (cond
&& !context
->waiter
) {
115 struct timespec ts
= {
117 .tv_nsec
= 10ull * NSEC_PER_MSEC
,
120 res
= pthread_cond_timedwait_relative_np(&context
->cond2
, &context
->mutex
, &ts
);
121 if (res
== ETIMEDOUT
) {
122 // ignore, should be the last thread out
124 T_ASSERT_POSIX_ZERO(res
, "[%ld] pthread_cond_wait",
128 res
= pthread_mutex_unlock(&context
->mutex
);
130 T_ASSERT_POSIX_ZERO(res
, "[%ld] pthread_mutex_unlock",
134 if (context
->waiter
) {
135 res
= pthread_cond_broadcast(&context
->cond2
);
137 T_ASSERT_POSIX_ZERO(res
, "[%ld] pthread_cond_broadcast",
141 res
= pthread_mutex_unlock(&context
->mutex
);
143 T_ASSERT_POSIX_ZERO(res
, "[%ld] pthread_mutex_unlock",
147 } while (__sync_fetch_and_sub(&context
->count
, 1) > 0);
153 _test_condvar_prepost_race(void)
155 struct context context
= {
156 .mutex
= PTHREAD_MUTEX_INITIALIZER
,
157 .cond2
= PTHREAD_COND_INITIALIZER
,
165 pthread_t p
[threads
];
166 for (i
= 0; i
< threads
; ++i
) {
167 res
= pthread_create(&p
[i
], NULL
, test_thread
, &context
);
168 T_QUIET
; T_ASSERT_POSIX_ZERO(res
, "pthread_create()");
170 for (i
= 0; i
< threads
; ++i
) {
171 res
= pthread_join(p
[i
], NULL
);
172 T_QUIET
; T_ASSERT_POSIX_ZERO(res
, "pthread_join()");
175 T_PASS("initial pthread mutex storm completed");
177 pthread_mutex_destroy(&context
.mutex
);
178 pthread_cond_destroy(&context
.cond2
);
180 pthread_mutex_init(&context
.mutex2
, NULL
);
181 pthread_cond_init(&context
.cond
, NULL
);
182 res
= pthread_mutex_lock(&context
.mutex2
);
183 T_ASSERT_POSIX_ZERO(res
, "mutex lock for condition wait");
184 res
= pthread_create(&p
[0], NULL
, test_cond
, &context
);
185 T_QUIET
; T_ASSERT_POSIX_ZERO(res
, "pthread_create()");
186 res
= pthread_create(&p
[1], NULL
, test_cond_wake
, &context
);
187 T_QUIET
; T_ASSERT_POSIX_ZERO(res
, "pthread_create()");
189 res
= pthread_join(p
[0], NULL
);
190 T_QUIET
; T_ASSERT_POSIX_ZERO(res
, "pthread_join()");
191 res
= pthread_join(p
[1], NULL
);
192 T_QUIET
; T_ASSERT_POSIX_ZERO(res
, "pthread_join()");
194 pthread_cond_destroy(&context
.cond
);
197 T_DECL(cond_prepost_fairshare
, "cond_prepost_fairshare (fairshare)",
198 T_META_ALL_VALID_ARCHS(YES
),
199 T_META_ENVVAR("PTHREAD_MUTEX_DEFAULT_POLICY=1"))
203 for (i
=0; i
< count
; i
++) {
204 _test_condvar_prepost_race();
208 T_DECL(cond_prepost_firstfit
, "cond_prepost_firstfit (firstfit)",
209 T_META_ALL_VALID_ARCHS(YES
),
210 T_META_ENVVAR("PTHREAD_MUTEX_DEFAULT_POLICY=3"))
214 for (i
=0; i
< count
; i
++) {
215 _test_condvar_prepost_race();