| //===-- 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; |
| } |