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