blob: 5fafff38d24872c71957540b8beaff8ae1399167 [file] [log] [blame]
Elliott Hughes42067112019-04-18 14:27:24 -07001/*
2 * Copyright (C) 2019 The Android Open Source Project
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in
12 * the documentation and/or other materials provided with the
13 * distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
18 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
19 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
22 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
25 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29#include <gtest/gtest.h>
30
31#if __has_include(<threads.h>)
32
33#define HAVE_THREADS_H
34#include <threads.h>
35
36static int g_call_once_call_count;
37
38static void increment_call_count() {
39 ++g_call_once_call_count;
40}
41
42static int g_dtor_call_count;
43
44static void tss_dtor(void* ptr) {
45 ++g_dtor_call_count;
46 free(ptr);
47}
48
49static int return_arg(void* arg) {
50 return static_cast<int>(reinterpret_cast<uintptr_t>(arg));
51}
52
53static int exit_arg(void* arg) {
54 thrd_exit(static_cast<int>(reinterpret_cast<uintptr_t>(arg)));
55}
56
57#endif
58
59#include <time.h>
60
61#include <thread>
62
63#include "BionicDeathTest.h"
64#include "SignalUtils.h"
65
66TEST(threads, call_once) {
67#if !defined(HAVE_THREADS_H)
68 GTEST_SKIP() << "<threads.h> unavailable";
69#else
70 once_flag flag = ONCE_FLAG_INIT;
71 call_once(&flag, increment_call_count);
72 call_once(&flag, increment_call_count);
73 std::thread([&flag] {
74 call_once(&flag, increment_call_count);
75 }).join();
76 ASSERT_EQ(1, g_call_once_call_count);
77#endif
78}
79
80TEST(threads, cnd_broadcast__cnd_wait) {
81#if !defined(HAVE_THREADS_H)
82 GTEST_SKIP() << "<threads.h> unavailable";
83#else
84 mtx_t m;
85 ASSERT_EQ(thrd_success, mtx_init(&m, mtx_plain));
86
87 cnd_t c;
88 ASSERT_EQ(thrd_success, cnd_init(&c));
89
90 std::atomic_int i = 0;
91
92 auto waiter = [&c, &i, &m] {
93 ASSERT_EQ(thrd_success, mtx_lock(&m));
94 while (i != 1) ASSERT_EQ(thrd_success, cnd_wait(&c, &m));
95 ASSERT_EQ(thrd_success, mtx_unlock(&m));
96 };
97 std::thread t1(waiter);
98 std::thread t2(waiter);
99 std::thread t3(waiter);
100
101 ASSERT_EQ(thrd_success, mtx_lock(&m));
102 i = 1;
103 ASSERT_EQ(thrd_success, mtx_unlock(&m));
104
105 ASSERT_EQ(thrd_success, cnd_broadcast(&c));
106
107 t1.join();
108 t2.join();
109 t3.join();
110
111 mtx_destroy(&m);
112 cnd_destroy(&c);
113#endif
114}
115
116TEST(threads, cnd_init__cnd_destroy) {
117#if !defined(HAVE_THREADS_H)
118 GTEST_SKIP() << "<threads.h> unavailable";
119#else
120 cnd_t c;
121 ASSERT_EQ(thrd_success, cnd_init(&c));
122 cnd_destroy(&c);
123#endif
124}
125
126TEST(threads, cnd_signal__cnd_wait) {
127#if !defined(HAVE_THREADS_H)
128 GTEST_SKIP() << "<threads.h> unavailable";
129#else
130 mtx_t m;
131 ASSERT_EQ(thrd_success, mtx_init(&m, mtx_plain));
132 cnd_t c;
133 ASSERT_EQ(thrd_success, cnd_init(&c));
134
135 std::atomic_int count = 0;
136 auto waiter = [&c, &m, &count] {
137 ASSERT_EQ(thrd_success, mtx_lock(&m));
138 ASSERT_EQ(thrd_success, cnd_wait(&c, &m));
139 ASSERT_EQ(thrd_success, mtx_unlock(&m));
140 ++count;
141 };
142 std::thread t1(waiter);
143 std::thread t2(waiter);
144 std::thread t3(waiter);
145
146 // This is inherently racy, but attempts to distinguish between cnd_signal and
147 // cnd_broadcast.
148 usleep(100000);
149 ASSERT_EQ(thrd_success, cnd_signal(&c));
150 while (count == 0) {
151 }
152 usleep(100000);
153 ASSERT_EQ(1, count);
154
155 ASSERT_EQ(thrd_success, cnd_signal(&c));
156 while (count == 1) {
157 }
158 usleep(100000);
159 ASSERT_EQ(2, count);
160
161 ASSERT_EQ(thrd_success, cnd_signal(&c));
162 while (count == 2) {
163 }
164 usleep(100000);
165 ASSERT_EQ(3, count);
166
167 t1.join();
168 t2.join();
169 t3.join();
170
171 mtx_destroy(&m);
172 cnd_destroy(&c);
173#endif
174}
175
176TEST(threads, cnd_timedwait_timedout) {
177#if !defined(HAVE_THREADS_H)
178 GTEST_SKIP() << "<threads.h> unavailable";
179#else
180 mtx_t m;
181 ASSERT_EQ(thrd_success, mtx_init(&m, mtx_timed));
182 ASSERT_EQ(thrd_success, mtx_lock(&m));
183
184 cnd_t c;
185 ASSERT_EQ(thrd_success, cnd_init(&c));
186
187 timespec ts = {};
188 ASSERT_EQ(thrd_timedout, cnd_timedwait(&c, &m, &ts));
189#endif
190}
191
192TEST(threads, cnd_timedwait) {
193#if !defined(HAVE_THREADS_H)
194 GTEST_SKIP() << "<threads.h> unavailable";
195#else
196 mtx_t m;
197 ASSERT_EQ(thrd_success, mtx_init(&m, mtx_timed));
198
199 cnd_t c;
200 ASSERT_EQ(thrd_success, cnd_init(&c));
201
202 std::atomic_bool done = false;
203 std::thread t([&c, &m, &done] {
204 ASSERT_EQ(thrd_success, mtx_lock(&m));
205
206 // cnd_timewait's time is *absolute*.
207 timespec ts;
208 ASSERT_EQ(TIME_UTC, timespec_get(&ts, TIME_UTC));
209 ts.tv_sec += 666;
210
211 ASSERT_EQ(thrd_success, cnd_timedwait(&c, &m, &ts));
212 done = true;
213 ASSERT_EQ(thrd_success, mtx_unlock(&m));
214 });
215
216 while (!done) ASSERT_EQ(thrd_success, cnd_signal(&c));
217
218 t.join();
219#endif
220}
221
222TEST(threads, mtx_init) {
223#if !defined(HAVE_THREADS_H)
224 GTEST_SKIP() << "<threads.h> unavailable";
225#else
226 mtx_t m;
227 ASSERT_EQ(thrd_success, mtx_init(&m, mtx_plain));
228 ASSERT_EQ(thrd_success, mtx_init(&m, mtx_timed));
229 ASSERT_EQ(thrd_success, mtx_init(&m, mtx_plain | mtx_recursive));
230 ASSERT_EQ(thrd_success, mtx_init(&m, mtx_timed | mtx_recursive));
231 ASSERT_EQ(thrd_error, mtx_init(&m, 123));
232 ASSERT_EQ(thrd_error, mtx_init(&m, mtx_recursive));
233#endif
234}
235
236TEST(threads, mtx_destroy) {
237#if !defined(HAVE_THREADS_H)
238 GTEST_SKIP() << "<threads.h> unavailable";
239#else
240 mtx_t m;
241 ASSERT_EQ(thrd_success, mtx_init(&m, mtx_plain));
242 mtx_destroy(&m);
243#endif
244}
245
246TEST(threads, mtx_lock_plain) {
247#if !defined(HAVE_THREADS_H)
248 GTEST_SKIP() << "<threads.h> unavailable";
249#else
250 mtx_t m;
251 ASSERT_EQ(thrd_success, mtx_init(&m, mtx_plain));
252
253 ASSERT_EQ(thrd_success, mtx_lock(&m));
254 ASSERT_EQ(thrd_busy, mtx_trylock(&m));
255 ASSERT_EQ(thrd_success, mtx_unlock(&m));
256
257 mtx_destroy(&m);
258#endif
259}
260
261TEST(threads, mtx_lock_recursive) {
262#if !defined(HAVE_THREADS_H)
263 GTEST_SKIP() << "<threads.h> unavailable";
264#else
265 mtx_t m;
266 ASSERT_EQ(thrd_success, mtx_init(&m, mtx_plain | mtx_recursive));
267
268 ASSERT_EQ(thrd_success, mtx_lock(&m));
269 ASSERT_EQ(thrd_success, mtx_trylock(&m));
270 ASSERT_EQ(thrd_success, mtx_unlock(&m));
271 ASSERT_EQ(thrd_success, mtx_unlock(&m));
272
273 mtx_destroy(&m);
274#endif
275}
276
277TEST(threads, mtx_timedlock) {
278#if !defined(HAVE_THREADS_H)
279 GTEST_SKIP() << "<threads.h> unavailable";
280#else
281 mtx_t m;
282 ASSERT_EQ(thrd_success, mtx_init(&m, mtx_timed));
283
284 timespec ts = {};
285 ASSERT_EQ(thrd_success, mtx_timedlock(&m, &ts));
286
287 std::thread([&m] {
288 timespec ts = { .tv_nsec = 500000 };
289 ASSERT_EQ(thrd_timedout, mtx_timedlock(&m, &ts));
290 }).join();
291
292 ASSERT_EQ(thrd_success, mtx_unlock(&m));
293 mtx_destroy(&m);
294#endif
295}
296
297
298TEST(threads, mtx_unlock) {
299#if !defined(HAVE_THREADS_H)
300 GTEST_SKIP() << "<threads.h> unavailable";
301#else
302 mtx_t m;
303 ASSERT_EQ(thrd_success, mtx_init(&m, mtx_plain));
304 ASSERT_EQ(thrd_success, mtx_lock(&m));
305 std::thread([&m] {
306 ASSERT_EQ(thrd_busy, mtx_trylock(&m));
307 }).join();
308 ASSERT_EQ(thrd_success, mtx_unlock(&m));
309 std::thread([&m] {
310 ASSERT_EQ(thrd_success, mtx_trylock(&m));
311 }).join();
312#endif
313}
314
315TEST(threads, thrd_current__thrd_equal) {
316#if !defined(HAVE_THREADS_H)
317 GTEST_SKIP() << "<threads.h> unavailable";
318#else
319 thrd_t t1 = thrd_current();
320 // (As a side-effect, this demonstrates interoperability with std::thread.)
321 std::thread([&t1] {
322 thrd_t t2 = thrd_current();
323 ASSERT_FALSE(thrd_equal(t1, t2));
324 thrd_t t2_2 = thrd_current();
325 ASSERT_TRUE(thrd_equal(t2, t2_2));
326 }).join();
327 thrd_t t1_2 = thrd_current();
328 ASSERT_TRUE(thrd_equal(t1, t1_2));
329#endif
330}
331
332TEST(threads, thrd_create__thrd_detach) {
333#if !defined(HAVE_THREADS_H)
334 GTEST_SKIP() << "<threads.h> unavailable";
335#else
336 thrd_t t;
337 ASSERT_EQ(thrd_success, thrd_create(&t, exit_arg, reinterpret_cast<void*>(1)));
338 ASSERT_EQ(thrd_success, thrd_detach(t));
339#endif
340}
341
342TEST(threads, thrd_create__thrd_exit) {
343#if !defined(HAVE_THREADS_H)
344 GTEST_SKIP() << "<threads.h> unavailable";
345#else
346 // Similar to the thrd_join test, but with a function that calls thrd_exit
347 // instead.
348 thrd_t t;
349 int result;
350 ASSERT_EQ(thrd_success, thrd_create(&t, exit_arg, reinterpret_cast<void*>(1)));
351 ASSERT_EQ(thrd_success, thrd_join(t, &result));
352 ASSERT_EQ(1, result);
353
354 ASSERT_EQ(thrd_success, thrd_create(&t, exit_arg, reinterpret_cast<void*>(2)));
355 ASSERT_EQ(thrd_success, thrd_join(t, &result));
356 ASSERT_EQ(2, result);
357
358 // The `result` argument can be null if you don't care...
359 ASSERT_EQ(thrd_success, thrd_create(&t, exit_arg, reinterpret_cast<void*>(3)));
360 ASSERT_EQ(thrd_success, thrd_join(t, nullptr));
361#endif
362}
363
364class threads_DeathTest : public BionicDeathTest {};
365
366TEST(threads_DeathTest, thrd_exit_main_thread) {
367#if !defined(HAVE_THREADS_H)
368 GTEST_SKIP() << "<threads.h> unavailable";
369#else
370 // "The program terminates normally after the last thread has been terminated.
371 // The behavior is as if the program called the exit function with the status
372 // EXIT_SUCCESS at thread termination time." (ISO/IEC 9899:2018)
373 ASSERT_EXIT(thrd_exit(12), ::testing::ExitedWithCode(EXIT_SUCCESS), "");
374#endif
375}
376
377TEST(threads, thrd_create__thrd_join) {
378#if !defined(HAVE_THREADS_H)
379 GTEST_SKIP() << "<threads.h> unavailable";
380#else
381 // Similar to the thrd_exit test, but with a function that calls return
382 // instead.
383 thrd_t t;
384 int result;
385 ASSERT_EQ(thrd_success, thrd_create(&t, return_arg, reinterpret_cast<void*>(1)));
386 ASSERT_EQ(thrd_success, thrd_join(t, &result));
387 ASSERT_EQ(1, result);
388
389 ASSERT_EQ(thrd_success, thrd_create(&t, return_arg, reinterpret_cast<void*>(2)));
390 ASSERT_EQ(thrd_success, thrd_join(t, &result));
391 ASSERT_EQ(2, result);
392
393 // The `result` argument can be null if you don't care...
394 ASSERT_EQ(thrd_success, thrd_create(&t, return_arg, reinterpret_cast<void*>(3)));
395 ASSERT_EQ(thrd_success, thrd_join(t, nullptr));
396#endif
397}
398
399TEST(threads, thrd_sleep_signal) {
400#if !defined(HAVE_THREADS_H)
401 GTEST_SKIP() << "<threads.h> unavailable";
402#else
403 ScopedSignalHandler ssh{SIGALRM, [](int) {}};
404 std::thread t([] {
405 timespec long_time = { .tv_sec = 666 };
406 timespec remaining = {};
407 ASSERT_EQ(-1, thrd_sleep(&long_time, &remaining));
408 uint64_t t = remaining.tv_sec * 1000000000 + remaining.tv_nsec;
409 ASSERT_LE(t, 666ULL * 1000000000);
410 });
411 usleep(100000); // 0.1s
412 pthread_kill(t.native_handle(), SIGALRM);
413 t.join();
414#endif
415}
416
417TEST(threads, thrd_sleep_signal_nullptr) {
418#if !defined(HAVE_THREADS_H)
419 GTEST_SKIP() << "<threads.h> unavailable";
420#else
421 ScopedSignalHandler ssh{SIGALRM, [](int) {}};
422 std::thread t([] {
423 timespec long_time = { .tv_sec = 666 };
424 ASSERT_EQ(-1, thrd_sleep(&long_time, nullptr));
425 });
426 usleep(100000); // 0.1s
427 pthread_kill(t.native_handle(), SIGALRM);
428 t.join();
429#endif
430}
431
432TEST(threads, thrd_sleep_error) {
433#if !defined(HAVE_THREADS_H)
434 GTEST_SKIP() << "<threads.h> unavailable";
435#else
436 timespec invalid = { .tv_sec = -1 };
437 ASSERT_EQ(-2, thrd_sleep(&invalid, nullptr));
438#endif
439}
440
441TEST(threads, thrd_yield) {
442#if !defined(HAVE_THREADS_H)
443 GTEST_SKIP() << "<threads.h> unavailable";
444#else
445 thrd_yield();
446#endif
447}
448
449TEST(threads, TSS_DTOR_ITERATIONS_macro) {
450#if !defined(HAVE_THREADS_H)
451 GTEST_SKIP() << "<threads.h> unavailable";
452#else
453 ASSERT_EQ(PTHREAD_DESTRUCTOR_ITERATIONS, TSS_DTOR_ITERATIONS);
454#endif
455}
456
457TEST(threads, tss_create) {
458#if !defined(HAVE_THREADS_H)
459 GTEST_SKIP() << "<threads.h> unavailable";
460#else
461 tss_t key;
462 ASSERT_EQ(thrd_success, tss_create(&key, nullptr));
463 tss_delete(key);
464#endif
465}
466
467TEST(threads, tss_create_dtor) {
468#if !defined(HAVE_THREADS_H)
469 GTEST_SKIP() << "<threads.h> unavailable";
470#else
471 tss_dtor_t dtor = tss_dtor;
472 tss_t key;
473 ASSERT_EQ(thrd_success, tss_create(&key, dtor));
474
475 ASSERT_EQ(thrd_success, tss_set(key, strdup("hello")));
476 std::thread([&key] {
477 ASSERT_EQ(thrd_success, tss_set(key, strdup("world")));
478 }).join();
479 // Thread exit calls the destructor...
480 ASSERT_EQ(1, g_dtor_call_count);
481
482 // "[A call to tss_set] will not invoke the destructor associated with the
483 // key on the value being replaced" (ISO/IEC 9899:2018).
484 g_dtor_call_count = 0;
485 ASSERT_EQ(thrd_success, tss_set(key, strdup("hello")));
486 ASSERT_EQ(0, g_dtor_call_count);
487
488 // "Calling tss_delete will not result in the invocation of any
489 // destructors" (ISO/IEC 9899:2018).
490 // The destructor for "hello" won't be called until *this* thread exits.
491 g_dtor_call_count = 0;
492 tss_delete(key);
493 ASSERT_EQ(0, g_dtor_call_count);
494#endif
495}
496
497TEST(threads, tss_get__tss_set) {
498#if !defined(HAVE_THREADS_H)
499 GTEST_SKIP() << "<threads.h> unavailable";
500#else
501 tss_t key;
502 ASSERT_EQ(thrd_success, tss_create(&key, nullptr));
503
504 ASSERT_EQ(thrd_success, tss_set(key, const_cast<char*>("hello")));
505 ASSERT_STREQ("hello", reinterpret_cast<char*>(tss_get(key)));
506 std::thread([&key] {
507 ASSERT_EQ(nullptr, tss_get(key));
508 ASSERT_EQ(thrd_success, tss_set(key, const_cast<char*>("world")));
509 ASSERT_STREQ("world", reinterpret_cast<char*>(tss_get(key)));
510 }).join();
511 ASSERT_STREQ("hello", reinterpret_cast<char*>(tss_get(key)));
512
513 tss_delete(key);
514#endif
515}