diff --git a/Userland/Tests/CMakeLists.txt b/Userland/Tests/CMakeLists.txt index c16c400cdd..245fb76ee9 100644 --- a/Userland/Tests/CMakeLists.txt +++ b/Userland/Tests/CMakeLists.txt @@ -2,4 +2,5 @@ add_subdirectory(Kernel) add_subdirectory(LibC) add_subdirectory(LibGfx) add_subdirectory(LibM) +add_subdirectory(LibPthread) add_subdirectory(UserspaceEmulator) diff --git a/Userland/Tests/LibPthread/CMakeLists.txt b/Userland/Tests/LibPthread/CMakeLists.txt new file mode 100644 index 0000000000..2b0209e4ef --- /dev/null +++ b/Userland/Tests/LibPthread/CMakeLists.txt @@ -0,0 +1,4 @@ +file(GLOB TEST_SOURCES CONFIGURE_DEPENDS "*.cpp") +foreach(source ${TEST_SOURCES}) + serenity_test(${source} LibPthread LIBS LibPthread) +endforeach() diff --git a/Userland/Tests/LibPthread/TestLibPthreadSpinLocks.cpp b/Userland/Tests/LibPthread/TestLibPthreadSpinLocks.cpp new file mode 100644 index 0000000000..5cd13ec61a --- /dev/null +++ b/Userland/Tests/LibPthread/TestLibPthreadSpinLocks.cpp @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2021, Brian Gianforcaro + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include + +TEST_CASE(spin_init_process_scope) +{ + { + pthread_spinlock_t lock {}; + auto result = pthread_spin_init(&lock, PTHREAD_SCOPE_PROCESS); + EXPECT_EQ(0, result); + result = pthread_spin_destroy(&lock); + EXPECT_EQ(0, result); + } + + { + pthread_spinlock_t garbage_lock { 0x1337 }; + auto result = pthread_spin_init(&garbage_lock, PTHREAD_SCOPE_PROCESS); + EXPECT_EQ(0, result); + result = pthread_spin_destroy(&garbage_lock); + EXPECT_EQ(0, result); + } +} + +TEST_CASE(spin_init_system_scope) +{ + pthread_spinlock_t lock {}; + auto result = pthread_spin_init(&lock, PTHREAD_SCOPE_SYSTEM); + EXPECT_EQ(0, result); + pthread_spinlock_t garbage_lock { 0x99999 }; + result = pthread_spin_init(&garbage_lock, PTHREAD_SCOPE_PROCESS); + EXPECT_EQ(0, result); +} + +TEST_CASE(spin_lock) +{ + pthread_spinlock_t lock {}; + auto result = pthread_spin_lock(&lock); + EXPECT_EQ(0, result); + + // We should detect that this thread already holds this lock. + result = pthread_spin_lock(&lock); + EXPECT_EQ(EDEADLK, result); +} + +TEST_CASE(spin_try_lock) +{ + { + pthread_spinlock_t lock {}; + auto result = pthread_spin_trylock(&lock); + EXPECT_EQ(0, result); + + result = pthread_spin_unlock(&lock); + EXPECT_EQ(0, result); + } + + { + pthread_spinlock_t lock {}; + auto result = pthread_spin_trylock(&lock); + EXPECT_EQ(0, result); + + // We should detect that this thread already holds the lock. + result = pthread_spin_trylock(&lock); + EXPECT_EQ(EBUSY, result); + } +} + +static void lock_from_different_thread(pthread_spinlock_t* lock) +{ + pthread_t thread_id {}; + auto result = pthread_create( + &thread_id, nullptr, [](void* param) -> void* { + auto lock = (pthread_spinlock_t*)param; + pthread_spin_lock(lock); + return nullptr; + }, + lock); + EXPECT_EQ(0, result); + + result = pthread_join(thread_id, nullptr); + EXPECT_EQ(0, result); +} + +TEST_CASE(spin_unlock) +{ + { + pthread_spinlock_t lock {}; + auto result = pthread_spin_lock(&lock); + EXPECT_EQ(0, result); + + result = pthread_spin_unlock(&lock); + EXPECT_EQ(0, result); + } + + { + pthread_spinlock_t lock {}; + lock_from_different_thread(&lock); + + auto result = pthread_spin_unlock(&lock); + EXPECT_EQ(EPERM, result); + } +} + +TEST_CASE(spin_destroy) +{ + { + pthread_spinlock_t lock {}; + auto result = pthread_spin_lock(&lock); + EXPECT_EQ(0, result); + + result = pthread_spin_destroy(&lock); + EXPECT_EQ(EBUSY, result); + + result = pthread_spin_unlock(&lock); + EXPECT_EQ(0, result); + } + + { + pthread_spinlock_t lock {}; + lock_from_different_thread(&lock); + + auto result = pthread_spin_destroy(&lock); + EXPECT_EQ(EBUSY, result); + } +}