blob: 30b1cc93f4a5c576daa1786e7d81afd95d964f52 [file]
//===-- Tests for pthread_join-- ------------------------------------------===//
//
// 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/pthread/pthread_create.h"
#include "src/pthread/pthread_join.h"
#include "src/pthread/pthread_self.h"
#include "src/__support/CPP/atomic.h"
#include "src/__support/threads/thread.h"
#include "test/IntegrationTest/test.h"
#include <errno.h>
#include <pthread.h>
static void *simple_func(void *) { return nullptr; }
static void null_join_test() {
pthread_t tid;
ASSERT_EQ(LIBC_NAMESPACE::pthread_create(&tid, nullptr, simple_func, nullptr),
0);
ASSERT_ERRNO_SUCCESS();
ASSERT_EQ(LIBC_NAMESPACE::pthread_join(tid, nullptr), 0);
ASSERT_ERRNO_SUCCESS();
}
static void self_join_test() {
ASSERT_EQ(
LIBC_NAMESPACE::pthread_join(LIBC_NAMESPACE::pthread_self(), nullptr),
EDEADLK);
}
struct MutualJoinArgs {
pthread_t *peer;
LIBC_NAMESPACE::cpp::Atomic<int> *ready_count;
LIBC_NAMESPACE::cpp::Atomic<int> *start;
LIBC_NAMESPACE::cpp::Atomic<int> *result;
int start_value;
};
static void *mutual_join_func(void *arg) {
auto *args = reinterpret_cast<MutualJoinArgs *>(arg);
args->ready_count->fetch_add(1);
while (args->start->load() < args->start_value)
; // Spin until this thread is released to join its peer.
args->result->store(LIBC_NAMESPACE::pthread_join(*args->peer, nullptr));
return nullptr;
}
static bool is_joining(pthread_t joiner, pthread_t target) {
auto *joiner_thread = reinterpret_cast<LIBC_NAMESPACE::Thread *>(&joiner);
auto *target_thread = reinterpret_cast<LIBC_NAMESPACE::Thread *>(&target);
return target_thread->attrib->joiner.load() == joiner_thread->attrib;
}
static void mutual_join_test() {
pthread_t thread1;
pthread_t thread2;
LIBC_NAMESPACE::cpp::Atomic<int> ready_count(0);
LIBC_NAMESPACE::cpp::Atomic<int> start(0);
LIBC_NAMESPACE::cpp::Atomic<int> result1(-1);
LIBC_NAMESPACE::cpp::Atomic<int> result2(-1);
MutualJoinArgs args1{&thread2, &ready_count, &start, &result1, 1};
MutualJoinArgs args2{&thread1, &ready_count, &start, &result2, 2};
ASSERT_EQ(LIBC_NAMESPACE::pthread_create(&thread1, nullptr, mutual_join_func,
&args1),
0);
ASSERT_EQ(LIBC_NAMESPACE::pthread_create(&thread2, nullptr, mutual_join_func,
&args2),
0);
while (ready_count.load() != 2)
; // Spin until both threads are ready to join each other.
start.store(1);
while (!is_joining(thread1, thread2))
; // Spin until thread1 has started joining thread2.
start.store(2);
while (result1.load() == -1 || result2.load() == -1)
; // Spin until the successful joiner and deadlock loser have both exited.
// A thread is recovered to joinable state if its joining requester gets
// EDEADLK.
bool thread1_joinable = result2.load() == EDEADLK;
bool thread2_joinable = result1.load() == EDEADLK;
ASSERT_TRUE(thread1_joinable || thread2_joinable);
if (thread1_joinable)
ASSERT_EQ(LIBC_NAMESPACE::pthread_join(thread1, nullptr), 0);
if (thread2_joinable)
ASSERT_EQ(LIBC_NAMESPACE::pthread_join(thread2, nullptr), 0);
}
TEST_MAIN() {
errno = 0;
null_join_test();
self_join_test();
mutual_join_test();
return 0;
}