| 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 "test/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 = 1000; |
| 39 | |
| 40 | static LIBC_NAMESPACE::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 | LIBC_NAMESPACE::mtx_lock(&broadcast_mtx); |
| 46 | unsigned oldval = broadcast_count.fetch_add(1); |
| 47 | if (oldval == THRD_COUNT - 1) { |
| 48 | LIBC_NAMESPACE::mtx_lock(&threads_ready_mtx); |
| 49 | LIBC_NAMESPACE::cnd_signal(&threads_ready_cnd); |
| 50 | LIBC_NAMESPACE::mtx_unlock(&threads_ready_mtx); |
| 51 | } |
| 52 | |
| 53 | LIBC_NAMESPACE::cnd_wait(&broadcast_cnd, &broadcast_mtx); |
| 54 | LIBC_NAMESPACE::mtx_unlock(&broadcast_mtx); |
| 55 | broadcast_count.fetch_sub(1); |
| 56 | return 0; |
| 57 | } |
| 58 | |
| 59 | void wait_notify_broadcast_test() { |
| 60 | LIBC_NAMESPACE::cnd_init(&broadcast_cnd); |
| 61 | LIBC_NAMESPACE::cnd_init(&threads_ready_cnd); |
| 62 | LIBC_NAMESPACE::mtx_init(&broadcast_mtx, mtx_plain); |
| 63 | LIBC_NAMESPACE::mtx_init(&threads_ready_mtx, mtx_plain); |
| 64 | |
| 65 | LIBC_NAMESPACE::mtx_lock(&threads_ready_mtx); |
| 66 | thrd_t threads[THRD_COUNT]; |
| 67 | for (unsigned int i = 0; i < THRD_COUNT; ++i) |
| 68 | LIBC_NAMESPACE::thrd_create(&threads[i], broadcast_thread_func, nullptr); |
| 69 | |
| 70 | LIBC_NAMESPACE::cnd_wait(&threads_ready_cnd, &threads_ready_mtx); |
| 71 | LIBC_NAMESPACE::mtx_unlock(&threads_ready_mtx); |
| 72 | |
| 73 | LIBC_NAMESPACE::mtx_lock(&broadcast_mtx); |
| 74 | ASSERT_EQ(broadcast_count.val, THRD_COUNT); |
| 75 | LIBC_NAMESPACE::cnd_broadcast(&broadcast_cnd); |
| 76 | LIBC_NAMESPACE::mtx_unlock(&broadcast_mtx); |
| 77 | |
| 78 | for (unsigned int i = 0; i < THRD_COUNT; ++i) { |
| 79 | int retval = 0xBAD; |
| 80 | LIBC_NAMESPACE::thrd_join(threads[i], &retval); |
| 81 | ASSERT_EQ(retval, 0); |
| 82 | } |
| 83 | |
| 84 | ASSERT_EQ(broadcast_count.val, 0U); |
| 85 | |
| 86 | LIBC_NAMESPACE::cnd_destroy(&broadcast_cnd); |
| 87 | LIBC_NAMESPACE::cnd_destroy(&threads_ready_cnd); |
| 88 | LIBC_NAMESPACE::mtx_destroy(&broadcast_mtx); |
| 89 | LIBC_NAMESPACE::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 | LIBC_NAMESPACE::mtx_lock(&waiter_mtx); |
| 105 | |
| 106 | LIBC_NAMESPACE::mtx_lock(&main_thread_mtx); |
| 107 | LIBC_NAMESPACE::cnd_signal(&main_thread_cnd); |
| 108 | LIBC_NAMESPACE::mtx_unlock(&main_thread_mtx); |
| 109 | |
| 110 | LIBC_NAMESPACE::cnd_wait(&waiter_cnd, &waiter_mtx); |
| 111 | LIBC_NAMESPACE::mtx_unlock(&waiter_mtx); |
| 112 | |
| 113 | return 0x600D; |
| 114 | } |
| 115 | |
| 116 | void single_waiter_test() { |
| 117 | ASSERT_EQ(LIBC_NAMESPACE::mtx_init(&waiter_mtx, mtx_plain), |
| 118 | int(thrd_success)); |
| 119 | ASSERT_EQ(LIBC_NAMESPACE::mtx_init(&main_thread_mtx, mtx_plain), |
| 120 | int(thrd_success)); |
| 121 | ASSERT_EQ(LIBC_NAMESPACE::cnd_init(&waiter_cnd), int(thrd_success)); |
| 122 | ASSERT_EQ(LIBC_NAMESPACE::cnd_init(&main_thread_cnd), int(thrd_success)); |
| 123 | |
| 124 | ASSERT_EQ(LIBC_NAMESPACE::mtx_lock(&main_thread_mtx), int(thrd_success)); |
| 125 | |
| 126 | thrd_t waiter_thread; |
| 127 | LIBC_NAMESPACE::thrd_create(&waiter_thread, waiter_thread_func, nullptr); |
| 128 | |
| 129 | ASSERT_EQ(LIBC_NAMESPACE::cnd_wait(&main_thread_cnd, &main_thread_mtx), |
| 130 | int(thrd_success)); |
| 131 | ASSERT_EQ(LIBC_NAMESPACE::mtx_unlock(&main_thread_mtx), int(thrd_success)); |
| 132 | |
| 133 | ASSERT_EQ(LIBC_NAMESPACE::mtx_lock(&waiter_mtx), int(thrd_success)); |
| 134 | ASSERT_EQ(LIBC_NAMESPACE::cnd_signal(&waiter_cnd), int(thrd_success)); |
| 135 | ASSERT_EQ(LIBC_NAMESPACE::mtx_unlock(&waiter_mtx), int(thrd_success)); |
| 136 | |
| 137 | int retval; |
| 138 | LIBC_NAMESPACE::thrd_join(waiter_thread, &retval); |
| 139 | ASSERT_EQ(retval, 0x600D); |
| 140 | |
| 141 | LIBC_NAMESPACE::mtx_destroy(&waiter_mtx); |
| 142 | LIBC_NAMESPACE::mtx_destroy(&main_thread_mtx); |
| 143 | LIBC_NAMESPACE::cnd_destroy(&waiter_cnd); |
| 144 | LIBC_NAMESPACE::cnd_destroy(&main_thread_cnd); |
| 145 | } |
| 146 | |
| 147 | } // namespace single_waiter_test |
| 148 | |
| 149 | TEST_MAIN() { |
| 150 | wait_notify_broadcast_test::wait_notify_broadcast_test(); |
| 151 | single_waiter_test::single_waiter_test(); |
| 152 | return 0; |
| 153 | } |
| 154 | |