1 //===-- Unittests for call_once -------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8
9 #include "include/threads.h"
10 #include "src/threads/call_once.h"
11 #include "src/threads/mtx_init.h"
12 #include "src/threads/mtx_lock.h"
13 #include "src/threads/mtx_unlock.h"
14 #include "src/threads/thrd_create.h"
15 #include "src/threads/thrd_join.h"
16 #include "utils/UnitTest/Test.h"
17
18 #include <stdatomic.h>
19
20 static constexpr unsigned int num_threads = 5;
21 static atomic_uint thread_count;
22
23 static unsigned int call_count;
call_once_func()24 static void call_once_func() { ++call_count; }
25
func(void *)26 static int func(void *) {
27 static once_flag flag = ONCE_FLAG_INIT;
28 __llvm_libc::call_once(&flag, call_once_func);
29
30 ++thread_count; // This is a an atomic update.
31
32 return 0;
33 }
34
TEST(CallOnceTest,CallFrom5Threads)35 TEST(CallOnceTest, CallFrom5Threads) {
36 // Ensure the call count and thread count are 0 to begin with.
37 call_count = 0;
38 thread_count = 0;
39
40 thrd_t threads[num_threads];
41 for (unsigned int i = 0; i < num_threads; ++i) {
42 ASSERT_EQ(__llvm_libc::thrd_create(threads + i, func, nullptr),
43 static_cast<int>(thrd_success));
44 }
45
46 for (unsigned int i = 0; i < num_threads; ++i) {
47 int retval;
48 ASSERT_EQ(__llvm_libc::thrd_join(threads + i, &retval),
49 static_cast<int>(thrd_success));
50 ASSERT_EQ(retval, 0);
51 }
52
53 EXPECT_EQ(static_cast<unsigned int>(thread_count), 5U);
54 EXPECT_EQ(call_count, 1U);
55 }
56
57 static mtx_t once_func_blocker;
blocking_once_func()58 static void blocking_once_func() {
59 __llvm_libc::mtx_lock(&once_func_blocker);
60 __llvm_libc::mtx_unlock(&once_func_blocker);
61 }
62
63 static atomic_uint start_count;
64 static atomic_uint done_count;
once_func_caller(void *)65 static int once_func_caller(void *) {
66 static once_flag flag;
67 ++start_count;
68 __llvm_libc::call_once(&flag, blocking_once_func);
69 ++done_count;
70 return 0;
71 }
72
73 // Test the synchronization aspect of the call_once function.
74 // This is not a fool proof test, but something which might be
75 // useful when we add a flakiness detection scheme to UnitTest.
TEST(CallOnceTest,TestSynchronization)76 TEST(CallOnceTest, TestSynchronization) {
77 start_count = 0;
78 done_count = 0;
79
80 ASSERT_EQ(__llvm_libc::mtx_init(&once_func_blocker, mtx_plain),
81 static_cast<int>(thrd_success));
82 // Lock the blocking mutex so that the once func blocks.
83 ASSERT_EQ(__llvm_libc::mtx_lock(&once_func_blocker),
84 static_cast<int>(thrd_success));
85
86 thrd_t t1, t2;
87 ASSERT_EQ(__llvm_libc::thrd_create(&t1, once_func_caller, nullptr),
88 static_cast<int>(thrd_success));
89 ASSERT_EQ(__llvm_libc::thrd_create(&t2, once_func_caller, nullptr),
90 static_cast<int>(thrd_success));
91
92 while (start_count != 2)
93 ; // Spin until both threads start.
94
95 // Since the once func is blocked, the threads should not be done yet.
96 EXPECT_EQ(static_cast<unsigned int>(done_count), 0U);
97
98 // Unlock the blocking mutex so that the once func blocks.
99 ASSERT_EQ(__llvm_libc::mtx_unlock(&once_func_blocker),
100 static_cast<int>(thrd_success));
101
102 int retval;
103 ASSERT_EQ(__llvm_libc::thrd_join(&t1, &retval),
104 static_cast<int>(thrd_success));
105 ASSERT_EQ(retval, 0);
106 ASSERT_EQ(__llvm_libc::thrd_join(&t2, &retval),
107 static_cast<int>(thrd_success));
108 ASSERT_EQ(retval, 0);
109
110 ASSERT_EQ(static_cast<unsigned int>(done_count), 2U);
111 }
112