10 #include <libkern/OSAtomic.h>
11 #include <dispatch/dispatch.h>
13 #include "darwintest_defaults.h"
16 #define RDAR_38144536 1
20 pthread_mutex_t mutex
;
25 static void *wait_thread(void *ptr
) {
27 struct context
*context
= ptr
;
33 gettimeofday(&tv
, NULL
);
34 tv
.tv_sec
+= (tv
.tv_usec
+ context
->udelay
) / (__typeof(tv
.tv_sec
)) USEC_PER_SEC
;
35 tv
.tv_usec
= (tv
.tv_usec
+ context
->udelay
) % (__typeof(tv
.tv_usec
)) USEC_PER_SEC
;
36 TIMEVAL_TO_TIMESPEC(&tv
, &ts
);
38 res
= pthread_mutex_lock(&context
->mutex
);
40 fprintf(stderr
, "[%ld] pthread_mutex_lock: %s\n", context
->count
, strerror(res
));
44 if (context
->count
> 0) {
45 res
= pthread_cond_timedwait(&context
->cond
, &context
->mutex
, &ts
);
46 if (res
!= ETIMEDOUT
) {
47 fprintf(stderr
, "[%ld] pthread_cond_timedwait: %s\n", context
->count
, strerror(res
));
55 res
= pthread_mutex_unlock(&context
->mutex
);
57 fprintf(stderr
, "[%ld] pthread_mutex_unlock: %s\n", context
->count
, strerror(res
));
65 T_DECL(cond_timedwait_timeout
, "pthread_cond_timedwait() timeout")
67 // This testcase launches 8 threads that all perform timed wait on the same
68 // conditional variable that is not being signaled in a loop. Ater the total
69 // of 8000 timeouts all threads finish and the testcase prints out the
70 // expected time (5[ms]*8000[timeouts]/8[threads]=5s) vs elapsed time.
71 struct context context
= {
72 .cond
= PTHREAD_COND_INITIALIZER
,
73 .mutex
= PTHREAD_MUTEX_INITIALIZER
,
78 long uexpected
= (context
.udelay
* context
.count
) / NUM_THREADS
;
79 T_LOG("waittime expected: %ld us", uexpected
);
80 struct timeval start
, end
;
81 gettimeofday(&start
, NULL
);
83 pthread_t p
[NUM_THREADS
];
84 for (int i
= 0; i
< NUM_THREADS
; ++i
) {
85 T_ASSERT_POSIX_ZERO(pthread_create(&p
[i
], NULL
, wait_thread
, &context
),
89 usleep((useconds_t
) uexpected
);
92 T_QUIET
; T_ASSERT_POSIX_ZERO(pthread_mutex_lock(&context
.mutex
),
93 "pthread_mutex_lock");
94 if (context
.count
<= 0) {
97 T_QUIET
; T_ASSERT_POSIX_ZERO(pthread_mutex_unlock(&context
.mutex
),
98 "pthread_mutex_unlock");
101 for (int i
= 0; i
< NUM_THREADS
; ++i
) {
102 T_ASSERT_POSIX_ZERO(pthread_join(p
[i
], NULL
), "pthread_join");
105 gettimeofday(&end
, NULL
);
107 ((uint64_t) end
.tv_sec
* USEC_PER_SEC
+ (uint64_t) end
.tv_usec
) -
108 ((uint64_t) start
.tv_sec
* USEC_PER_SEC
+ (uint64_t) start
.tv_usec
);
109 T_LOG("waittime actual: %llu us", uelapsed
);
112 struct prodcons_context
{
114 pthread_mutex_t mutex
;
116 bool workitem_available
;
120 static void *consumer_thread(void *ptr
) {
121 struct prodcons_context
*context
= ptr
;
123 // tell producer thread that we are ready
124 T_ASSERT_POSIX_ZERO(pthread_mutex_lock(&context
->mutex
), "pthread_mutex_lock");
126 context
->consumer_ready
= true;
127 T_ASSERT_POSIX_ZERO(pthread_cond_signal(&context
->cond
), "pthread_cond_signal");
129 // wait for a work item to become available
131 // mutex will be dropped and allow producer thread to acquire
132 T_ASSERT_POSIX_ZERO(pthread_cond_wait(&context
->cond
, &context
->mutex
), "pthread_cond_wait");
134 // loop in case of spurious wakeups
135 } while (context
->workitem_available
== false);
137 // work item has been sent, so dequeue it and tell producer
138 context
->workitem_available
= false;
139 T_ASSERT_POSIX_ZERO(pthread_cond_signal(&context
->cond
), "pthread_cond_signal");
141 // unlock mutex, we are done here
142 T_ASSERT_POSIX_ZERO(pthread_mutex_unlock(&context
->mutex
), "pthread_mutex_unlock");
144 T_PASS("Consumer thread exiting");
149 #define TESTCASE_TIMEOUT (10) /* seconds */
157 static DT_TEST_RETURN
cond_timedwait_timeouts_internal(TimeOutType timeout
, bool relative
);
159 T_DECL(cond_timedwait_nulltimeout
, "pthread_cond_timedwait() with NULL timeout, ensure mutex is unlocked")
161 cond_timedwait_timeouts_internal(eNullTimeout
, false);
164 T_DECL(cond_timedwait_zerotimeout
, "pthread_cond_timedwait() with zero timeout, ensure mutex is unlocked")
167 T_SKIP("skipped <rdar://38144536>");
168 #else // RDAR_38144536
169 cond_timedwait_timeouts_internal(eZeroTimeout
, false);
170 #endif // RDAR_38144536
173 T_DECL(cond_timedwait_beforeepochtimeout
, "pthread_cond_timedwait() with timeout before the epoch, ensure mutex is unlocked")
176 T_SKIP("skipped <rdar://38144536>");
177 #else // RDAR_38144536
178 cond_timedwait_timeouts_internal(eBeforeEpochTimeout
, false);
179 #endif // RDAR_38144536
182 T_DECL(cond_timedwait_pasttimeout
, "pthread_cond_timedwait() with timeout in the past, ensure mutex is unlocked")
185 T_SKIP("skipped <rdar://38144536>");
186 #else // RDAR_38144536
187 cond_timedwait_timeouts_internal(eRecentPastTimeout
, false);
188 #endif // RDAR_38144536
191 T_DECL(cond_timedwait_relative_nulltimeout
, "pthread_cond_timedwait_relative_np() with relative NULL timeout, ensure mutex is unlocked")
193 cond_timedwait_timeouts_internal(eNullTimeout
, true);
196 T_DECL(cond_timedwait_relative_pasttimeout
, "pthread_cond_timedwait_relative_np() with relative timeout in the past, ensure mutex is unlocked")
198 cond_timedwait_timeouts_internal(eRecentPastTimeout
, true);
201 static DT_TEST_RETURN
cond_timedwait_timeouts_internal(TimeOutType timeout
, bool relative
)
203 // This testcase mimics a producer-consumer model where the consumer checks
204 // in and waits until work becomes available. The producer then waits until
205 // the work has been consumed and the consumer quiesces. Since condition
206 // variables may have spurious wakeups, the timeout should not matter,
207 // but there have been functional issues where the mutex would not be unlocked
208 // for a timeout in the past.
209 struct prodcons_context context
= {
210 .cond
= PTHREAD_COND_INITIALIZER
,
211 .mutex
= PTHREAD_MUTEX_INITIALIZER
,
212 .consumer_ready
= false,
213 .workitem_available
= false
216 struct timeval test_timeout
;
217 gettimeofday(&test_timeout
, NULL
);
218 test_timeout
.tv_sec
+= TESTCASE_TIMEOUT
;
220 T_ASSERT_POSIX_ZERO(pthread_mutex_lock(&context
.mutex
), "pthread_mutex_lock");
223 T_ASSERT_POSIX_ZERO(pthread_create(&p
, NULL
, consumer_thread
, &context
),
226 // Wait until consumer thread is able to acquire the mutex, check in, and block
227 // in its own condition variable. We do not want to start generating work before
228 // the consumer thread is available
230 // mutex will be dropped and allow consumer thread to acquire
231 T_ASSERT_POSIX_ZERO(pthread_cond_wait(&context
.cond
, &context
.mutex
), "pthread_cond_wait");
233 // loop in case of spurious wakeups
234 } while (context
.consumer_ready
== false);
236 // consumer is ready and blocked in its own condition variable, and
237 // producer has mutex acquired. Send a work item and wait for it
240 context
.workitem_available
= true;
241 T_ASSERT_POSIX_ZERO(pthread_cond_signal(&context
.cond
), "pthread_cond_signal");
246 gettimeofday(&now
, NULL
);
247 T_QUIET
; T_ASSERT_TRUE(timercmp(&now
, &test_timeout
, <), "timeout reached waiting for consumer thread to consume");
255 case eRecentPastTimeout
:
260 case eBeforeEpochTimeout
:
271 case eBeforeEpochTimeout
:
275 case eRecentPastTimeout
:
276 ts
.tv_sec
= now
.tv_sec
- 1;
277 ts
.tv_nsec
= now
.tv_usec
/ 1000;
284 ret
= pthread_cond_timedwait_relative_np(&context
.cond
, &context
.mutex
, timeout
== eNullTimeout
? NULL
: &ts
);
286 ret
= pthread_cond_timedwait(&context
.cond
, &context
.mutex
, timeout
== eNullTimeout
? NULL
: &ts
);
288 if (ret
!= 0 && ret
!= EINTR
&& ret
!= ETIMEDOUT
) T_ASSERT_POSIX_ZERO(ret
, "timedwait returned error");
290 usleep(10*1000); // avoid spinning in a CPU-bound loop
292 // loop in case of spurious wakeups
293 } while (context
.workitem_available
== true);
295 T_ASSERT_POSIX_ZERO(pthread_mutex_unlock(&context
.mutex
), "pthread_mutex_unlock");
297 T_ASSERT_POSIX_ZERO(pthread_join(p
, NULL
), "pthread_join");
299 T_PASS("Consumer completed work");