diff --git a/libc/include/llvm-libc-macros/pthread-macros.h b/libc/include/llvm-libc-macros/pthread-macros.h index bba3faa11c4e..e6ade6507e07 100644 --- a/libc/include/llvm-libc-macros/pthread-macros.h +++ b/libc/include/llvm-libc-macros/pthread-macros.h @@ -47,15 +47,17 @@ #define PTHREAD_RWLOCK_INITIALIZER \ { \ - /* .__is_pshared = */ 0, \ - /* .__preference = */ 0, \ - /* .__state = */ 0, \ + /* .__raw = */ { \ + /* .__is_pshared = */ 0, \ + /* .__preference = */ 0, \ + /* .__state = */ 0, \ + /* .__wait_queue_mutex = */ {0}, \ + /* .__pending_readers = */ {0}, \ + /* .__pending_writers = */ {0}, \ + /* .__reader_serialization = */ {0}, \ + /* .__writer_serialization = */ {0}, \ + }, \ /* .__write_tid = */ 0, \ - /* .__wait_queue_mutex = */ {0}, \ - /* .__pending_readers = */ {0}, \ - /* .__pending_writers = */ {0}, \ - /* .__reader_serialization = */ {0}, \ - /* .__writer_serialization = */ {0}, \ } // glibc extensions diff --git a/libc/include/llvm-libc-types/pthread_rwlock_t.h b/libc/include/llvm-libc-types/pthread_rwlock_t.h index 4a7c6c75250a..38778868409d 100644 --- a/libc/include/llvm-libc-types/pthread_rwlock_t.h +++ b/libc/include/llvm-libc-types/pthread_rwlock_t.h @@ -12,15 +12,17 @@ #include "__futex_word.h" #include "pid_t.h" typedef struct { - unsigned __is_pshared : 1; - unsigned __preference : 1; - int __state; + struct { + unsigned __is_pshared : 1; + unsigned __preference : 1; + int __state; + __futex_word __wait_queue_mutex; + __futex_word __pending_readers; + __futex_word __pending_writers; + __futex_word __reader_serialization; + __futex_word __writer_serialization; + } __raw; pid_t __writer_tid; - __futex_word __wait_queue_mutex; - __futex_word __pending_readers; - __futex_word __pending_writers; - __futex_word __reader_serialization; - __futex_word __writer_serialization; } pthread_rwlock_t; #endif // LLVM_LIBC_TYPES_PTHREAD_RWLOCK_T_H diff --git a/libc/src/__support/threads/CMakeLists.txt b/libc/src/__support/threads/CMakeLists.txt index dc1445634da2..9b0b77be098e 100644 --- a/libc/src/__support/threads/CMakeLists.txt +++ b/libc/src/__support/threads/CMakeLists.txt @@ -24,54 +24,79 @@ if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS}) endif() if(TARGET libc.src.__support.threads.${LIBC_TARGET_OS}.futex_utils) + libc_set_definition(rwlock_default_spin_count "LIBC_COPT_RWLOCK_DEFAULT_SPIN_COUNT=${LIBC_CONF_RWLOCK_DEFAULT_SPIN_COUNT}") + add_header_library( raw_mutex HDRS - raw_mutex.h + raw_mutex.h COMPILE_OPTIONS - ${monotonicity_flags} + ${monotonicity_flags} DEPENDS - .${LIBC_TARGET_OS}.futex_utils - libc.src.__support.threads.sleep - libc.src.__support.time.abs_timeout - libc.src.__support.time.monotonicity - libc.src.__support.CPP.optional - libc.hdr.types.pid_t + .${LIBC_TARGET_OS}.futex_utils + libc.src.__support.threads.sleep + libc.src.__support.time.abs_timeout + libc.src.__support.time.monotonicity + libc.src.__support.CPP.optional + libc.hdr.types.pid_t + ) + + add_header_library( + raw_rwlock + HDRS + raw_rwlock.h + COMPILE_OPTIONS + ${rwlock_default_spin_count} + ${monotonicity_flags} + DEPENDS + .raw_mutex + libc.src.__support.common + libc.src.__support.CPP.limits + ) + + add_header_library( + unix_rwlock + HDRS + unix_rwlock.h + DEPENDS + .raw_rwlock + libc.hdr.types.pid_t + libc.src.__support.threads.identifier ) add_header_library( unix_mutex HDRS - unix_mutex.h + unix_mutex.h DEPENDS - .raw_mutex + .raw_mutex ) add_header_library( mutex HDRS - mutex.h + mutex.h DEPENDS - .unix_mutex + .unix_mutex ) add_object_library( fork_callbacks SRCS - fork_callbacks.cpp + fork_callbacks.cpp HDRS - fork_callbacks.h + fork_callbacks.h DEPENDS - .mutex - libc.src.__support.CPP.mutex + .mutex + libc.src.__support.CPP.mutex ) elseif(NOT (LIBC_CONF_THREAD_MODE STREQUAL LIBC_THREAD_MODE_PLATFORM)) add_header_library( mutex HDRS - mutex.h + mutex.h DEPENDS - .mutex_common + .mutex_common ) endif() diff --git a/libc/src/__support/threads/linux/CMakeLists.txt b/libc/src/__support/threads/linux/CMakeLists.txt index 9d292003f21c..5ada10ed04b8 100644 --- a/libc/src/__support/threads/linux/CMakeLists.txt +++ b/libc/src/__support/threads/linux/CMakeLists.txt @@ -31,23 +31,6 @@ else() libc_set_definition(monotonicity_flags LIBC_COPT_TIMEOUT_ENSURE_MONOTONICITY=0) endif() -libc_set_definition(rwlock_default_spin_count "LIBC_COPT_RWLOCK_DEFAULT_SPIN_COUNT=${LIBC_CONF_RWLOCK_DEFAULT_SPIN_COUNT}") -add_header_library( - rwlock - HDRS - rwlock.h - COMPILE_OPTIONS - ${rwlock_default_spin_count} - ${monotonicity_flags} - DEPENDS - .futex_utils - libc.src.__support.threads.raw_mutex - libc.src.__support.common - libc.src.__support.OSUtil.osutil - libc.src.__support.CPP.limits - libc.src.__support.threads.identifier -) - add_object_library( thread SRCS diff --git a/libc/src/__support/threads/linux/rwlock.h b/libc/src/__support/threads/raw_rwlock.h similarity index 90% rename from libc/src/__support/threads/linux/rwlock.h rename to libc/src/__support/threads/raw_rwlock.h index 9fb3ff972b58..a29667221c5d 100644 --- a/libc/src/__support/threads/linux/rwlock.h +++ b/libc/src/__support/threads/raw_rwlock.h @@ -1,27 +1,22 @@ -//===--- Implementation of a Linux RwLock class ---------------*- C++ -*-===// +//===--- Implementation of the RawRwLock class -----------------*- 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_SUPPORT_THREADS_LINUX_RWLOCK_H -#define LLVM_LIBC_SRC_SUPPORT_THREADS_LINUX_RWLOCK_H +#ifndef LLVM_LIBC_SRC___SUPPORT_THREADS_RAW_RWLOCK_H +#define LLVM_LIBC_SRC___SUPPORT_THREADS_RAW_RWLOCK_H #include "hdr/errno_macros.h" -#include "hdr/types/pid_t.h" #include "src/__support/CPP/atomic.h" #include "src/__support/CPP/limits.h" #include "src/__support/CPP/optional.h" -#include "src/__support/OSUtil/syscall.h" #include "src/__support/common.h" #include "src/__support/libc_assert.h" #include "src/__support/macros/attributes.h" #include "src/__support/macros/config.h" #include "src/__support/macros/optimization.h" -#include "src/__support/threads/identifier.h" -#include "src/__support/threads/linux/futex_utils.h" -#include "src/__support/threads/linux/futex_word.h" #include "src/__support/threads/raw_mutex.h" #include "src/__support/threads/sleep.h" @@ -39,13 +34,20 @@ #endif namespace LIBC_NAMESPACE_DECL { -// Forward declaration of the RwLock class. -class RwLock; // A namespace to rwlock specific utilities. namespace rwlock { // The role of the thread in the RwLock. enum class Role { Reader = 0, Writer = 1 }; +enum class LockResult : int { + Success = 0, + TimedOut = ETIMEDOUT, + Overflow = EAGAIN, + Busy = EBUSY, + Deadlock = EDEADLOCK, + PermissionDenied = EPERM, +}; + // A waiting queue to keep track of the pending readers and writers. class WaitingQueue final : private RawMutex { /* FutexWordType raw_mutex; (from base class) */ @@ -296,7 +298,7 @@ public: }; } // namespace rwlock -class RwLock { +class RawRwLock { using RwState = rwlock::RwState; using Role = rwlock::Role; using WaitingQueue = rwlock::WaitingQueue; @@ -307,14 +309,7 @@ public: // because it is a common error to assume the lock success without checking // the return value, which can lead to undefined behaviors or other subtle // bugs that are hard to reason about. - enum class LockResult : int { - Success = 0, - TimedOut = ETIMEDOUT, - Overflow = EAGAIN, /* EAGAIN is specified in the standard for overflow. */ - Busy = EBUSY, - Deadlock = EDEADLOCK, - PermissionDenied = EPERM, - }; + using LockResult = rwlock::LockResult; private: // Whether the RwLock is shared between processes. @@ -325,10 +320,6 @@ private: unsigned preference : 1; // RwState to keep track of the RwLock. cpp::Atomic state; - // writer_tid is used to keep track of the thread id of the writer. Notice - // that TLS address is not a good idea here since it may remains the same - // across forked processes. - cpp::Atomic writer_tid; // Waiting queue to keep track of the readers and writers. WaitingQueue queue; @@ -357,10 +348,8 @@ private: while (LIBC_LIKELY(old.can_acquire(get_preference()))) { if (LIBC_LIKELY(old.compare_exchange_weak_with( state, old.set_writer_bit(), cpp::MemoryOrder::ACQUIRE, - cpp::MemoryOrder::RELAXED))) { - writer_tid.store(internal::gettid(), cpp::MemoryOrder::RELAXED); + cpp::MemoryOrder::RELAXED))) return LockResult::Success; - } // Notice that old is updated by the compare_exchange_weak_with // function. } @@ -368,34 +357,10 @@ private: } } -public: - LIBC_INLINE constexpr RwLock(Role preference = Role::Reader, - bool is_pshared = false) - : is_pshared(is_pshared), - preference(static_cast(preference) & 1u), state(0), - writer_tid(0), queue() {} - - [[nodiscard]] - LIBC_INLINE LockResult try_read_lock() { - RwState old = RwState::load(state, cpp::MemoryOrder::RELAXED); - return try_lock(old); - } - [[nodiscard]] - LIBC_INLINE LockResult try_write_lock() { - RwState old = RwState::load(state, cpp::MemoryOrder::RELAXED); - return try_lock(old); - } - -private: template LIBC_INLINE LockResult lock_slow(cpp::optional timeout = cpp::nullopt, unsigned spin_count = LIBC_COPT_RWLOCK_DEFAULT_SPIN_COUNT) { - // Phase 1: deadlock detection. - // A deadlock happens if this is a RAW/WAW lock in the same thread. - if (writer_tid.load(cpp::MemoryOrder::RELAXED) == internal::gettid()) - return LockResult::Deadlock; - #if LIBC_COPT_TIMEOUT_ENSURE_MONOTONICITY // Phase 2: convert the timeout if necessary. if (timeout) @@ -465,27 +430,6 @@ private: } } -public: - [[nodiscard]] - LIBC_INLINE LockResult - read_lock(cpp::optional timeout = cpp::nullopt, - unsigned spin_count = LIBC_COPT_RWLOCK_DEFAULT_SPIN_COUNT) { - LockResult result = try_read_lock(); - if (LIBC_LIKELY(result != LockResult::Busy)) - return result; - return lock_slow(timeout, spin_count); - } - [[nodiscard]] - LIBC_INLINE LockResult - write_lock(cpp::optional timeout = cpp::nullopt, - unsigned spin_count = LIBC_COPT_RWLOCK_DEFAULT_SPIN_COUNT) { - LockResult result = try_write_lock(); - if (LIBC_LIKELY(result != LockResult::Busy)) - return result; - return lock_slow(timeout, spin_count); - } - -private: // Compiler (clang 19.0) somehow decides that this function may be inlined, // which leads to a larger unlock function that is infeasible to be inlined. // Since notifcation routine is colder we mark it as noinline explicitly. @@ -502,8 +446,9 @@ private: } else if (guard.pending_count() != 0) { guard.serialization()++; status = WakeTarget::Readers; - } else + } else { status = WakeTarget::None; + } } if (status == WakeTarget::Readers) @@ -513,16 +458,52 @@ private: } public: + LIBC_INLINE bool has_active_writer() { + return RwState::load(state, cpp::MemoryOrder::RELAXED).has_active_writer(); + } + + LIBC_INLINE constexpr RawRwLock(Role preference = Role::Reader, + bool is_pshared = false) + : is_pshared(is_pshared), + preference(static_cast(preference) & 1u), state(0), queue() {} + + [[nodiscard]] + LIBC_INLINE LockResult try_read_lock() { + RwState old = RwState::load(state, cpp::MemoryOrder::RELAXED); + return try_lock(old); + } + + [[nodiscard]] + LIBC_INLINE LockResult try_write_lock() { + RwState old = RwState::load(state, cpp::MemoryOrder::RELAXED); + return try_lock(old); + } + + [[nodiscard]] + LIBC_INLINE LockResult + read_lock(cpp::optional timeout = cpp::nullopt, + unsigned spin_count = LIBC_COPT_RWLOCK_DEFAULT_SPIN_COUNT) { + LockResult result = try_read_lock(); + if (LIBC_LIKELY(result != LockResult::Busy)) + return result; + return lock_slow(timeout, spin_count); + } + + [[nodiscard]] + LIBC_INLINE LockResult + write_lock(cpp::optional timeout = cpp::nullopt, + unsigned spin_count = LIBC_COPT_RWLOCK_DEFAULT_SPIN_COUNT) { + LockResult result = try_write_lock(); + if (LIBC_LIKELY(result != LockResult::Busy)) + return result; + return lock_slow(timeout, spin_count); + } + [[nodiscard]] LIBC_INLINE LockResult unlock() { RwState old = RwState::load(state, cpp::MemoryOrder::RELAXED); if (old.has_active_writer()) { // The lock is held by a writer. - // Check if we are the owner of the lock. - if (writer_tid.load(cpp::MemoryOrder::RELAXED) != internal::gettid()) - return LockResult::PermissionDenied; - // clear writer tid. - writer_tid.store(0, cpp::MemoryOrder::RELAXED); // clear the writer bit. old = RwState::fetch_clear_active_writer(state, cpp::MemoryOrder::RELEASE); @@ -536,23 +517,25 @@ public: // If there is no pending readers or writers, we are done. if (!old.has_last_reader() || !old.has_pending()) return LockResult::Success; - } else + } else { return LockResult::PermissionDenied; + } notify_pending_threads(); return LockResult::Success; } - // We do not allocate any special resources for the RwLock, so this function - // will only check if the lock is currently held by any thread. [[nodiscard]] LIBC_INLINE LockResult check_for_destroy() { + // We do not allocate any special resources for the RwLock, so this function + // will only check if the lock is currently held by any thread. RwState old = RwState::load(state, cpp::MemoryOrder::RELAXED); if (old.has_active_owner()) return LockResult::Busy; return LockResult::Success; } }; + } // namespace LIBC_NAMESPACE_DECL -#endif // LLVM_LIBC_SRC_SUPPORT_THREADS_LINUX_RWLOCK_H +#endif // LLVM_LIBC_SRC___SUPPORT_THREADS_RAW_RWLOCK_H diff --git a/libc/src/__support/threads/unix_rwlock.h b/libc/src/__support/threads/unix_rwlock.h new file mode 100644 index 000000000000..f75220837600 --- /dev/null +++ b/libc/src/__support/threads/unix_rwlock.h @@ -0,0 +1,90 @@ +//===--- Implementation of a Unix RwLock class -----------------*- 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___SUPPORT_THREADS_UNIX_RWLOCK_H +#define LLVM_LIBC_SRC___SUPPORT_THREADS_UNIX_RWLOCK_H + +#include "hdr/types/pid_t.h" +#include "src/__support/CPP/atomic.h" +#include "src/__support/common.h" +#include "src/__support/threads/identifier.h" +#include "src/__support/threads/raw_rwlock.h" + +namespace LIBC_NAMESPACE_DECL { + +class RwLock final { + RawRwLock raw; + cpp::Atomic writer_tid; + + LIBC_INLINE pid_t get_writer_tid() { + return writer_tid.load(cpp::MemoryOrder::RELAXED); + } + + LIBC_INLINE void set_writer_tid(pid_t tid) { + writer_tid.store(tid, cpp::MemoryOrder::RELAXED); + } + +public: + using LockResult = rwlock::LockResult; + using Role = rwlock::Role; + + LIBC_INLINE constexpr RwLock(Role preference = Role::Reader, + bool is_pshared = false) + : raw(preference, is_pshared), writer_tid(0) {} + + [[nodiscard]] LIBC_INLINE LockResult try_read_lock() { + return raw.try_read_lock(); + } + + [[nodiscard]] LIBC_INLINE LockResult try_write_lock() { + LockResult result = raw.try_write_lock(); + if (result == LockResult::Success) + set_writer_tid(internal::gettid()); + return result; + } + + [[nodiscard]] + LIBC_INLINE LockResult + read_lock(cpp::optional timeout = cpp::nullopt, + unsigned spin_count = LIBC_COPT_RWLOCK_DEFAULT_SPIN_COUNT) { + if (get_writer_tid() == internal::gettid()) + return LockResult::Deadlock; + return raw.read_lock(timeout, spin_count); + } + + [[nodiscard]] + LIBC_INLINE LockResult + write_lock(cpp::optional timeout = cpp::nullopt, + unsigned spin_count = LIBC_COPT_RWLOCK_DEFAULT_SPIN_COUNT) { + if (get_writer_tid() == internal::gettid()) + return LockResult::Deadlock; + + LockResult result = raw.write_lock(timeout, spin_count); + if (result == LockResult::Success) + set_writer_tid(internal::gettid()); + return result; + } + + [[nodiscard]] LIBC_INLINE LockResult unlock() { + bool is_writer_unlock = raw.has_active_writer(); + if (is_writer_unlock) { + if (get_writer_tid() != internal::gettid()) + return LockResult::PermissionDenied; + set_writer_tid(0); + } + return raw.unlock(); + } + + [[nodiscard]] LIBC_INLINE LockResult check_for_destroy() { + return raw.check_for_destroy(); + } +}; + +} // namespace LIBC_NAMESPACE_DECL + +#endif // LLVM_LIBC_SRC___SUPPORT_THREADS_UNIX_RWLOCK_H diff --git a/libc/src/pthread/CMakeLists.txt b/libc/src/pthread/CMakeLists.txt index d7f03c8f18d2..5e41dbdf1c84 100644 --- a/libc/src/pthread/CMakeLists.txt +++ b/libc/src/pthread/CMakeLists.txt @@ -591,7 +591,7 @@ add_entrypoint_object( pthread_rwlock_init.h DEPENDS libc.include.pthread - libc.src.__support.threads.linux.rwlock + libc.src.__support.threads.unix_rwlock libc.src.__support.CPP.new ) @@ -603,7 +603,7 @@ add_entrypoint_object( pthread_rwlock_tryrdlock.h DEPENDS libc.include.pthread - libc.src.__support.threads.linux.rwlock + libc.src.__support.threads.unix_rwlock ) add_entrypoint_object( @@ -614,7 +614,7 @@ add_entrypoint_object( pthread_rwlock_trywrlock.h DEPENDS libc.include.pthread - libc.src.__support.threads.linux.rwlock + libc.src.__support.threads.unix_rwlock libc.src.errno.errno ) @@ -626,7 +626,7 @@ add_entrypoint_object( pthread_rwlock_clockrdlock.h DEPENDS libc.include.pthread - libc.src.__support.threads.linux.rwlock + libc.src.__support.threads.unix_rwlock ) add_entrypoint_object( @@ -637,7 +637,7 @@ add_entrypoint_object( pthread_rwlock_clockwrlock.h DEPENDS libc.include.pthread - libc.src.__support.threads.linux.rwlock + libc.src.__support.threads.unix_rwlock ) add_entrypoint_object( @@ -648,7 +648,7 @@ add_entrypoint_object( pthread_rwlock_timedrdlock.h DEPENDS libc.include.pthread - libc.src.__support.threads.linux.rwlock + libc.src.__support.threads.unix_rwlock libc.src.errno.errno ) @@ -660,7 +660,7 @@ add_entrypoint_object( pthread_rwlock_timedwrlock.h DEPENDS libc.include.pthread - libc.src.__support.threads.linux.rwlock + libc.src.__support.threads.unix_rwlock ) add_entrypoint_object( @@ -671,7 +671,7 @@ add_entrypoint_object( pthread_rwlock_rdlock.h DEPENDS libc.include.pthread - libc.src.__support.threads.linux.rwlock + libc.src.__support.threads.unix_rwlock ) add_entrypoint_object( @@ -682,7 +682,7 @@ add_entrypoint_object( pthread_rwlock_wrlock.h DEPENDS libc.include.pthread - libc.src.__support.threads.linux.rwlock + libc.src.__support.threads.unix_rwlock ) add_entrypoint_object( @@ -693,7 +693,7 @@ add_entrypoint_object( pthread_rwlock_unlock.h DEPENDS libc.include.pthread - libc.src.__support.threads.linux.rwlock + libc.src.__support.threads.unix_rwlock libc.src.errno.errno ) @@ -705,7 +705,7 @@ add_entrypoint_object( pthread_rwlock_destroy.h DEPENDS libc.include.pthread - libc.src.__support.threads.linux.rwlock + libc.src.__support.threads.unix_rwlock ) add_entrypoint_object( diff --git a/libc/src/pthread/pthread_rwlock_clockrdlock.cpp b/libc/src/pthread/pthread_rwlock_clockrdlock.cpp index 1e44e6d7694f..2a15ce443aac 100644 --- a/libc/src/pthread/pthread_rwlock_clockrdlock.cpp +++ b/libc/src/pthread/pthread_rwlock_clockrdlock.cpp @@ -11,7 +11,7 @@ #include "hdr/errno_macros.h" #include "src/__support/common.h" #include "src/__support/macros/config.h" -#include "src/__support/threads/linux/rwlock.h" +#include "src/__support/threads/unix_rwlock.h" #include diff --git a/libc/src/pthread/pthread_rwlock_clockwrlock.cpp b/libc/src/pthread/pthread_rwlock_clockwrlock.cpp index 787a1b1484df..7d9613fbeb66 100644 --- a/libc/src/pthread/pthread_rwlock_clockwrlock.cpp +++ b/libc/src/pthread/pthread_rwlock_clockwrlock.cpp @@ -11,7 +11,7 @@ #include "hdr/errno_macros.h" #include "src/__support/common.h" #include "src/__support/macros/config.h" -#include "src/__support/threads/linux/rwlock.h" +#include "src/__support/threads/unix_rwlock.h" #include "src/__support/time/abs_timeout.h" #include diff --git a/libc/src/pthread/pthread_rwlock_destroy.cpp b/libc/src/pthread/pthread_rwlock_destroy.cpp index afc5622e54a0..85cf127630fe 100644 --- a/libc/src/pthread/pthread_rwlock_destroy.cpp +++ b/libc/src/pthread/pthread_rwlock_destroy.cpp @@ -10,7 +10,7 @@ #include "src/__support/common.h" #include "src/__support/macros/config.h" -#include "src/__support/threads/linux/rwlock.h" +#include "src/__support/threads/unix_rwlock.h" #include diff --git a/libc/src/pthread/pthread_rwlock_init.cpp b/libc/src/pthread/pthread_rwlock_init.cpp index dc5424cbc483..2c840665c99e 100644 --- a/libc/src/pthread/pthread_rwlock_init.cpp +++ b/libc/src/pthread/pthread_rwlock_init.cpp @@ -12,7 +12,7 @@ #include "src/__support/common.h" #include "src/__support/libc_assert.h" #include "src/__support/macros/config.h" -#include "src/__support/threads/linux/rwlock.h" +#include "src/__support/threads/unix_rwlock.h" #include diff --git a/libc/src/pthread/pthread_rwlock_rdlock.cpp b/libc/src/pthread/pthread_rwlock_rdlock.cpp index 7dee8eb9a44b..697a4a8e1765 100644 --- a/libc/src/pthread/pthread_rwlock_rdlock.cpp +++ b/libc/src/pthread/pthread_rwlock_rdlock.cpp @@ -10,7 +10,7 @@ #include "src/__support/common.h" #include "src/__support/macros/config.h" -#include "src/__support/threads/linux/rwlock.h" +#include "src/__support/threads/unix_rwlock.h" #include diff --git a/libc/src/pthread/pthread_rwlock_timedrdlock.cpp b/libc/src/pthread/pthread_rwlock_timedrdlock.cpp index 745da508cf14..6128f39fa359 100644 --- a/libc/src/pthread/pthread_rwlock_timedrdlock.cpp +++ b/libc/src/pthread/pthread_rwlock_timedrdlock.cpp @@ -12,7 +12,7 @@ #include "src/__support/libc_errno.h" #include "src/__support/macros/config.h" #include "src/__support/macros/optimization.h" -#include "src/__support/threads/linux/rwlock.h" +#include "src/__support/threads/unix_rwlock.h" #include "src/__support/time/abs_timeout.h" #include diff --git a/libc/src/pthread/pthread_rwlock_timedwrlock.cpp b/libc/src/pthread/pthread_rwlock_timedwrlock.cpp index 9666fc5b4728..848895e21b35 100644 --- a/libc/src/pthread/pthread_rwlock_timedwrlock.cpp +++ b/libc/src/pthread/pthread_rwlock_timedwrlock.cpp @@ -12,7 +12,7 @@ #include "src/__support/libc_assert.h" #include "src/__support/macros/config.h" #include "src/__support/macros/optimization.h" -#include "src/__support/threads/linux/rwlock.h" +#include "src/__support/threads/unix_rwlock.h" #include "src/__support/time/abs_timeout.h" #include diff --git a/libc/src/pthread/pthread_rwlock_tryrdlock.cpp b/libc/src/pthread/pthread_rwlock_tryrdlock.cpp index d54b57f05609..206ad9268832 100644 --- a/libc/src/pthread/pthread_rwlock_tryrdlock.cpp +++ b/libc/src/pthread/pthread_rwlock_tryrdlock.cpp @@ -10,7 +10,7 @@ #include "src/__support/common.h" #include "src/__support/macros/config.h" -#include "src/__support/threads/linux/rwlock.h" +#include "src/__support/threads/unix_rwlock.h" #include diff --git a/libc/src/pthread/pthread_rwlock_trywrlock.cpp b/libc/src/pthread/pthread_rwlock_trywrlock.cpp index 660c15a87b36..5278bb2a8cce 100644 --- a/libc/src/pthread/pthread_rwlock_trywrlock.cpp +++ b/libc/src/pthread/pthread_rwlock_trywrlock.cpp @@ -11,7 +11,7 @@ #include "src/__support/common.h" #include "src/__support/libc_errno.h" #include "src/__support/macros/config.h" -#include "src/__support/threads/linux/rwlock.h" +#include "src/__support/threads/unix_rwlock.h" #include diff --git a/libc/src/pthread/pthread_rwlock_unlock.cpp b/libc/src/pthread/pthread_rwlock_unlock.cpp index 5496bea929c5..73989b1fbfde 100644 --- a/libc/src/pthread/pthread_rwlock_unlock.cpp +++ b/libc/src/pthread/pthread_rwlock_unlock.cpp @@ -11,7 +11,7 @@ #include "src/__support/common.h" #include "src/__support/libc_errno.h" #include "src/__support/macros/config.h" -#include "src/__support/threads/linux/rwlock.h" +#include "src/__support/threads/unix_rwlock.h" #include diff --git a/libc/src/pthread/pthread_rwlock_wrlock.cpp b/libc/src/pthread/pthread_rwlock_wrlock.cpp index f02fb6b5db9c..1934b6d12c79 100644 --- a/libc/src/pthread/pthread_rwlock_wrlock.cpp +++ b/libc/src/pthread/pthread_rwlock_wrlock.cpp @@ -10,7 +10,7 @@ #include "src/__support/common.h" #include "src/__support/macros/config.h" -#include "src/__support/threads/linux/rwlock.h" +#include "src/__support/threads/unix_rwlock.h" #include diff --git a/libc/test/integration/src/pthread/CMakeLists.txt b/libc/test/integration/src/pthread/CMakeLists.txt index b7414f2bea12..62f6ed777e52 100644 --- a/libc/test/integration/src/pthread/CMakeLists.txt +++ b/libc/test/integration/src/pthread/CMakeLists.txt @@ -63,6 +63,7 @@ add_integration_test( libc.src.pthread.pthread_rwlockattr_setpshared libc.src.pthread.pthread_rwlockattr_setkind_np libc.src.__support.threads.raw_mutex + libc.src.__support.threads.raw_rwlock libc.src.stdio.printf libc.src.stdlib.getenv libc.src.sys.mman.mmap diff --git a/libc/test/integration/src/pthread/pthread_rwlock_test.cpp b/libc/test/integration/src/pthread/pthread_rwlock_test.cpp index dddfb282ce16..57d336d0704b 100644 --- a/libc/test/integration/src/pthread/pthread_rwlock_test.cpp +++ b/libc/test/integration/src/pthread/pthread_rwlock_test.cpp @@ -12,8 +12,8 @@ #include "src/__support/CPP/new.h" #include "src/__support/OSUtil/syscall.h" #include "src/__support/macros/config.h" -#include "src/__support/threads/linux/rwlock.h" #include "src/__support/threads/raw_mutex.h" +#include "src/__support/threads/raw_rwlock.h" #include "src/__support/threads/sleep.h" #include "src/pthread/pthread_create.h" #include "src/pthread/pthread_join.h" @@ -131,7 +131,8 @@ static void nullptr_test() { // counts. static void high_reader_count_test() { pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER; - rwlock.__state = LIBC_NAMESPACE::rwlock::RwLockTester::full_reader_state(); + rwlock.__raw.__state = + LIBC_NAMESPACE::rwlock::RwLockTester::full_reader_state(); ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_rdlock(&rwlock), EAGAIN); ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_tryrdlock(&rwlock), EAGAIN); // allocate 4 reader slots. @@ -374,20 +375,20 @@ static void randomized_thread_operation(SharedData *data, ThreadGuard &guard) { case Operation::READ: { LIBC_NAMESPACE::pthread_rwlock_rdlock(&data->lock); read_ops(); - LIBC_NAMESPACE::pthread_rwlock_unlock(&data->lock); + ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_unlock(&data->lock), 0); break; } case Operation::WRITE: { LIBC_NAMESPACE::pthread_rwlock_wrlock(&data->lock); write_ops(); - LIBC_NAMESPACE::pthread_rwlock_unlock(&data->lock); + ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_unlock(&data->lock), 0); break; } case Operation::TIMED_READ: { timespec ts = get_ts(); if (LIBC_NAMESPACE::pthread_rwlock_timedrdlock(&data->lock, &ts) == 0) { read_ops(); - LIBC_NAMESPACE::pthread_rwlock_unlock(&data->lock); + ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_unlock(&data->lock), 0); } break; } @@ -395,7 +396,7 @@ static void randomized_thread_operation(SharedData *data, ThreadGuard &guard) { timespec ts = get_ts(); if (LIBC_NAMESPACE::pthread_rwlock_timedwrlock(&data->lock, &ts) == 0) { write_ops(); - LIBC_NAMESPACE::pthread_rwlock_unlock(&data->lock); + ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_unlock(&data->lock), 0); } break; } @@ -404,7 +405,7 @@ static void randomized_thread_operation(SharedData *data, ThreadGuard &guard) { if (LIBC_NAMESPACE::pthread_rwlock_clockrdlock(&data->lock, CLOCK_MONOTONIC, &ts) == 0) { read_ops(); - LIBC_NAMESPACE::pthread_rwlock_unlock(&data->lock); + ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_unlock(&data->lock), 0); } break; } @@ -413,21 +414,21 @@ static void randomized_thread_operation(SharedData *data, ThreadGuard &guard) { if (LIBC_NAMESPACE::pthread_rwlock_clockwrlock(&data->lock, CLOCK_MONOTONIC, &ts) == 0) { write_ops(); - LIBC_NAMESPACE::pthread_rwlock_unlock(&data->lock); + ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_unlock(&data->lock), 0); } break; } case Operation::TRY_READ: { if (LIBC_NAMESPACE::pthread_rwlock_tryrdlock(&data->lock) == 0) { read_ops(); - LIBC_NAMESPACE::pthread_rwlock_unlock(&data->lock); + ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_unlock(&data->lock), 0); } break; } case Operation::TRY_WRITE: { if (LIBC_NAMESPACE::pthread_rwlock_trywrlock(&data->lock) == 0) { write_ops(); - LIBC_NAMESPACE::pthread_rwlock_unlock(&data->lock); + ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_unlock(&data->lock), 0); } break; } diff --git a/libc/test/src/__support/threads/linux/CMakeLists.txt b/libc/test/src/__support/threads/linux/CMakeLists.txt index a660e7ceb449..855676b1ca05 100644 --- a/libc/test/src/__support/threads/linux/CMakeLists.txt +++ b/libc/test/src/__support/threads/linux/CMakeLists.txt @@ -11,3 +11,13 @@ add_libc_test( libc.src.stdlib.exit libc.hdr.signal_macros ) + +add_libc_test( + raw_rwlock_test + SUITE + libc-support-threads-tests + SRCS + raw_rwlock_test.cpp + DEPENDS + libc.src.__support.threads.raw_rwlock +) diff --git a/libc/test/src/__support/threads/linux/raw_rwlock_test.cpp b/libc/test/src/__support/threads/linux/raw_rwlock_test.cpp new file mode 100644 index 000000000000..fa643f3df9fc --- /dev/null +++ b/libc/test/src/__support/threads/linux/raw_rwlock_test.cpp @@ -0,0 +1,47 @@ +//===-- Unittests for RawRwLock -------------------------------------------===// +// +// 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/time_macros.h" +#include "src/__support/threads/raw_rwlock.h" +#include "src/__support/time/clock_gettime.h" +#include "test/UnitTest/Test.h" + +TEST(LlvmLibcSupportThreadsRawRwLockTest, SmokeTest) { + LIBC_NAMESPACE::RawRwLock rwlock; + using LockResult = LIBC_NAMESPACE::RawRwLock::LockResult; + + ASSERT_EQ(rwlock.read_lock(), LockResult::Success); + ASSERT_EQ(rwlock.try_read_lock(), LockResult::Success); + ASSERT_EQ(rwlock.try_write_lock(), LockResult::Busy); + ASSERT_EQ(rwlock.unlock(), LockResult::Success); + ASSERT_EQ(rwlock.unlock(), LockResult::Success); + + ASSERT_EQ(rwlock.write_lock(), LockResult::Success); + ASSERT_EQ(rwlock.try_read_lock(), LockResult::Busy); + ASSERT_EQ(rwlock.try_write_lock(), LockResult::Busy); + ASSERT_EQ(rwlock.unlock(), LockResult::Success); + ASSERT_EQ(rwlock.unlock(), LockResult::PermissionDenied); +} + +TEST(LlvmLibcSupportThreadsRawRwLockTest, TimeoutWithoutDeadlockDetection) { + LIBC_NAMESPACE::RawRwLock rwlock; + using LockResult = LIBC_NAMESPACE::RawRwLock::LockResult; + + ASSERT_EQ(rwlock.write_lock(), LockResult::Success); + + timespec ts; + LIBC_NAMESPACE::internal::clock_gettime(CLOCK_MONOTONIC, &ts); + ts.tv_sec += 1; + auto timeout = LIBC_NAMESPACE::internal::AbsTimeout::from_timespec( + ts, /*is_realtime=*/false); + ASSERT_TRUE(timeout.has_value()); + + ASSERT_EQ(rwlock.write_lock(*timeout), LockResult::TimedOut); + ASSERT_EQ(rwlock.read_lock(*timeout), LockResult::TimedOut); + ASSERT_EQ(rwlock.unlock(), LockResult::Success); +}