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
26namespace 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
38constexpr unsigned int THRD_COUNT = 1000;
39
40static LIBC_NAMESPACE::cpp::Atomic<unsigned int> broadcast_count(0);
41static cnd_t broadcast_cnd, threads_ready_cnd;
42static mtx_t broadcast_mtx, threads_ready_mtx;
43
44int 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
59void 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
94namespace 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
100mtx_t waiter_mtx, main_thread_mtx;
101cnd_t waiter_cnd, main_thread_cnd;
102
103int 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
116void 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
149TEST_MAIN() {
150 wait_notify_broadcast_test::wait_notify_broadcast_test();
151 single_waiter_test::single_waiter_test();
152 return 0;
153}
154

source code of libc/test/integration/src/threads/cnd_test.cpp