154 lines · cpp
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-exception6//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. The29// main thread spawns THRD_COUNT threads, each of which wait on a condition30// variable |broadcast_cnd|. After spawing the threads, it waits on another31// condition variable |threads_ready_cnd| which will be signalled by the last32// thread before it starts waiting on |broadcast_cnd|. On signalled by the33// last thread, the main thread then wakes up to broadcast to all waiting34// threads to wake up. Each of the THRD_COUNT child threads increment35// |broadcast_count| by 1 before they start waiting on |broadcast_cnd|, and36// 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(&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 59void 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_test93 94namespace single_waiter_test {95 96// In this namespace we set up test with two threads, one the main thread97// and the other a waiter thread. They wait on each other using condition98// 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([[maybe_unused]] 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 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(&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_test148 149TEST_MAIN() {150 wait_notify_broadcast_test::wait_notify_broadcast_test();151 single_waiter_test::single_waiter_test();152 return 0;153}154