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