1 //===-- Tests for standard condition variables ----------------------------===//
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
7 //===----------------------------------------------------------------------===//
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"
22 #include "test/IntegrationTest/test.h"
26 namespace wait_notify_broadcast_test
{
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|.
38 constexpr unsigned int THRD_COUNT
= 1000;
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
;
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
);
53 LIBC_NAMESPACE::cnd_wait(&broadcast_cnd
, &broadcast_mtx
);
54 LIBC_NAMESPACE::mtx_unlock(&broadcast_mtx
);
55 broadcast_count
.fetch_sub(1);
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
);
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);
70 LIBC_NAMESPACE::cnd_wait(&threads_ready_cnd
, &threads_ready_mtx
);
71 LIBC_NAMESPACE::mtx_unlock(&threads_ready_mtx
);
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
);
78 for (unsigned int i
= 0; i
< THRD_COUNT
; ++i
) {
80 LIBC_NAMESPACE::thrd_join(threads
[i
], &retval
);
84 ASSERT_EQ(broadcast_count
.val
, 0U);
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
);
92 } // namespace wait_notify_broadcast_test
94 namespace single_waiter_test
{
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.
100 mtx_t waiter_mtx
, main_thread_mtx
;
101 cnd_t waiter_cnd
, main_thread_cnd
;
103 int waiter_thread_func(void *unused
) {
104 LIBC_NAMESPACE::mtx_lock(&waiter_mtx
);
106 LIBC_NAMESPACE::mtx_lock(&main_thread_mtx
);
107 LIBC_NAMESPACE::cnd_signal(&main_thread_cnd
);
108 LIBC_NAMESPACE::mtx_unlock(&main_thread_mtx
);
110 LIBC_NAMESPACE::cnd_wait(&waiter_cnd
, &waiter_mtx
);
111 LIBC_NAMESPACE::mtx_unlock(&waiter_mtx
);
116 void single_waiter_test() {
117 ASSERT_EQ(LIBC_NAMESPACE::mtx_init(&waiter_mtx
, mtx_plain
),
119 ASSERT_EQ(LIBC_NAMESPACE::mtx_init(&main_thread_mtx
, mtx_plain
),
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
));
124 ASSERT_EQ(LIBC_NAMESPACE::mtx_lock(&main_thread_mtx
), int(thrd_success
));
126 thrd_t waiter_thread
;
127 LIBC_NAMESPACE::thrd_create(&waiter_thread
, waiter_thread_func
, nullptr);
129 ASSERT_EQ(LIBC_NAMESPACE::cnd_wait(&main_thread_cnd
, &main_thread_mtx
),
131 ASSERT_EQ(LIBC_NAMESPACE::mtx_unlock(&main_thread_mtx
), int(thrd_success
));
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
));
138 LIBC_NAMESPACE::thrd_join(waiter_thread
, &retval
);
139 ASSERT_EQ(retval
, 0x600D);
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
);
147 } // namespace single_waiter_test
150 wait_notify_broadcast_test::wait_notify_broadcast_test();
151 single_waiter_test::single_waiter_test();