1 //===-- Tests 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 "src/__support/CPP/atomic.h" 10 #include "src/threads/call_once.h" 11 #include "src/threads/mtx_destroy.h" 12 #include "src/threads/mtx_init.h" 13 #include "src/threads/mtx_lock.h" 14 #include "src/threads/mtx_unlock.h" 15 #include "src/threads/thrd_create.h" 16 #include "src/threads/thrd_join.h" 17 18 #include "utils/IntegrationTest/test.h" 19 20 #include <threads.h> 21 22 static constexpr unsigned int NUM_THREADS = 5; 23 static __llvm_libc::cpp::Atomic<unsigned int> thread_count; 24 25 static unsigned int call_count; 26 static void call_once_func() { ++call_count; } 27 28 static int func(void *) { 29 static once_flag flag = ONCE_FLAG_INIT; 30 __llvm_libc::call_once(&flag, call_once_func); 31 32 thread_count.fetch_add(1); 33 34 return 0; 35 } 36 37 void call_from_5_threads() { 38 // Ensure the call count and thread count are 0 to begin with. 39 call_count = 0; 40 thread_count = 0; 41 42 thrd_t threads[NUM_THREADS]; 43 for (unsigned int i = 0; i < NUM_THREADS; ++i) { 44 ASSERT_EQ(__llvm_libc::thrd_create(threads + i, func, nullptr), 45 static_cast<int>(thrd_success)); 46 } 47 48 for (unsigned int i = 0; i < NUM_THREADS; ++i) { 49 int retval; 50 ASSERT_EQ(__llvm_libc::thrd_join(threads[i], &retval), 51 static_cast<int>(thrd_success)); 52 ASSERT_EQ(retval, 0); 53 } 54 55 EXPECT_EQ(thread_count.val, 5U); 56 EXPECT_EQ(call_count, 1U); 57 } 58 59 static mtx_t once_func_blocker; 60 static void blocking_once_func() { 61 __llvm_libc::mtx_lock(&once_func_blocker); 62 __llvm_libc::mtx_unlock(&once_func_blocker); 63 } 64 65 static __llvm_libc::cpp::Atomic<unsigned int> start_count; 66 static __llvm_libc::cpp::Atomic<unsigned int> done_count; 67 static int once_func_caller(void *) { 68 static once_flag flag; 69 start_count.fetch_add(1); 70 __llvm_libc::call_once(&flag, blocking_once_func); 71 done_count.fetch_add(1); 72 return 0; 73 } 74 75 // Test the synchronization aspect of the call_once function. 76 // This is not a fool proof test, but something which might be 77 // useful when we add a flakiness detection scheme to UnitTest. 78 void test_synchronization() { 79 start_count = 0; 80 done_count = 0; 81 82 ASSERT_EQ(__llvm_libc::mtx_init(&once_func_blocker, mtx_plain), 83 static_cast<int>(thrd_success)); 84 // Lock the blocking mutex so that the once func blocks. 85 ASSERT_EQ(__llvm_libc::mtx_lock(&once_func_blocker), 86 static_cast<int>(thrd_success)); 87 88 thrd_t t1, t2; 89 ASSERT_EQ(__llvm_libc::thrd_create(&t1, once_func_caller, nullptr), 90 static_cast<int>(thrd_success)); 91 ASSERT_EQ(__llvm_libc::thrd_create(&t2, once_func_caller, nullptr), 92 static_cast<int>(thrd_success)); 93 94 while (start_count.load() != 2) 95 ; // Spin until both threads start. 96 97 // Since the once func is blocked, the threads should not be done yet. 98 EXPECT_EQ(done_count.val, 0U); 99 100 // Unlock the blocking mutex so that the once func blocks. 101 ASSERT_EQ(__llvm_libc::mtx_unlock(&once_func_blocker), 102 static_cast<int>(thrd_success)); 103 104 int retval; 105 ASSERT_EQ(__llvm_libc::thrd_join(t1, &retval), 106 static_cast<int>(thrd_success)); 107 ASSERT_EQ(retval, 0); 108 ASSERT_EQ(__llvm_libc::thrd_join(t2, &retval), 109 static_cast<int>(thrd_success)); 110 ASSERT_EQ(retval, 0); 111 112 ASSERT_EQ(done_count.val, 2U); 113 114 __llvm_libc::mtx_destroy(&once_func_blocker); 115 } 116 117 TEST_MAIN() { 118 call_from_5_threads(); 119 test_synchronization(); 120 return 0; 121 } 122