| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| //===-- Implementation header for pthread_spin_init function ---*- C++ -*-===// | ||
| // | ||
| // 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 | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| #ifndef LLVM_LIBC_SRC_PTHREAD_PTHREAD_SPIN_INIT_H | ||
| #define LLVM_LIBC_SRC_PTHREAD_PTHREAD_SPIN_INIT_H | ||
|
|
||
| #include "src/__support/macros/config.h" | ||
| #include <pthread.h> | ||
|
|
||
| namespace LIBC_NAMESPACE_DECL { | ||
|
|
||
| int pthread_spin_init(pthread_spinlock_t *lock, int pshared); | ||
|
|
||
| } // namespace LIBC_NAMESPACE_DECL | ||
|
|
||
| #endif // LLVM_LIBC_SRC_PTHREAD_PTHREAD_SPIN_INIT_H |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,47 @@ | ||
| //===-- Implementation of pthread_spin_lock function ----------------------===// | ||
| // | ||
| // 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_spin_lock.h" | ||
| #include "hdr/errno_macros.h" | ||
| #include "src/__support/common.h" | ||
| #include "src/__support/threads/identifier.h" | ||
| #include "src/__support/threads/spin_lock.h" | ||
|
|
||
| namespace LIBC_NAMESPACE_DECL { | ||
|
|
||
| static_assert(sizeof(pthread_spinlock_t::__lockword) == sizeof(SpinLock) && | ||
| alignof(decltype(pthread_spinlock_t::__lockword)) == | ||
| alignof(SpinLock), | ||
| "pthread_spinlock_t::__lockword and SpinLock must be of the same " | ||
| "size and alignment"); | ||
|
|
||
| LLVM_LIBC_FUNCTION(int, pthread_spin_lock, (pthread_spinlock_t * lock)) { | ||
| // If an implementation detects that the value specified by the lock argument | ||
| // to pthread_spin_lock() or pthread_spin_trylock() does not refer to an | ||
| // initialized spin lock object, it is recommended that the function should | ||
| // fail and report an [EINVAL] error. | ||
| if (!lock) | ||
| return EINVAL; | ||
| auto spin_lock = reinterpret_cast<SpinLock *>(&lock->__lockword); | ||
| if (spin_lock->is_invalid()) | ||
| return EINVAL; | ||
|
|
||
| pid_t self_tid = internal::gettid(); | ||
| // If an implementation detects that the value specified by the lock argument | ||
| // to pthread_spin_lock() refers to a spin lock object for which the calling | ||
| // thread already holds the lock, it is recommended that the function should | ||
| // fail and report an [EDEADLK] error. | ||
| if (lock->__owner == self_tid) | ||
| return EDEADLK; | ||
|
|
||
| spin_lock->lock(); | ||
| lock->__owner = self_tid; | ||
| return 0; | ||
| } | ||
|
|
||
| } // namespace LIBC_NAMESPACE_DECL |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| //===-- Implementation header for pthread_spin_lock function ---*- C++ -*-===// | ||
| // | ||
| // 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 | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| #ifndef LLVM_LIBC_SRC_PTHREAD_PTHREAD_SPIN_LOCK_H | ||
| #define LLVM_LIBC_SRC_PTHREAD_PTHREAD_SPIN_LOCK_H | ||
|
|
||
| #include "src/__support/macros/config.h" | ||
| #include <pthread.h> | ||
|
|
||
| namespace LIBC_NAMESPACE_DECL { | ||
|
|
||
| int pthread_spin_lock(pthread_spinlock_t *lock); | ||
|
|
||
| } // namespace LIBC_NAMESPACE_DECL | ||
|
|
||
| #endif // LLVM_LIBC_SRC_PTHREAD_PTHREAD_SPIN_LOCK_H |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| //===-- Implementation of pthread_spin_trylock function -------------------===// | ||
| // | ||
| // 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_spin_trylock.h" | ||
| #include "hdr/errno_macros.h" | ||
| #include "src/__support/common.h" | ||
| #include "src/__support/threads/identifier.h" | ||
| #include "src/__support/threads/spin_lock.h" | ||
|
|
||
| namespace LIBC_NAMESPACE_DECL { | ||
|
|
||
| static_assert(sizeof(pthread_spinlock_t::__lockword) == sizeof(SpinLock) && | ||
| alignof(decltype(pthread_spinlock_t::__lockword)) == | ||
| alignof(SpinLock), | ||
| "pthread_spinlock_t::__lockword and SpinLock must be of the same " | ||
| "size and alignment"); | ||
|
|
||
| LLVM_LIBC_FUNCTION(int, pthread_spin_trylock, (pthread_spinlock_t * lock)) { | ||
| // If an implementation detects that the value specified by the lock argument | ||
| // to pthread_spin_lock() or pthread_spin_trylock() does not refer to an | ||
| // initialized spin lock object, it is recommended that the function should | ||
| // fail and report an [EINVAL] error. | ||
| if (!lock) | ||
| return EINVAL; | ||
| auto spin_lock = reinterpret_cast<SpinLock *>(&lock->__lockword); | ||
| if (!spin_lock || spin_lock->is_invalid()) | ||
| return EINVAL; | ||
| // Try to acquire the lock without blocking. | ||
| if (!spin_lock->try_lock()) | ||
| return EBUSY; | ||
| // We have acquired the lock. Update the owner field. | ||
| lock->__owner = internal::gettid(); | ||
| return 0; | ||
| } | ||
|
|
||
| } // namespace LIBC_NAMESPACE_DECL |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| //===-- Implementation header for pthread_spin_trylock function ---*- C++ | ||
| //-*-===// | ||
| // | ||
| // 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 | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| #ifndef LLVM_LIBC_SRC_PTHREAD_PTHREAD_SPIN_TRYLOCK_H | ||
| #define LLVM_LIBC_SRC_PTHREAD_PTHREAD_SPIN_TRYLOCK_H | ||
|
|
||
| #include "src/__support/macros/config.h" | ||
| #include <pthread.h> | ||
|
|
||
| namespace LIBC_NAMESPACE_DECL { | ||
|
|
||
| int pthread_spin_trylock(pthread_spinlock_t *lock); | ||
|
|
||
| } // namespace LIBC_NAMESPACE_DECL | ||
|
|
||
| #endif // LLVM_LIBC_SRC_PTHREAD_PTHREAD_SPIN_TRYLOCK_H |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,44 @@ | ||
| //===-- Implementation of pthread_spin_unlock function --------------------===// | ||
| // | ||
| // 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_spin_unlock.h" | ||
| #include "hdr/errno_macros.h" | ||
| #include "src/__support/common.h" | ||
| #include "src/__support/threads/identifier.h" | ||
| #include "src/__support/threads/spin_lock.h" | ||
|
|
||
| namespace LIBC_NAMESPACE_DECL { | ||
|
|
||
| static_assert(sizeof(pthread_spinlock_t::__lockword) == sizeof(SpinLock) && | ||
| alignof(decltype(pthread_spinlock_t::__lockword)) == | ||
| alignof(SpinLock), | ||
| "pthread_spinlock_t::__lockword and SpinLock must be of the same " | ||
| "size and alignment"); | ||
|
|
||
| LLVM_LIBC_FUNCTION(int, pthread_spin_unlock, (pthread_spinlock_t * lock)) { | ||
| // If an implementation detects that the value specified by the lock argument | ||
| // to pthread_spin_lock() or pthread_spin_trylock() does not refer to an | ||
| // initialized spin lock object, it is recommended that the function should | ||
| // fail and report an [EINVAL] error. | ||
| if (!lock) | ||
| return EINVAL; | ||
| auto spin_lock = reinterpret_cast<SpinLock *>(&lock->__lockword); | ||
| if (!spin_lock || spin_lock->is_invalid()) | ||
| return EINVAL; | ||
| // If an implementation detects that the value specified by the lock argument | ||
| // to pthread_spin_unlock() refers to a spin lock object for which the current | ||
| // thread does not hold the lock, it is recommended that the function should | ||
| // fail and report an [EPERM] error. | ||
| if (lock->__owner != internal::gettid()) | ||
| return EPERM; | ||
| // Release the lock. | ||
| lock->__owner = 0; | ||
| spin_lock->unlock(); | ||
| return 0; | ||
| } | ||
| } // namespace LIBC_NAMESPACE_DECL |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| //===-- Implementation header for pthread_spin_unlock function ---*- C++-*-===// | ||
| // | ||
| // 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 | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| #ifndef LLVM_LIBC_SRC_PTHREAD_PTHREAD_SPIN_UNLOCK_H | ||
| #define LLVM_LIBC_SRC_PTHREAD_PTHREAD_SPIN_UNLOCK_H | ||
|
|
||
| #include "src/__support/macros/config.h" | ||
| #include <pthread.h> | ||
|
|
||
| namespace LIBC_NAMESPACE_DECL { | ||
|
|
||
| int pthread_spin_unlock(pthread_spinlock_t *lock); | ||
|
|
||
| } // namespace LIBC_NAMESPACE_DECL | ||
|
|
||
| #endif // LLVM_LIBC_SRC_PTHREAD_PTHREAD_SPIN_UNLOCK_H |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,145 @@ | ||
| //===-- 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; | ||
| } |