Tests: Add unit tests for the pthread_spinlock_t API.

This change establishes a new set of LibTest based tests for validating
the functionality of our pthread implementation. The first tests added
validate the error handling for the pthread spinlock API:
 * pthread_spin_init
 * pthread_spin_lock
 * pthread_spin_try_lock
 * pthread_spin_unlock
 * pthread_spin_destroy
This commit is contained in:
Brian Gianforcaro 2021-05-02 03:37:07 -07:00 committed by Andreas Kling
parent 9f07627f58
commit f5c676fd86
3 changed files with 135 additions and 0 deletions

View file

@ -2,4 +2,5 @@ add_subdirectory(Kernel)
add_subdirectory(LibC)
add_subdirectory(LibGfx)
add_subdirectory(LibM)
add_subdirectory(LibPthread)
add_subdirectory(UserspaceEmulator)

View file

@ -0,0 +1,4 @@
file(GLOB TEST_SOURCES CONFIGURE_DEPENDS "*.cpp")
foreach(source ${TEST_SOURCES})
serenity_test(${source} LibPthread LIBS LibPthread)
endforeach()

View file

@ -0,0 +1,130 @@
/*
* Copyright (c) 2021, Brian Gianforcaro <bgianf@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibPthread/pthread.h>
#include <LibTest/TestCase.h>
#include <unistd.h>
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);
}
}