10 #include <libkern/OSAtomic.h>
11 #include <dispatch/dispatch.h>
13 #include "darwintest_defaults.h"
19 pthread_mutex_t mutex
;
24 static void *wait_thread(void *ptr
) {
26 struct context
*context
= ptr
;
32 gettimeofday(&tv
, NULL
);
33 tv
.tv_sec
+= (tv
.tv_usec
+ context
->udelay
) / (__typeof(tv
.tv_sec
)) USEC_PER_SEC
;
34 tv
.tv_usec
= (tv
.tv_usec
+ context
->udelay
) % (__typeof(tv
.tv_usec
)) USEC_PER_SEC
;
35 TIMEVAL_TO_TIMESPEC(&tv
, &ts
);
37 res
= pthread_mutex_lock(&context
->mutex
);
39 fprintf(stderr
, "[%ld] pthread_mutex_lock: %s\n", context
->count
, strerror(res
));
43 if (context
->count
> 0) {
44 res
= pthread_cond_timedwait(&context
->cond
, &context
->mutex
, &ts
);
45 if (res
!= ETIMEDOUT
) {
46 fprintf(stderr
, "[%ld] pthread_cond_timedwait: %s\n", context
->count
, strerror(res
));
54 res
= pthread_mutex_unlock(&context
->mutex
);
56 fprintf(stderr
, "[%ld] pthread_mutex_unlock: %s\n", context
->count
, strerror(res
));
64 T_DECL(cond_timedwait_timeout
, "pthread_cond_timedwait() timeout")
66 // This testcase launches 8 threads that all perform timed wait on the same
67 // conditional variable that is not being signaled in a loop. Ater the total
68 // of 8000 timeouts all threads finish and the testcase prints out the
69 // expected time (5[ms]*8000[timeouts]/8[threads]=5s) vs elapsed time.
70 struct context context
= {
71 .cond
= PTHREAD_COND_INITIALIZER
,
72 .mutex
= PTHREAD_MUTEX_INITIALIZER
,
77 long uexpected
= (context
.udelay
* context
.count
) / NUM_THREADS
;
78 T_LOG("waittime expected: %ld us", uexpected
);
79 struct timeval start
, end
;
80 gettimeofday(&start
, NULL
);
82 pthread_t p
[NUM_THREADS
];
83 for (int i
= 0; i
< NUM_THREADS
; ++i
) {
84 T_ASSERT_POSIX_ZERO(pthread_create(&p
[i
], NULL
, wait_thread
, &context
),
88 usleep((useconds_t
) uexpected
);
91 T_QUIET
; T_ASSERT_POSIX_ZERO(pthread_mutex_lock(&context
.mutex
),
92 "pthread_mutex_lock");
93 if (context
.count
<= 0) {
96 T_QUIET
; T_ASSERT_POSIX_ZERO(pthread_mutex_unlock(&context
.mutex
),
97 "pthread_mutex_unlock");
100 for (int i
= 0; i
< NUM_THREADS
; ++i
) {
101 T_ASSERT_POSIX_ZERO(pthread_join(p
[i
], NULL
), "pthread_join");
104 gettimeofday(&end
, NULL
);
106 ((uint64_t) end
.tv_sec
* USEC_PER_SEC
+ (uint64_t) end
.tv_usec
) -
107 ((uint64_t) start
.tv_sec
* USEC_PER_SEC
+ (uint64_t) start
.tv_usec
);
108 T_LOG("waittime actual: %llu us", uelapsed
);
111 struct prodcons_context
{
113 pthread_mutex_t mutex
;
115 bool workitem_available
;
119 static void *consumer_thread(void *ptr
) {
120 struct prodcons_context
*context
= ptr
;
122 // tell producer thread that we are ready
123 T_ASSERT_POSIX_ZERO(pthread_mutex_lock(&context
->mutex
), "pthread_mutex_lock");
125 context
->consumer_ready
= true;
126 T_ASSERT_POSIX_ZERO(pthread_cond_signal(&context
->cond
), "pthread_cond_signal");
128 // wait for a work item to become available
130 // mutex will be dropped and allow producer thread to acquire
131 T_ASSERT_POSIX_ZERO(pthread_cond_wait(&context
->cond
, &context
->mutex
), "pthread_cond_wait");
133 // loop in case of spurious wakeups
134 } while (context
->workitem_available
== false);
136 // work item has been sent, so dequeue it and tell producer
137 context
->workitem_available
= false;
138 T_ASSERT_POSIX_ZERO(pthread_cond_signal(&context
->cond
), "pthread_cond_signal");
140 // unlock mutex, we are done here
141 T_ASSERT_POSIX_ZERO(pthread_mutex_unlock(&context
->mutex
), "pthread_mutex_unlock");
143 T_PASS("Consumer thread exiting");
148 #define TESTCASE_TIMEOUT (10) /* seconds */
156 static DT_TEST_RETURN
cond_timedwait_timeouts_internal(TimeOutType timeout
, bool relative
);
158 T_DECL(cond_timedwait_nulltimeout
, "pthread_cond_timedwait() with NULL timeout, ensure mutex is unlocked")
160 cond_timedwait_timeouts_internal(eNullTimeout
, false);
163 T_DECL(cond_timedwait_zerotimeout
, "pthread_cond_timedwait() with zero timeout, ensure mutex is unlocked")
165 cond_timedwait_timeouts_internal(eZeroTimeout
, false);
168 T_DECL(cond_timedwait_beforeepochtimeout
, "pthread_cond_timedwait() with timeout before the epoch, ensure mutex is unlocked")
170 cond_timedwait_timeouts_internal(eBeforeEpochTimeout
, false);
173 T_DECL(cond_timedwait_pasttimeout
, "pthread_cond_timedwait() with timeout in the past, ensure mutex is unlocked")
175 cond_timedwait_timeouts_internal(eRecentPastTimeout
, false);
178 T_DECL(cond_timedwait_relative_nulltimeout
, "pthread_cond_timedwait_relative_np() with relative NULL timeout, ensure mutex is unlocked")
180 cond_timedwait_timeouts_internal(eNullTimeout
, true);
183 T_DECL(cond_timedwait_relative_pasttimeout
, "pthread_cond_timedwait_relative_np() with relative timeout in the past, ensure mutex is unlocked")
185 cond_timedwait_timeouts_internal(eRecentPastTimeout
, true);
188 static DT_TEST_RETURN
cond_timedwait_timeouts_internal(TimeOutType timeout
, bool relative
)
190 // This testcase mimics a producer-consumer model where the consumer checks
191 // in and waits until work becomes available. The producer then waits until
192 // the work has been consumed and the consumer quiesces. Since condition
193 // variables may have spurious wakeups, the timeout should not matter,
194 // but there have been functional issues where the mutex would not be unlocked
195 // for a timeout in the past.
196 struct prodcons_context context
= {
197 .cond
= PTHREAD_COND_INITIALIZER
,
198 .mutex
= PTHREAD_MUTEX_INITIALIZER
,
199 .consumer_ready
= false,
200 .workitem_available
= false
203 struct timeval test_timeout
;
204 gettimeofday(&test_timeout
, NULL
);
205 test_timeout
.tv_sec
+= TESTCASE_TIMEOUT
;
207 T_ASSERT_POSIX_ZERO(pthread_mutex_lock(&context
.mutex
), "pthread_mutex_lock");
210 T_ASSERT_POSIX_ZERO(pthread_create(&p
, NULL
, consumer_thread
, &context
),
213 // Wait until consumer thread is able to acquire the mutex, check in, and block
214 // in its own condition variable. We do not want to start generating work before
215 // the consumer thread is available
217 // mutex will be dropped and allow consumer thread to acquire
218 T_ASSERT_POSIX_ZERO(pthread_cond_wait(&context
.cond
, &context
.mutex
), "pthread_cond_wait");
220 // loop in case of spurious wakeups
221 } while (context
.consumer_ready
== false);
223 // consumer is ready and blocked in its own condition variable, and
224 // producer has mutex acquired. Send a work item and wait for it
227 context
.workitem_available
= true;
228 T_ASSERT_POSIX_ZERO(pthread_cond_signal(&context
.cond
), "pthread_cond_signal");
233 gettimeofday(&now
, NULL
);
234 T_QUIET
; T_ASSERT_TRUE(timercmp(&now
, &test_timeout
, <), "timeout reached waiting for consumer thread to consume");
242 case eRecentPastTimeout
:
247 case eBeforeEpochTimeout
:
258 case eBeforeEpochTimeout
:
262 case eRecentPastTimeout
:
263 ts
.tv_sec
= now
.tv_sec
- 1;
264 ts
.tv_nsec
= now
.tv_usec
/ 1000;
271 ret
= pthread_cond_timedwait_relative_np(&context
.cond
, &context
.mutex
, timeout
== eNullTimeout
? NULL
: &ts
);
273 ret
= pthread_cond_timedwait(&context
.cond
, &context
.mutex
, timeout
== eNullTimeout
? NULL
: &ts
);
275 if (ret
!= 0 && ret
!= EINTR
&& ret
!= ETIMEDOUT
) T_ASSERT_POSIX_ZERO(ret
, "timedwait returned error");
277 usleep(10*1000); // avoid spinning in a CPU-bound loop
279 // loop in case of spurious wakeups
280 } while (context
.workitem_available
== true);
282 T_ASSERT_POSIX_ZERO(pthread_mutex_unlock(&context
.mutex
), "pthread_mutex_unlock");
284 T_ASSERT_POSIX_ZERO(pthread_join(p
, NULL
), "pthread_join");
286 T_PASS("Consumer completed work");