blob: c1e9b123ace69b74738f08cfdff952005ecfcc74 [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
Elliott Hughes141b9172021-04-09 17:13:09 -070063#include <android-base/silent_death_test.h>
64
Elliott Hughes42067112019-04-18 14:27:24 -070065#include "SignalUtils.h"
66
67TEST(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
81TEST(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
117TEST(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
127TEST(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
177TEST(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
193TEST(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
223TEST(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
237TEST(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
247TEST(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
262TEST(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
278TEST(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
299TEST(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
316TEST(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
333TEST(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
343TEST(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
Elliott Hughes141b9172021-04-09 17:13:09 -0700365using threads_DeathTest = SilentDeathTest;
Elliott Hughes42067112019-04-18 14:27:24 -0700366
367TEST(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
378TEST(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
400TEST(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
418TEST(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
433TEST(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
442TEST(threads, thrd_yield) {
443#if !defined(HAVE_THREADS_H)
444 GTEST_SKIP() << "<threads.h> unavailable";
445#else
446 thrd_yield();
447#endif
448}
449
450TEST(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
458TEST(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
468TEST(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
498TEST(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}