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(mutex: &broadcast_mtx); |
46 | unsigned oldval = broadcast_count.fetch_add(increment: 1); |
47 | if (oldval == THRD_COUNT - 1) { |
48 | LIBC_NAMESPACE::mtx_lock(mutex: &threads_ready_mtx); |
49 | LIBC_NAMESPACE::cnd_signal(cond: &threads_ready_cnd); |
50 | LIBC_NAMESPACE::mtx_unlock(mutex: &threads_ready_mtx); |
51 | } |
52 | |
53 | LIBC_NAMESPACE::cnd_wait(cond: &broadcast_cnd, mutex: &broadcast_mtx); |
54 | LIBC_NAMESPACE::mtx_unlock(mutex: &broadcast_mtx); |
55 | broadcast_count.fetch_sub(decrement: 1); |
56 | return 0; |
57 | } |
58 | |
59 | void wait_notify_broadcast_test() { |
60 | LIBC_NAMESPACE::cnd_init(cond: &broadcast_cnd); |
61 | LIBC_NAMESPACE::cnd_init(cond: &threads_ready_cnd); |
62 | LIBC_NAMESPACE::mtx_init(mutex: &broadcast_mtx, type: mtx_plain); |
63 | LIBC_NAMESPACE::mtx_init(mutex: &threads_ready_mtx, type: mtx_plain); |
64 | |
65 | LIBC_NAMESPACE::mtx_lock(mutex: &threads_ready_mtx); |
66 | thrd_t threads[THRD_COUNT]; |
67 | for (unsigned int i = 0; i < THRD_COUNT; ++i) |
68 | LIBC_NAMESPACE::thrd_create(thread: &threads[i], func: broadcast_thread_func, arg: nullptr); |
69 | |
70 | LIBC_NAMESPACE::cnd_wait(cond: &threads_ready_cnd, mutex: &threads_ready_mtx); |
71 | LIBC_NAMESPACE::mtx_unlock(mutex: &threads_ready_mtx); |
72 | |
73 | LIBC_NAMESPACE::mtx_lock(mutex: &broadcast_mtx); |
74 | ASSERT_EQ(broadcast_count.val, THRD_COUNT); |
75 | LIBC_NAMESPACE::cnd_broadcast(cond: &broadcast_cnd); |
76 | LIBC_NAMESPACE::mtx_unlock(mutex: &broadcast_mtx); |
77 | |
78 | for (unsigned int i = 0; i < THRD_COUNT; ++i) { |
79 | int retval = 0xBAD; |
80 | LIBC_NAMESPACE::thrd_join(thread: threads[i], retval: &retval); |
81 | ASSERT_EQ(retval, 0); |
82 | } |
83 | |
84 | ASSERT_EQ(broadcast_count.val, 0U); |
85 | |
86 | LIBC_NAMESPACE::cnd_destroy(cond: &broadcast_cnd); |
87 | LIBC_NAMESPACE::cnd_destroy(cond: &threads_ready_cnd); |
88 | LIBC_NAMESPACE::mtx_destroy(mutex: &broadcast_mtx); |
89 | LIBC_NAMESPACE::mtx_destroy(mutex: &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(mutex: &waiter_mtx); |
105 | |
106 | LIBC_NAMESPACE::mtx_lock(mutex: &main_thread_mtx); |
107 | LIBC_NAMESPACE::cnd_signal(cond: &main_thread_cnd); |
108 | LIBC_NAMESPACE::mtx_unlock(mutex: &main_thread_mtx); |
109 | |
110 | LIBC_NAMESPACE::cnd_wait(cond: &waiter_cnd, mutex: &waiter_mtx); |
111 | LIBC_NAMESPACE::mtx_unlock(mutex: &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(thread: &waiter_thread, func: waiter_thread_func, arg: 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(thread: waiter_thread, retval: &retval); |
139 | ASSERT_EQ(retval, 0x600D); |
140 | |
141 | LIBC_NAMESPACE::mtx_destroy(mutex: &waiter_mtx); |
142 | LIBC_NAMESPACE::mtx_destroy(mutex: &main_thread_mtx); |
143 | LIBC_NAMESPACE::cnd_destroy(cond: &waiter_cnd); |
144 | LIBC_NAMESPACE::cnd_destroy(cond: &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 | |