brintos

brintos / llvm-project-archived public Read only

0
0
Text · 5.5 KiB · 0b0a9fa Raw
203 lines · cpp
1//===-- Tests for mtx_t operations ----------------------------------------===//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/threads/mtx_destroy.h"10#include "src/threads/mtx_init.h"11#include "src/threads/mtx_lock.h"12#include "src/threads/mtx_unlock.h"13#include "src/threads/thrd_create.h"14#include "src/threads/thrd_join.h"15 16#include "test/IntegrationTest/test.h"17 18#include <threads.h>19 20constexpr int START = 0;21constexpr int MAX = 10000;22 23mtx_t mutex;24static int shared_int = START;25 26int counter([[maybe_unused]] void *arg) {27  int last_count = START;28  while (true) {29    LIBC_NAMESPACE::mtx_lock(&mutex);30    if (shared_int == last_count + 1) {31      shared_int++;32      last_count = shared_int;33    }34    LIBC_NAMESPACE::mtx_unlock(&mutex);35    if (last_count >= MAX)36      break;37  }38  return 0;39}40 41void relay_counter() {42  ASSERT_EQ(LIBC_NAMESPACE::mtx_init(&mutex, mtx_plain),43            static_cast<int>(thrd_success));44 45  // The idea of this test is that two competing threads will update46  // a counter only if the other thread has updated it.47  thrd_t thread;48  LIBC_NAMESPACE::thrd_create(&thread, counter, nullptr);49 50  int last_count = START;51  while (true) {52    ASSERT_EQ(LIBC_NAMESPACE::mtx_lock(&mutex), static_cast<int>(thrd_success));53    if (shared_int == START) {54      ++shared_int;55      last_count = shared_int;56    } else if (shared_int != last_count) {57      ASSERT_EQ(shared_int, last_count + 1);58      ++shared_int;59      last_count = shared_int;60    }61    ASSERT_EQ(LIBC_NAMESPACE::mtx_unlock(&mutex),62              static_cast<int>(thrd_success));63    if (last_count > MAX)64      break;65  }66 67  int retval = 123;68  LIBC_NAMESPACE::thrd_join(thread, &retval);69  ASSERT_EQ(retval, 0);70 71  LIBC_NAMESPACE::mtx_destroy(&mutex);72}73 74mtx_t start_lock, step_lock;75bool started, step;76 77int stepper([[maybe_unused]] void *arg) {78  LIBC_NAMESPACE::mtx_lock(&start_lock);79  started = true;80  LIBC_NAMESPACE::mtx_unlock(&start_lock);81 82  LIBC_NAMESPACE::mtx_lock(&step_lock);83  step = true;84  LIBC_NAMESPACE::mtx_unlock(&step_lock);85  return 0;86}87 88void wait_and_step() {89  ASSERT_EQ(LIBC_NAMESPACE::mtx_init(&start_lock, mtx_plain),90            static_cast<int>(thrd_success));91  ASSERT_EQ(LIBC_NAMESPACE::mtx_init(&step_lock, mtx_plain),92            static_cast<int>(thrd_success));93 94  // In this test, we start a new thread but block it before it can make a95  // step. Once we ensure that the thread is blocked, we unblock it.96  // After unblocking, we then verify that the thread was indeed unblocked.97  step = false;98  started = false;99  ASSERT_EQ(LIBC_NAMESPACE::mtx_lock(&step_lock),100            static_cast<int>(thrd_success));101 102  thrd_t thread;103  LIBC_NAMESPACE::thrd_create(&thread, stepper, nullptr);104 105  while (true) {106    // Make sure the thread actually started.107    ASSERT_EQ(LIBC_NAMESPACE::mtx_lock(&start_lock),108              static_cast<int>(thrd_success));109    bool s = started;110    ASSERT_EQ(LIBC_NAMESPACE::mtx_unlock(&start_lock),111              static_cast<int>(thrd_success));112    if (s)113      break;114  }115 116  // Since |step_lock| is still locked, |step| should be false.117  ASSERT_FALSE(step);118 119  // Unlock the step lock and wait until the step is made.120  ASSERT_EQ(LIBC_NAMESPACE::mtx_unlock(&step_lock),121            static_cast<int>(thrd_success));122 123  while (true) {124    ASSERT_EQ(LIBC_NAMESPACE::mtx_lock(&step_lock),125              static_cast<int>(thrd_success));126    bool current_step_value = step;127    ASSERT_EQ(LIBC_NAMESPACE::mtx_unlock(&step_lock),128              static_cast<int>(thrd_success));129    if (current_step_value)130      break;131  }132 133  int retval = 123;134  LIBC_NAMESPACE::thrd_join(thread, &retval);135  ASSERT_EQ(retval, 0);136 137  LIBC_NAMESPACE::mtx_destroy(&start_lock);138  LIBC_NAMESPACE::mtx_destroy(&step_lock);139}140 141static constexpr int THREAD_COUNT = 10;142static mtx_t multiple_waiter_lock;143static mtx_t counter_lock;144static int wait_count = 0;145 146int waiter_func(void *) {147  LIBC_NAMESPACE::mtx_lock(&counter_lock);148  ++wait_count;149  LIBC_NAMESPACE::mtx_unlock(&counter_lock);150 151  // Block on the waiter lock until the main152  // thread unblocks.153  LIBC_NAMESPACE::mtx_lock(&multiple_waiter_lock);154  LIBC_NAMESPACE::mtx_unlock(&multiple_waiter_lock);155 156  LIBC_NAMESPACE::mtx_lock(&counter_lock);157  --wait_count;158  LIBC_NAMESPACE::mtx_unlock(&counter_lock);159 160  return 0;161}162 163void multiple_waiters() {164  LIBC_NAMESPACE::mtx_init(&multiple_waiter_lock, mtx_plain);165  LIBC_NAMESPACE::mtx_init(&counter_lock, mtx_plain);166 167  LIBC_NAMESPACE::mtx_lock(&multiple_waiter_lock);168  thrd_t waiters[THREAD_COUNT];169  for (int i = 0; i < THREAD_COUNT; ++i) {170    LIBC_NAMESPACE::thrd_create(waiters + i, waiter_func, nullptr);171  }172 173  // Spin until the counter is incremented to the desired174  // value.175  while (true) {176    LIBC_NAMESPACE::mtx_lock(&counter_lock);177    if (wait_count == THREAD_COUNT) {178      LIBC_NAMESPACE::mtx_unlock(&counter_lock);179      break;180    }181    LIBC_NAMESPACE::mtx_unlock(&counter_lock);182  }183 184  LIBC_NAMESPACE::mtx_unlock(&multiple_waiter_lock);185 186  int retval;187  for (int i = 0; i < THREAD_COUNT; ++i) {188    LIBC_NAMESPACE::thrd_join(waiters[i], &retval);189  }190 191  ASSERT_EQ(wait_count, 0);192 193  LIBC_NAMESPACE::mtx_destroy(&multiple_waiter_lock);194  LIBC_NAMESPACE::mtx_destroy(&counter_lock);195}196 197TEST_MAIN() {198  relay_counter();199  wait_and_step();200  multiple_waiters();201  return 0;202}203