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