]> git.saurik.com Git - apple/libpthread.git/blob - tests/cond_prepost.c
libpthread-416.60.2.tar.gz
[apple/libpthread.git] / tests / cond_prepost.c
1 #include <pthread.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <unistd.h>
5 #include <stdbool.h>
6 #include <errno.h>
7 #include <TargetConditionals.h>
8
9 #include <pthread/pthread_spis.h>
10
11 #include <sys/sysctl.h>
12
13 #include "darwintest_defaults.h"
14 #include <darwintest_multiprocess.h>
15
16 // <rdar://problem/38810583> this test case is intended to test for the
17 // specific issue found in this radar. That is, if:
18 //
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
23 //
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.
29 //
30 // So this test is attempting to:
31 //
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.
37
38 struct context {
39 union {
40 pthread_mutex_t mutex;
41 pthread_cond_t cond;
42 };
43 union {
44 pthread_mutex_t mutex2;
45 pthread_cond_t cond2;
46 };
47 long value;
48 long count;
49 long waiter;
50 };
51
52 static void *test_cond(void *ptr) {
53 struct context *context = ptr;
54 int res;
55
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");
60 return NULL;
61 }
62
63 static void *test_cond_wake(void *ptr) {
64 struct context *context = ptr;
65 int res;
66
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");
73
74 return NULL;
75 }
76
77 static void *test_thread(void *ptr) {
78 int res;
79 long old;
80 struct context *context = ptr;
81
82 int i = 0;
83 char *str;
84
85 do {
86 bool try = i++ & 1;
87 bool cond = i & 16;
88
89 if (!try){
90 str = "pthread_mutex_lock";
91 res = pthread_mutex_lock(&context->mutex);
92 } else {
93 str = "pthread_mutex_trylock";
94 res = pthread_mutex_trylock(&context->mutex);
95 }
96 if (res != 0) {
97 if (try && res == EBUSY) {
98 continue;
99 }
100 T_ASSERT_POSIX_ZERO(res, "[%ld] %s", context->count, str);
101 }
102
103 old = __sync_fetch_and_or(&context->value, 1);
104 if ((old & 1) != 0) {
105 T_FAIL("[%ld] OR %lx\n", context->count, old);
106 }
107
108 old = __sync_fetch_and_and(&context->value, 0);
109 if ((old & 1) == 0) {
110 T_FAIL("[%ld] AND %lx\n", context->count, old);
111 }
112
113 if (cond && !context->waiter) {
114 context->waiter = 1;
115 struct timespec ts = {
116 .tv_sec = 0,
117 .tv_nsec = 10ull * NSEC_PER_MSEC,
118 };
119
120 res = pthread_cond_timedwait_relative_np(&context->cond2, &context->mutex, &ts);
121 if (res == ETIMEDOUT) {
122 // ignore, should be the last thread out
123 } else if (res) {
124 T_ASSERT_POSIX_ZERO(res, "[%ld] pthread_cond_wait",
125 context->count);
126 }
127 context->waiter = 0;
128 res = pthread_mutex_unlock(&context->mutex);
129 if (res) {
130 T_ASSERT_POSIX_ZERO(res, "[%ld] pthread_mutex_unlock",
131 context->count);
132 }
133 } else {
134 if (context->waiter) {
135 res = pthread_cond_broadcast(&context->cond2);
136 if (res) {
137 T_ASSERT_POSIX_ZERO(res, "[%ld] pthread_cond_broadcast",
138 context->count);
139 }
140 }
141 res = pthread_mutex_unlock(&context->mutex);
142 if (res) {
143 T_ASSERT_POSIX_ZERO(res, "[%ld] pthread_mutex_unlock",
144 context->count);
145 }
146 }
147 } while (__sync_fetch_and_sub(&context->count, 1) > 0);
148 return NULL;
149 }
150
151
152 static void
153 _test_condvar_prepost_race(void)
154 {
155 struct context context = {
156 .mutex = PTHREAD_MUTEX_INITIALIZER,
157 .cond2 = PTHREAD_COND_INITIALIZER,
158 .value = 0,
159 .count = 10000,
160 .waiter = false,
161 };
162 int i;
163 int res;
164 int threads = 8;
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()");
169 }
170 for (i = 0; i < threads; ++i) {
171 res = pthread_join(p[i], NULL);
172 T_QUIET; T_ASSERT_POSIX_ZERO(res, "pthread_join()");
173 }
174
175 T_PASS("initial pthread mutex storm completed");
176
177 pthread_mutex_destroy(&context.mutex);
178 pthread_cond_destroy(&context.cond2);
179
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()");
188
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()");
193
194 pthread_cond_destroy(&context.cond);
195 }
196
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"))
200 {
201 int i;
202 int count = 100;
203 for (i=0; i < count; i++) {
204 _test_condvar_prepost_race();
205 }
206 }
207
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"))
211 {
212 int i;
213 int count = 100;
214 for (i=0; i < count; i++) {
215 _test_condvar_prepost_race();
216 }
217 }