1 //===-- Tests for standard condition variables ----------------------------===// 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/cnd_broadcast.h" 11 #include "src/threads/cnd_destroy.h" 12 #include "src/threads/cnd_init.h" 13 #include "src/threads/cnd_signal.h" 14 #include "src/threads/cnd_wait.h" 15 #include "src/threads/mtx_destroy.h" 16 #include "src/threads/mtx_init.h" 17 #include "src/threads/mtx_lock.h" 18 #include "src/threads/mtx_unlock.h" 19 #include "src/threads/thrd_create.h" 20 #include "src/threads/thrd_join.h" 21 22 #include "utils/IntegrationTest/test.h" 23 24 #include <threads.h> 25 26 namespace wait_notify_broadcast_test { 27 28 // The test in this namespace tests all condition variable operations. The 29 // main thread spawns THRD_COUNT threads, each of which wait on a condition 30 // variable |broadcast_cnd|. After spawing the threads, it waits on another 31 // condition variable |threads_ready_cnd| which will be signalled by the last 32 // thread before it starts waiting on |broadcast_cnd|. On signalled by the 33 // last thread, the main thread then wakes up to broadcast to all waiting 34 // threads to wake up. Each of the THRD_COUNT child threads increment 35 // |broadcast_count| by 1 before they start waiting on |broadcast_cnd|, and 36 // decrement it by 1 after getting signalled on |broadcast_cnd|. 37 38 constexpr unsigned int THRD_COUNT = 10000; 39 40 static __llvm_libc::cpp::Atomic<unsigned int> broadcast_count(0); 41 static cnd_t broadcast_cnd, threads_ready_cnd; 42 static mtx_t broadcast_mtx, threads_ready_mtx; 43 44 int broadcast_thread_func(void *) { 45 __llvm_libc::mtx_lock(&broadcast_mtx); 46 unsigned oldval = broadcast_count.fetch_add(1); 47 if (oldval == THRD_COUNT - 1) { 48 __llvm_libc::mtx_lock(&threads_ready_mtx); 49 __llvm_libc::cnd_signal(&threads_ready_cnd); 50 __llvm_libc::mtx_unlock(&threads_ready_mtx); 51 } 52 53 __llvm_libc::cnd_wait(&broadcast_cnd, &broadcast_mtx); 54 __llvm_libc::mtx_unlock(&broadcast_mtx); 55 broadcast_count.fetch_sub(1); 56 return 0; 57 } 58 59 void wait_notify_broadcast_test() { 60 __llvm_libc::cnd_init(&broadcast_cnd); 61 __llvm_libc::cnd_init(&threads_ready_cnd); 62 __llvm_libc::mtx_init(&broadcast_mtx, mtx_plain); 63 __llvm_libc::mtx_init(&threads_ready_mtx, mtx_plain); 64 65 __llvm_libc::mtx_lock(&threads_ready_mtx); 66 thrd_t threads[THRD_COUNT]; 67 for (unsigned int i = 0; i < THRD_COUNT; ++i) 68 __llvm_libc::thrd_create(&threads[i], broadcast_thread_func, nullptr); 69 70 __llvm_libc::cnd_wait(&threads_ready_cnd, &threads_ready_mtx); 71 __llvm_libc::mtx_unlock(&threads_ready_mtx); 72 73 __llvm_libc::mtx_lock(&broadcast_mtx); 74 ASSERT_EQ(broadcast_count.val, THRD_COUNT); 75 __llvm_libc::cnd_broadcast(&broadcast_cnd); 76 __llvm_libc::mtx_unlock(&broadcast_mtx); 77 78 for (unsigned int i = 0; i < THRD_COUNT; ++i) { 79 int retval = 0xBAD; 80 __llvm_libc::thrd_join(&threads[i], &retval); 81 ASSERT_EQ(retval, 0); 82 } 83 84 ASSERT_EQ(broadcast_count.val, 0U); 85 86 __llvm_libc::cnd_destroy(&broadcast_cnd); 87 __llvm_libc::cnd_destroy(&threads_ready_cnd); 88 __llvm_libc::mtx_destroy(&broadcast_mtx); 89 __llvm_libc::mtx_destroy(&threads_ready_mtx); 90 } 91 92 } // namespace wait_notify_broadcast_test 93 94 namespace single_waiter_test { 95 96 // In this namespace we set up test with two threads, one the main thread 97 // and the other a waiter thread. They wait on each other using condition 98 // variables and mutexes before proceeding to completion. 99 100 mtx_t waiter_mtx, main_thread_mtx; 101 cnd_t waiter_cnd, main_thread_cnd; 102 103 int waiter_thread_func(void *unused) { 104 __llvm_libc::mtx_lock(&waiter_mtx); 105 106 __llvm_libc::mtx_lock(&main_thread_mtx); 107 __llvm_libc::cnd_signal(&main_thread_cnd); 108 __llvm_libc::mtx_unlock(&main_thread_mtx); 109 110 __llvm_libc::cnd_wait(&waiter_cnd, &waiter_mtx); 111 __llvm_libc::mtx_unlock(&waiter_mtx); 112 113 return 0x600D; 114 } 115 116 void single_waiter_test() { 117 ASSERT_EQ(__llvm_libc::mtx_init(&waiter_mtx, mtx_plain), int(thrd_success)); 118 ASSERT_EQ(__llvm_libc::mtx_init(&main_thread_mtx, mtx_plain), 119 int(thrd_success)); 120 ASSERT_EQ(__llvm_libc::cnd_init(&waiter_cnd), int(thrd_success)); 121 ASSERT_EQ(__llvm_libc::cnd_init(&main_thread_cnd), int(thrd_success)); 122 123 ASSERT_EQ(__llvm_libc::mtx_lock(&main_thread_mtx), int(thrd_success)); 124 125 thrd_t waiter_thread; 126 __llvm_libc::thrd_create(&waiter_thread, waiter_thread_func, nullptr); 127 128 ASSERT_EQ(__llvm_libc::cnd_wait(&main_thread_cnd, &main_thread_mtx), 129 int(thrd_success)); 130 ASSERT_EQ(__llvm_libc::mtx_unlock(&main_thread_mtx), int(thrd_success)); 131 132 ASSERT_EQ(__llvm_libc::mtx_lock(&waiter_mtx), int(thrd_success)); 133 ASSERT_EQ(__llvm_libc::cnd_signal(&waiter_cnd), int(thrd_success)); 134 ASSERT_EQ(__llvm_libc::mtx_unlock(&waiter_mtx), int(thrd_success)); 135 136 int retval; 137 __llvm_libc::thrd_join(&waiter_thread, &retval); 138 ASSERT_EQ(retval, 0x600D); 139 140 __llvm_libc::mtx_destroy(&waiter_mtx); 141 __llvm_libc::mtx_destroy(&main_thread_mtx); 142 __llvm_libc::cnd_destroy(&waiter_cnd); 143 __llvm_libc::cnd_destroy(&main_thread_cnd); 144 } 145 146 } // namespace single_waiter_test 147 148 int main() { 149 wait_notify_broadcast_test::wait_notify_broadcast_test(); 150 single_waiter_test::single_waiter_test(); 151 return 0; 152 } 153