| //===-- Tests for pthread_spinlock ----------------------------------------===// | |
| // | |
| // 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 "hdr/errno_macros.h" | |
| #include "src/pthread/pthread_create.h" | |
| #include "src/pthread/pthread_join.h" | |
| #include "src/pthread/pthread_spin_destroy.h" | |
| #include "src/pthread/pthread_spin_init.h" | |
| #include "src/pthread/pthread_spin_lock.h" | |
| #include "src/pthread/pthread_spin_trylock.h" | |
| #include "src/pthread/pthread_spin_unlock.h" | |
| #include "test/IntegrationTest/test.h" | |
| #include <pthread.h> | |
| namespace { | |
| void smoke_test() { | |
| pthread_spinlock_t lock; | |
| ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_init(&lock, PTHREAD_PROCESS_PRIVATE), | |
| 0); | |
| ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_lock(&lock), 0); | |
| ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_unlock(&lock), 0); | |
| ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_destroy(&lock), 0); | |
| } | |
| void trylock_test() { | |
| pthread_spinlock_t lock; | |
| ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_init(&lock, PTHREAD_PROCESS_PRIVATE), | |
| 0); | |
| ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_trylock(&lock), 0); | |
| ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_trylock(&lock), EBUSY); | |
| ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_unlock(&lock), 0); | |
| ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_trylock(&lock), 0); | |
| ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_unlock(&lock), 0); | |
| ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_destroy(&lock), 0); | |
| } | |
| void destroy_held_lock_test() { | |
| pthread_spinlock_t lock; | |
| ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_init(&lock, PTHREAD_PROCESS_PRIVATE), | |
| 0); | |
| ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_lock(&lock), 0); | |
| ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_destroy(&lock), EBUSY); | |
| ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_unlock(&lock), 0); | |
| ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_destroy(&lock), 0); | |
| } | |
| void use_after_destroy_test() { | |
| pthread_spinlock_t lock; | |
| ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_init(&lock, PTHREAD_PROCESS_PRIVATE), | |
| 0); | |
| ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_destroy(&lock), 0); | |
| ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_unlock(&lock), EINVAL); | |
| ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_lock(&lock), EINVAL); | |
| ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_trylock(&lock), EINVAL); | |
| ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_destroy(&lock), EINVAL); | |
| } | |
| void unlock_without_holding_test() { | |
| pthread_spinlock_t lock; | |
| ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_init(&lock, PTHREAD_PROCESS_PRIVATE), | |
| 0); | |
| ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_unlock(&lock), EPERM); | |
| ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_destroy(&lock), 0); | |
| } | |
| void deadlock_test() { | |
| pthread_spinlock_t lock; | |
| ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_init(&lock, PTHREAD_PROCESS_PRIVATE), | |
| 0); | |
| ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_lock(&lock), 0); | |
| ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_lock(&lock), EDEADLK); | |
| ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_unlock(&lock), 0); | |
| ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_destroy(&lock), 0); | |
| } | |
| void null_lock_test() { | |
| ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_init(nullptr, 0), EINVAL); | |
| ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_lock(nullptr), EINVAL); | |
| ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_trylock(nullptr), EINVAL); | |
| ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_unlock(nullptr), EINVAL); | |
| ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_destroy(nullptr), EINVAL); | |
| } | |
| void pshared_attribute_test() { | |
| pthread_spinlock_t lock; | |
| ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_init(&lock, PTHREAD_PROCESS_SHARED), | |
| 0); | |
| ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_destroy(&lock), 0); | |
| ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_init(&lock, PTHREAD_PROCESS_PRIVATE), | |
| 0); | |
| ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_destroy(&lock), 0); | |
| ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_init(&lock, -1), EINVAL); | |
| } | |
| void multi_thread_test() { | |
| struct shared_data { | |
| pthread_spinlock_t lock; | |
| int count = 0; | |
| } shared; | |
| pthread_t thread[10]; | |
| ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_init(&shared.lock, 0), 0); | |
| for (int i = 0; i < 10; ++i) { | |
| ASSERT_EQ( | |
| LIBC_NAMESPACE::pthread_create( | |
| &thread[i], nullptr, | |
| [](void *arg) -> void * { | |
| auto *data = static_cast<shared_data *>(arg); | |
| for (int j = 0; j < 1000; ++j) { | |
| ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_lock(&data->lock), 0); | |
| data->count += j; | |
| ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_unlock(&data->lock), 0); | |
| } | |
| return nullptr; | |
| }, | |
| &shared), | |
| 0); | |
| } | |
| for (int i = 0; i < 10; ++i) { | |
| ASSERT_EQ(LIBC_NAMESPACE::pthread_join(thread[i], nullptr), 0); | |
| } | |
| ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_destroy(&shared.lock), 0); | |
| ASSERT_EQ(shared.count, 1000 * 999 * 5); | |
| } | |
| } // namespace | |
| TEST_MAIN() { | |
| smoke_test(); | |
| trylock_test(); | |
| destroy_held_lock_test(); | |
| use_after_destroy_test(); | |
| unlock_without_holding_test(); | |
| deadlock_test(); | |
| multi_thread_test(); | |
| null_lock_test(); | |
| pshared_attribute_test(); | |
| return 0; | |
| } |