[libc-commits] [libc] 41fecca - [libc] add rwlock (#94156)

via libc-commits libc-commits at lists.llvm.org
Fri Jun 14 13:34:33 PDT 2024


Author: Schrodinger ZHU Yifan
Date: 2024-06-14T13:34:28-07:00
New Revision: 41fecca97b77a80926cb1b1a83c8af3c90d354bd

URL: https://github.com/llvm/llvm-project/commit/41fecca97b77a80926cb1b1a83c8af3c90d354bd
DIFF: https://github.com/llvm/llvm-project/commit/41fecca97b77a80926cb1b1a83c8af3c90d354bd.diff

LOG: [libc] add rwlock (#94156)

Added: 
    libc/include/llvm-libc-types/pthread_rwlock_t.h
    libc/src/__support/threads/linux/rwlock.h
    libc/src/pthread/pthread_rwlock_destroy.cpp
    libc/src/pthread/pthread_rwlock_destroy.h
    libc/src/pthread/pthread_rwlock_init.cpp
    libc/src/pthread/pthread_rwlock_init.h
    libc/src/pthread/pthread_rwlock_rdlock.cpp
    libc/src/pthread/pthread_rwlock_rdlock.h
    libc/src/pthread/pthread_rwlock_timedrdlock.cpp
    libc/src/pthread/pthread_rwlock_timedrdlock.h
    libc/src/pthread/pthread_rwlock_timedwrlock.cpp
    libc/src/pthread/pthread_rwlock_timedwrlock.h
    libc/src/pthread/pthread_rwlock_tryrdlock.cpp
    libc/src/pthread/pthread_rwlock_tryrdlock.h
    libc/src/pthread/pthread_rwlock_trywrlock.cpp
    libc/src/pthread/pthread_rwlock_trywrlock.h
    libc/src/pthread/pthread_rwlock_unlock.cpp
    libc/src/pthread/pthread_rwlock_unlock.h
    libc/src/pthread/pthread_rwlock_wrlock.cpp
    libc/src/pthread/pthread_rwlock_wrlock.h
    libc/test/integration/src/pthread/pthread_rwlock_test.cpp

Modified: 
    libc/config/config.json
    libc/config/linux/aarch64/entrypoints.txt
    libc/config/linux/api.td
    libc/config/linux/x86_64/entrypoints.txt
    libc/docs/configure.rst
    libc/include/CMakeLists.txt
    libc/include/llvm-libc-types/CMakeLists.txt
    libc/include/pthread.h.def
    libc/spec/posix.td
    libc/spec/spec.td
    libc/src/__support/macros/attributes.h
    libc/src/__support/threads/linux/CMakeLists.txt
    libc/src/pthread/CMakeLists.txt
    libc/test/integration/src/pthread/CMakeLists.txt

Removed: 
    


################################################################################
diff  --git a/libc/config/config.json b/libc/config/config.json
index d3d1ff1e28716..8d6a84e732597 100644
--- a/libc/config/config.json
+++ b/libc/config/config.json
@@ -49,6 +49,10 @@
     "LIBC_CONF_RAW_MUTEX_DEFAULT_SPIN_COUNT": {
       "value": 100,
       "doc": "Default number of spins before blocking if a mutex is in contention (default to 100)."
+    },
+    "LIBC_CONF_RWLOCK_DEFAULT_SPIN_COUNT": {
+      "value": 100,
+      "doc": "Default number of spins before blocking if a rwlock is in contention (default to 100)."
     }
   }
 }

diff  --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt
index f2f9803523df2..905f7e4871384 100644
--- a/libc/config/linux/aarch64/entrypoints.txt
+++ b/libc/config/linux/aarch64/entrypoints.txt
@@ -645,6 +645,15 @@ if(LLVM_LIBC_FULL_BUILD)
     libc.src.pthread.pthread_mutexattr_setrobust
     libc.src.pthread.pthread_mutexattr_settype
     libc.src.pthread.pthread_once
+    libc.src.pthread.pthread_rwlock_init
+    libc.src.pthread.pthread_rwlock_tryrdlock
+    libc.src.pthread.pthread_rwlock_rdlock
+    libc.src.pthread.pthread_rwlock_timedrdlock
+    libc.src.pthread.pthread_rwlock_trywrlock
+    libc.src.pthread.pthread_rwlock_wrlock
+    libc.src.pthread.pthread_rwlock_timedwrlock
+    libc.src.pthread.pthread_rwlock_unlock
+    libc.src.pthread.pthread_rwlock_destroy
     libc.src.pthread.pthread_rwlockattr_destroy
     libc.src.pthread.pthread_rwlockattr_getkind_np
     libc.src.pthread.pthread_rwlockattr_getpshared

diff  --git a/libc/config/linux/api.td b/libc/config/linux/api.td
index 902839b3e5b8f..eb0090c80b0da 100644
--- a/libc/config/linux/api.td
+++ b/libc/config/linux/api.td
@@ -181,6 +181,7 @@ def PThreadAPI : PublicAPI<"pthread.h"> {
       "pthread_mutexattr_t",
       "pthread_once_t",
       "pthread_rwlockattr_t",
+      "pthread_rwlock_t",
       "pthread_t",
   ];
 }
@@ -270,6 +271,7 @@ def SysTypesAPI : PublicAPI<"sys/types.h"> {
     "pthread_mutexattr_t",
     "pthread_once_t",
     "pthread_rwlockattr_t",
+    "pthread_rwlock_t",
     "pthread_t",
     "size_t",
     "ssize_t",

diff  --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index 45914fe9f7ad2..a5a634f61b7e7 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -726,6 +726,15 @@ if(LLVM_LIBC_FULL_BUILD)
     libc.src.pthread.pthread_mutexattr_setrobust
     libc.src.pthread.pthread_mutexattr_settype
     libc.src.pthread.pthread_once
+    libc.src.pthread.pthread_rwlock_init
+    libc.src.pthread.pthread_rwlock_tryrdlock
+    libc.src.pthread.pthread_rwlock_rdlock
+    libc.src.pthread.pthread_rwlock_timedrdlock
+    libc.src.pthread.pthread_rwlock_trywrlock
+    libc.src.pthread.pthread_rwlock_wrlock
+    libc.src.pthread.pthread_rwlock_timedwrlock
+    libc.src.pthread.pthread_rwlock_unlock
+    libc.src.pthread.pthread_rwlock_destroy
     libc.src.pthread.pthread_rwlockattr_destroy
     libc.src.pthread.pthread_rwlockattr_getkind_np
     libc.src.pthread.pthread_rwlockattr_getpshared

diff  --git a/libc/docs/configure.rst b/libc/docs/configure.rst
index 77ade07714fdf..bdae6c54052f2 100644
--- a/libc/docs/configure.rst
+++ b/libc/docs/configure.rst
@@ -36,6 +36,7 @@ to learn about the defaults for your platform and target.
     - ``LIBC_CONF_PRINTF_FLOAT_TO_STR_USE_MEGA_LONG_DOUBLE_TABLE``: Use large table for better printf long double performance.
 * **"pthread" options**
     - ``LIBC_CONF_RAW_MUTEX_DEFAULT_SPIN_COUNT``: Default number of spins before blocking if a mutex is in contention (default to 100).
+    - ``LIBC_CONF_RWLOCK_DEFAULT_SPIN_COUNT``: Default number of spins before blocking if a rwlock is in contention (default to 100).
     - ``LIBC_CONF_TIMEOUT_ENSURE_MONOTONICITY``: Automatically adjust timeout to CLOCK_MONOTONIC (default to true). POSIX API may require CLOCK_REALTIME, which can be unstable and leading to unexpected behavior. This option will convert the real-time timestamp to monotonic timestamp relative to the time of call.
 * **"string" options**
     - ``LIBC_CONF_MEMSET_X86_USE_SOFTWARE_PREFETCHING``: Inserts prefetch for write instructions (PREFETCHW) for memset on x86 to recover performance when hardware prefetcher is disabled.

diff  --git a/libc/include/CMakeLists.txt b/libc/include/CMakeLists.txt
index 2a41ec46abdab..bb10fd4c94703 100644
--- a/libc/include/CMakeLists.txt
+++ b/libc/include/CMakeLists.txt
@@ -332,6 +332,7 @@ add_gen_header(
     .llvm-libc-types.pthread_mutex_t
     .llvm-libc-types.pthread_mutexattr_t
     .llvm-libc-types.pthread_once_t
+    .llvm-libc-types.pthread_rwlock_t
     .llvm-libc-types.pthread_rwlockattr_t
     .llvm-libc-types.pthread_t
 )

diff  --git a/libc/include/llvm-libc-types/CMakeLists.txt b/libc/include/llvm-libc-types/CMakeLists.txt
index 356ac037770d2..d8b975572e0dd 100644
--- a/libc/include/llvm-libc-types/CMakeLists.txt
+++ b/libc/include/llvm-libc-types/CMakeLists.txt
@@ -54,6 +54,7 @@ add_header(pthread_key_t HDR pthread_key_t.h)
 add_header(pthread_mutex_t HDR pthread_mutex_t.h DEPENDS .__futex_word .__mutex_type)
 add_header(pthread_mutexattr_t HDR pthread_mutexattr_t.h)
 add_header(pthread_once_t HDR pthread_once_t.h DEPENDS .__futex_word)
+add_header(pthread_rwlock_t HDR pthread_rwlock_t.h DEPENDS .__futex_word .pid_t)
 add_header(pthread_rwlockattr_t HDR pthread_rwlockattr_t.h)
 add_header(pthread_t HDR pthread_t.h DEPENDS .__thread_type)
 add_header(rlim_t HDR rlim_t.h)

diff  --git a/libc/include/llvm-libc-types/pthread_rwlock_t.h b/libc/include/llvm-libc-types/pthread_rwlock_t.h
new file mode 100644
index 0000000000000..da49a155a2bdb
--- /dev/null
+++ b/libc/include/llvm-libc-types/pthread_rwlock_t.h
@@ -0,0 +1,26 @@
+//===-- Definition of pthread_mutex_t type --------------------------------===//
+//
+// 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_TYPES_PTHREAD_RWLOCK_T_H
+#define LLVM_LIBC_TYPES_PTHREAD_RWLOCK_T_H
+
+#include "llvm-libc-types/__futex_word.h"
+#include "llvm-libc-types/pid_t.h"
+typedef struct {
+  unsigned __is_pshared : 1;
+  unsigned __preference : 1;
+  int __state;
+  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/include/pthread.h.def b/libc/include/pthread.h.def
index d41273b5590ea..4dbeed6b5f321 100644
--- a/libc/include/pthread.h.def
+++ b/libc/include/pthread.h.def
@@ -17,6 +17,7 @@
 #define PTHREAD_STACK_MIN (1 << 14) // 16KB
 
 #define PTHREAD_MUTEX_INITIALIZER {0}
+#define PTHREAD_RWLOCK_INITIALIZER {}
 #define PTHREAD_ONCE_INIT {0}
 
 enum {

diff  --git a/libc/spec/posix.td b/libc/spec/posix.td
index e16353b8142de..40b9fa09dd61c 100644
--- a/libc/spec/posix.td
+++ b/libc/spec/posix.td
@@ -113,6 +113,8 @@ def POSIX : StandardSpec<"POSIX"> {
   NamedType PThreadRWLockAttrTType = NamedType<"pthread_rwlockattr_t">;
   PtrType PThreadRWLockAttrTPtr = PtrType<PThreadRWLockAttrTType>;
   ConstType ConstPThreadRWLockAttrTPtr = ConstType<PThreadRWLockAttrTPtr>;
+  RestrictedPtrType RestrictedPThreadRWLockAttrTPtr = RestrictedPtrType<PThreadRWLockAttrTType>;
+  ConstType ConstRestrictedPThreadRWLockAttrTPtr = ConstType<RestrictedPThreadRWLockAttrTPtr>;
 
   NamedType PThreadMutexAttrTType = NamedType<"pthread_mutexattr_t">;
   PtrType PThreadMutexAttrTPtr = PtrType<PThreadMutexAttrTType>;
@@ -126,6 +128,10 @@ def POSIX : StandardSpec<"POSIX"> {
   ConstType ConstPThreadMutexTPtr = ConstType<PThreadMutexTPtr>;
   ConstType ConstRestrictedPThreadMutexTPtr = ConstType<RestrictedPThreadMutexTPtr>;
 
+  NamedType PThreadRWLockTType = NamedType<"pthread_rwlock_t">;
+  PtrType PThreadRWLockTPtr = PtrType<PThreadRWLockTType>;
+  RestrictedPtrType RestrictedPThreadRWLockTPtr = RestrictedPtrType<PThreadRWLockTType>;
+
   PtrType PThreadTPtr = PtrType<PThreadTType>;
   RestrictedPtrType RestrictedPThreadTPtr = RestrictedPtrType<PThreadTType>;
 
@@ -1003,6 +1009,7 @@ def POSIX : StandardSpec<"POSIX"> {
         PThreadOnceCallback,
         PThreadOnceT,
         PThreadRWLockAttrTType,
+        PThreadRWLockTType,
         PThreadStartT,
         PThreadTSSDtorT,
         PThreadTType,
@@ -1259,6 +1266,51 @@ def POSIX : StandardSpec<"POSIX"> {
           RetValSpec<IntType>,
           [ArgSpec<PThreadRWLockAttrTPtr>, ArgSpec<IntType>]
       >,
+      FunctionSpec<
+        "pthread_rwlock_init",
+        RetValSpec<IntType>,
+        [ArgSpec<PThreadRWLockTPtr>, ArgSpec<ConstRestrictedPThreadRWLockAttrTPtr>]
+      >,
+      FunctionSpec<
+        "pthread_rwlock_tryrdlock",
+        RetValSpec<IntType>,
+        [ArgSpec<PThreadRWLockTPtr>]
+      >,
+      FunctionSpec<
+        "pthread_rwlock_trywrlock",
+        RetValSpec<IntType>,
+        [ArgSpec<PThreadRWLockTPtr>]
+      >,
+      FunctionSpec<
+        "pthread_rwlock_timedrdlock",
+        RetValSpec<IntType>,
+        [ArgSpec<RestrictedPThreadRWLockTPtr>, ArgSpec<ConstRestrictStructTimeSpecPtr>]
+      >,
+      FunctionSpec<
+        "pthread_rwlock_timedwrlock",
+        RetValSpec<IntType>,
+        [ArgSpec<RestrictedPThreadRWLockTPtr>, ArgSpec<ConstRestrictStructTimeSpecPtr>]
+      >,
+      FunctionSpec<
+        "pthread_rwlock_rdlock",
+        RetValSpec<IntType>,
+        [ArgSpec<PThreadRWLockTPtr>]
+      >,
+      FunctionSpec<
+        "pthread_rwlock_wrlock",
+        RetValSpec<IntType>,
+        [ArgSpec<PThreadRWLockTPtr>]
+      >,
+      FunctionSpec<
+        "pthread_rwlock_unlock",
+        RetValSpec<IntType>,
+        [ArgSpec<PThreadRWLockTPtr>]
+      >,
+      FunctionSpec<
+        "pthread_rwlock_destroy",
+        RetValSpec<IntType>,
+        [ArgSpec<PThreadRWLockTPtr>]
+      >,
     ]
   >;
 
@@ -1616,6 +1668,7 @@ def POSIX : StandardSpec<"POSIX"> {
       PThreadMutexTType,
       PThreadOnceT,
       PThreadRWLockAttrTType,
+      PThreadRWLockTType,
       PThreadTType,
       PidT,
       SSizeTType,

diff  --git a/libc/spec/spec.td b/libc/spec/spec.td
index 7e1283e67fab2..a3a5db7465b39 100644
--- a/libc/spec/spec.td
+++ b/libc/spec/spec.td
@@ -126,6 +126,8 @@ def TimeTType : NamedType<"time_t">;
 def StructTimeSpec : NamedType<"struct timespec">;
 def StructTimeSpecPtr : PtrType<StructTimeSpec>;
 def ConstStructTimeSpecPtr : ConstType<StructTimeSpecPtr>;
+def RestrictStructTimeSpecPtr : RestrictedPtrType<StructTimeSpec>;
+def ConstRestrictStructTimeSpecPtr : ConstType<RestrictStructTimeSpecPtr>;
 
 def BSearchCompareT : NamedType<"__bsearchcompare_t">;
 def QSortCompareT : NamedType<"__qsortcompare_t">;

diff  --git a/libc/src/__support/macros/attributes.h b/libc/src/__support/macros/attributes.h
index c489a288fbb11..7e8e2ddfac9b1 100644
--- a/libc/src/__support/macros/attributes.h
+++ b/libc/src/__support/macros/attributes.h
@@ -42,4 +42,10 @@
 #define LIBC_CONSTINIT
 #endif
 
+#ifdef __clang__
+#define LIBC_PREFERED_TYPE(TYPE) [[clang::preferred_type(TYPE)]]
+#else
+#define LIBC_PREFERED_TYPE(TYPE)
+#endif
+
 #endif // LLVM_LIBC_SRC___SUPPORT_MACROS_ATTRIBUTES_H

diff  --git a/libc/src/__support/threads/linux/CMakeLists.txt b/libc/src/__support/threads/linux/CMakeLists.txt
index 8e6cd7227b2c8..95e509b7a825d 100644
--- a/libc/src/__support/threads/linux/CMakeLists.txt
+++ b/libc/src/__support/threads/linux/CMakeLists.txt
@@ -22,11 +22,11 @@ add_header_library(
     libc.src.__support.time.linux.abs_timeout
 )
 
-set(raw_mutex_additional_flags)
+set(monotonicity_flags)
 if (LIBC_CONF_TIMEOUT_ENSURE_MONOTONICITY)
-  set(raw_mutex_additional_flags -DLIBC_COPT_TIMEOUT_ENSURE_MONOTONICITY=1)
+  set(monotonicity_flags -DLIBC_COPT_TIMEOUT_ENSURE_MONOTONICITY=1)
 else()
-  set(raw_mutex_additional_flags -DLIBC_COPT_TIMEOUT_ENSURE_MONOTONICITY=0)
+  set(monotonicity_flags -DLIBC_COPT_TIMEOUT_ENSURE_MONOTONICITY=0)
 endif()
 
 add_header_library(
@@ -42,8 +42,22 @@ add_header_library(
     libc.hdr.types.pid_t
   COMPILE_OPTIONS
     -DLIBC_COPT_RAW_MUTEX_DEFAULT_SPIN_COUNT=${LIBC_CONF_RAW_MUTEX_DEFAULT_SPIN_COUNT}
-    ${raw_mutex_additional_flags}
-  
+    ${monotonicity_flags}
+)
+
+add_header_library(
+  rwlock
+  HDRS
+    rwlock.h
+  DEPENDS
+    .futex_utils
+    .raw_mutex
+    libc.src.__support.common
+    libc.src.__support.OSUtil.osutil
+    libc.src.__support.CPP.limits
+  COMPILE_OPTIONS
+    -DLIBC_COPT_RWLOCK_DEFAULT_SPIN_COUNT=${LIBC_CONF_RWLOCK_DEFAULT_SPIN_COUNT}
+    ${monotonicity_flags}
 )
 
 add_header_library(

diff  --git a/libc/src/__support/threads/linux/rwlock.h b/libc/src/__support/threads/linux/rwlock.h
new file mode 100644
index 0000000000000..5db0590fd6aa0
--- /dev/null
+++ b/libc/src/__support/threads/linux/rwlock.h
@@ -0,0 +1,556 @@
+//===--- Implementation of a Linux 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_LINUX_RWLOCK_H
+#define LLVM_LIBC_SRC_SUPPORT_THREADS_LINUX_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/optimization.h"
+#include "src/__support/threads/linux/futex_utils.h"
+#include "src/__support/threads/linux/futex_word.h"
+#include "src/__support/threads/linux/raw_mutex.h"
+#include "src/__support/threads/sleep.h"
+
+#ifndef LIBC_COPT_RWLOCK_DEFAULT_SPIN_COUNT
+#define LIBC_COPT_RWLOCK_DEFAULT_SPIN_COUNT 100
+#endif
+
+#ifndef LIBC_COPT_TIMEOUT_ENSURE_MONOTONICITY
+#define LIBC_COPT_TIMEOUT_ENSURE_MONOTONICITY 1
+#warning "LIBC_COPT_TIMEOUT_ENSURE_MONOTONICITY is not defined, defaulting to 1"
+#endif
+
+#if LIBC_COPT_TIMEOUT_ENSURE_MONOTONICITY
+#include "src/__support/time/linux/monotonicity.h"
+#endif
+
+namespace LIBC_NAMESPACE {
+// 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 };
+
+// A waiting queue to keep track of the pending readers and writers.
+class WaitingQueue final : private RawMutex {
+  /* FutexWordType raw_mutex;  (from base class) */
+
+  // Pending reader count (protected by the mutex)
+  FutexWordType pending_readers;
+  // Pending writer count (protected by the mutex)
+  FutexWordType pending_writers;
+  // Reader serialization (increases on each reader-waking operation)
+  Futex reader_serialization;
+  // Writer serialization (increases on each writer-waking operation)
+  Futex writer_serialization;
+
+public:
+  // RAII guard to lock and unlock the waiting queue.
+  class Guard {
+    WaitingQueue &queue;
+    bool is_pshared;
+
+    LIBC_INLINE constexpr Guard(WaitingQueue &queue, bool is_pshared)
+        : queue(queue), is_pshared(is_pshared) {
+      queue.lock(cpp::nullopt, is_pshared);
+    }
+
+  public:
+    LIBC_INLINE ~Guard() { queue.unlock(is_pshared); }
+    template <Role role> LIBC_INLINE FutexWordType &pending_count() {
+      if constexpr (role == Role::Reader)
+        return queue.pending_readers;
+      else
+        return queue.pending_writers;
+    }
+    template <Role role> LIBC_INLINE FutexWordType &serialization() {
+      if constexpr (role == Role::Reader)
+        return queue.reader_serialization.val;
+      else
+        return queue.writer_serialization.val;
+    }
+    friend WaitingQueue;
+  };
+
+public:
+  LIBC_INLINE constexpr WaitingQueue()
+      : RawMutex(), pending_readers(0), pending_writers(0),
+        reader_serialization(0), writer_serialization(0) {}
+
+  LIBC_INLINE Guard acquire(bool is_pshared) {
+    return Guard(*this, is_pshared);
+  }
+
+  template <Role role>
+  LIBC_INLINE long wait(FutexWordType expected,
+                        cpp::optional<Futex::Timeout> timeout,
+                        bool is_pshared) {
+    if constexpr (role == Role::Reader)
+      return reader_serialization.wait(expected, timeout, is_pshared);
+    else
+      return writer_serialization.wait(expected, timeout, is_pshared);
+  }
+
+  template <Role role> LIBC_INLINE long notify(bool is_pshared) {
+    if constexpr (role == Role::Reader)
+      return reader_serialization.notify_all(is_pshared);
+    else
+      return writer_serialization.notify_one(is_pshared);
+  }
+};
+
+// The RwState of the RwLock is stored in an integer word, consisting of the
+// following components:
+// -----------------------------------------------
+// | Range    |           Description            |
+// ===============================================
+// | 0        | Pending Reader Bit               |
+// -----------------------------------------------
+// | 1        | Pending Writer Bit               |
+// -----------------------------------------------
+// | [2, MSB) | Active Reader Count              |
+// -----------------------------------------------
+// | MSB      | Active Writer Bit                |
+// -----------------------------------------------
+class RwState {
+  // Shift amounts to access the components of the state.
+  LIBC_INLINE_VAR static constexpr int PENDING_READER_SHIFT = 0;
+  LIBC_INLINE_VAR static constexpr int PENDING_WRITER_SHIFT = 1;
+  LIBC_INLINE_VAR static constexpr int ACTIVE_READER_SHIFT = 2;
+  LIBC_INLINE_VAR static constexpr int ACTIVE_WRITER_SHIFT =
+      cpp::numeric_limits<int>::digits;
+
+  // Bitmasks to access the components of the state.
+  LIBC_INLINE_VAR static constexpr int PENDING_READER_BIT =
+      1 << PENDING_READER_SHIFT;
+  LIBC_INLINE_VAR static constexpr int PENDING_WRITER_BIT =
+      1 << PENDING_WRITER_SHIFT;
+  LIBC_INLINE_VAR static constexpr int ACTIVE_READER_COUNT_UNIT =
+      1 << ACTIVE_READER_SHIFT;
+  LIBC_INLINE_VAR static constexpr int ACTIVE_WRITER_BIT =
+      1 << ACTIVE_WRITER_SHIFT;
+  LIBC_INLINE_VAR static constexpr int PENDING_MASK =
+      PENDING_READER_BIT | PENDING_WRITER_BIT;
+
+private:
+  // We use the signed integer as the state type. It is easier
+  // to reason about the state transitions using signness.
+  int state;
+
+public:
+  // Construction and conversion functions.
+  LIBC_INLINE constexpr RwState(int state = 0) : state(state) {}
+  LIBC_INLINE constexpr operator int() const { return state; }
+
+  // Utilities to check the state of the RwLock.
+  LIBC_INLINE constexpr bool has_active_writer() const { return state < 0; }
+  LIBC_INLINE constexpr bool has_active_reader() const {
+    return state >= ACTIVE_READER_COUNT_UNIT;
+  }
+  LIBC_INLINE constexpr bool has_acitve_owner() const {
+    return has_active_reader() || has_active_writer();
+  }
+  LIBC_INLINE constexpr bool has_last_reader() const {
+    return (state >> ACTIVE_READER_SHIFT) == 1;
+  }
+  LIBC_INLINE constexpr bool has_pending_writer() const {
+    return state & PENDING_WRITER_BIT;
+  }
+  LIBC_INLINE constexpr bool has_pending() const {
+    return state & PENDING_MASK;
+  }
+
+  LIBC_INLINE constexpr RwState set_writer_bit() const {
+    return RwState(state | ACTIVE_WRITER_BIT);
+  }
+
+  // The preference parameter changes the behavior of the lock acquisition
+  // if there are both readers and writers waiting for the lock. If writers
+  // are preferred, reader acquisition will be blocked until all pending
+  // writers are served.
+  template <Role role> LIBC_INLINE bool can_acquire(Role preference) const {
+    if constexpr (role == Role::Reader) {
+      switch (preference) {
+      case Role::Reader:
+        return !has_active_writer();
+      case Role::Writer:
+        return !has_active_writer() && !has_pending_writer();
+      }
+    } else
+      return !has_acitve_owner();
+  }
+
+  // This function check if it is possible to grow the reader count without
+  // overflowing the state.
+  LIBC_INLINE cpp::optional<RwState> try_increase_reader_count() const {
+    LIBC_ASSERT(!has_active_writer() &&
+                "try_increase_reader_count shall only be called when there "
+                "is no active writer.");
+    RwState res;
+    if (LIBC_UNLIKELY(__builtin_sadd_overflow(state, ACTIVE_READER_COUNT_UNIT,
+                                              &res.state)))
+      return cpp::nullopt;
+    return res;
+  }
+
+  // Utilities to do atomic operations on the state.
+  LIBC_INLINE static RwState fetch_sub_reader_count(cpp::Atomic<int> &target,
+                                                    cpp::MemoryOrder order) {
+    return RwState(target.fetch_sub(ACTIVE_READER_COUNT_UNIT, order));
+  }
+
+  LIBC_INLINE static RwState load(cpp::Atomic<int> &target,
+                                  cpp::MemoryOrder order) {
+    return RwState(target.load(order));
+  }
+
+  template <Role role>
+  LIBC_INLINE static RwState fetch_set_pending_bit(cpp::Atomic<int> &target,
+                                                   cpp::MemoryOrder order) {
+    if constexpr (role == Role::Reader)
+      return RwState(target.fetch_or(PENDING_READER_BIT, order));
+    else
+      return RwState(target.fetch_or(PENDING_WRITER_BIT, order));
+  }
+  template <Role role>
+  LIBC_INLINE static RwState fetch_clear_pending_bit(cpp::Atomic<int> &target,
+                                                     cpp::MemoryOrder order) {
+    if constexpr (role == Role::Reader)
+      return RwState(target.fetch_and(~PENDING_READER_BIT, order));
+    else
+      return RwState(target.fetch_and(~PENDING_WRITER_BIT, order));
+  }
+
+  LIBC_INLINE static RwState fetch_clear_active_writer(cpp::Atomic<int> &target,
+                                                       cpp::MemoryOrder order) {
+    return RwState(target.fetch_and(~ACTIVE_WRITER_BIT, order));
+  }
+
+  LIBC_INLINE bool compare_exchange_weak_with(cpp::Atomic<int> &target,
+                                              RwState desired,
+                                              cpp::MemoryOrder success_order,
+                                              cpp::MemoryOrder failure_order) {
+    return target.compare_exchange_weak(state, desired, success_order,
+                                        failure_order);
+  }
+
+  // Utilities to spin and reload the state.
+private:
+  template <class F>
+  LIBC_INLINE static RwState spin_reload_until(cpp::Atomic<int> &target,
+                                               F &&func, unsigned spin_count) {
+    for (;;) {
+      auto state = RwState::load(target, cpp::MemoryOrder::RELAXED);
+      if (func(state) || spin_count == 0)
+        return state;
+      sleep_briefly();
+      spin_count--;
+    }
+  }
+
+public:
+  template <Role role>
+  LIBC_INLINE static RwState spin_reload(cpp::Atomic<int> &target,
+                                         Role preference, unsigned spin_count) {
+    if constexpr (role == Role::Reader) {
+      // Return the reader state if either the lock is available or there is
+      // any ongoing contention.
+      return spin_reload_until(
+          target,
+          [=](RwState state) {
+            return state.can_acquire<Role::Reader>(preference) ||
+                   state.has_pending();
+          },
+          spin_count);
+    } else {
+      // Return the writer state if either the lock is available or there is
+      // any contention *between writers*. Since writers can be way less than
+      // readers, we allow them to spin more to improve the fairness.
+      return spin_reload_until(
+          target,
+          [=](RwState state) {
+            return state.can_acquire<Role::Writer>(preference) ||
+                   state.has_pending_writer();
+          },
+          spin_count);
+    }
+  }
+
+  friend class RwLockTester;
+};
+} // namespace rwlock
+
+class RwLock {
+  using RwState = rwlock::RwState;
+  using Role = rwlock::Role;
+  using WaitingQueue = rwlock::WaitingQueue;
+
+public:
+  // Return types for the lock functions.
+  // All the locking routines returning this type are marked as [[nodiscard]]
+  // 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,
+  };
+
+private:
+  // Whether the RwLock is shared between processes.
+  LIBC_PREFERED_TYPE(bool)
+  unsigned is_pshared : 1;
+  // Reader/Writer preference.
+  LIBC_PREFERED_TYPE(Role)
+  unsigned preference : 1;
+  // RwState to keep track of the RwLock.
+  cpp::Atomic<int> 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<pid_t> writer_tid;
+  // Waiting queue to keep track of the  readers and writers.
+  WaitingQueue queue;
+
+private:
+  // Load the bitfield preference.
+  LIBC_INLINE Role get_preference() const {
+    return static_cast<Role>(preference);
+  }
+  // TODO: use cached thread id once implemented.
+  LIBC_INLINE static pid_t gettid() { return syscall_impl<pid_t>(SYS_gettid); }
+
+  template <Role role> LIBC_INLINE LockResult try_lock(RwState &old) {
+    if constexpr (role == Role::Reader) {
+      while (LIBC_LIKELY(old.can_acquire<Role::Reader>(get_preference()))) {
+        cpp::optional<RwState> next = old.try_increase_reader_count();
+        if (!next)
+          return LockResult::Overflow;
+        if (LIBC_LIKELY(old.compare_exchange_weak_with(
+                state, *next, cpp::MemoryOrder::ACQUIRE,
+                cpp::MemoryOrder::RELAXED)))
+          return LockResult::Success;
+        // Notice that old is updated by the compare_exchange_weak_with
+        // function.
+      }
+      return LockResult::Busy;
+    } else {
+      // This while loop should terminate quickly
+      while (LIBC_LIKELY(old.can_acquire<Role::Writer>(get_preference()))) {
+        if (LIBC_LIKELY(old.compare_exchange_weak_with(
+                state, old.set_writer_bit(), cpp::MemoryOrder::ACQUIRE,
+                cpp::MemoryOrder::RELAXED))) {
+          writer_tid.store(gettid(), cpp::MemoryOrder::RELAXED);
+          return LockResult::Success;
+        }
+        // Notice that old is updated by the compare_exchange_weak_with
+        // function.
+      }
+      return LockResult::Busy;
+    }
+  }
+
+public:
+  LIBC_INLINE constexpr RwLock(Role preference = Role::Reader,
+                               bool is_pshared = false)
+      : is_pshared(is_pshared), preference(static_cast<unsigned>(preference)),
+        state(0), writer_tid(0), queue() {}
+
+  [[nodiscard]]
+  LIBC_INLINE LockResult try_read_lock() {
+    RwState old = RwState::load(state, cpp::MemoryOrder::RELAXED);
+    return try_lock<Role::Reader>(old);
+  }
+  [[nodiscard]]
+  LIBC_INLINE LockResult try_write_lock() {
+    RwState old = RwState::load(state, cpp::MemoryOrder::RELAXED);
+    return try_lock<Role::Writer>(old);
+  }
+
+private:
+  template <Role role>
+  LIBC_INLINE LockResult
+  lock_slow(cpp::optional<Futex::Timeout> 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) == gettid())
+      return LockResult::Deadlock;
+
+#if LIBC_COPT_TIMEOUT_ENSURE_MONOTONICITY
+    // Phase 2: convert the timeout if necessary.
+    if (timeout)
+      ensure_monotonicity(*timeout);
+#endif
+
+    // Phase 3: spin to get the initial state. We ignore the timing due to
+    // spin since it should end quickly.
+    RwState old =
+        RwState::spin_reload<role>(state, get_preference(), spin_count);
+
+    // Enter the main acquisition loop.
+    for (;;) {
+      // Phase 4: if the lock can be acquired, try to acquire it.
+      LockResult result = try_lock<role>(old);
+      if (result != LockResult::Busy)
+        return result;
+
+      // Phase 5: register ourselves as a  reader.
+      int serial_number;
+      {
+        // The queue need to be protected by a mutex since the operations in
+        // this block must be executed as a whole transaction. It is possible
+        // that this lock will make the timeout imprecise, but this is the
+        // best we can do. The transaction is small and everyone should make
+        // progress rather quickly.
+        WaitingQueue::Guard guard = queue.acquire(is_pshared);
+        guard.template pending_count<role>()++;
+
+        // Use atomic operation to guarantee the total order of the operations
+        // on the state. The pending flag update should be visible to any
+        // succeeding unlock events. Or, if a unlock does happen before we
+        // sleep on the futex, we can avoid such waiting.
+        old = RwState::fetch_set_pending_bit<role>(state,
+                                                   cpp::MemoryOrder::RELAXED);
+        // no need to use atomic since it is already protected by the mutex.
+        serial_number = guard.serialization<role>();
+      }
+
+      // Phase 6: do futex wait until the lock is available or timeout is
+      // reached.
+      bool timeout_flag = false;
+      if (!old.can_acquire<role>(get_preference()))
+        timeout_flag = (queue.wait<role>(serial_number, timeout, is_pshared) ==
+                        -ETIMEDOUT);
+
+      // Phase 7: unregister ourselves as a pending reader/writer.
+      {
+        // Similarly, the unregister operation should also be an atomic
+        // transaction.
+        WaitingQueue::Guard guard = queue.acquire(is_pshared);
+        guard.pending_count<role>()--;
+        // Clear the flag if we are the last reader. The flag must be
+        // cleared otherwise operations like trylock may fail even though
+        // there is no competitors.
+        if (guard.pending_count<role>() == 0)
+          RwState::fetch_clear_pending_bit<role>(state,
+                                                 cpp::MemoryOrder::RELAXED);
+      }
+
+      // Phase 8: exit the loop is timeout is reached.
+      if (timeout_flag)
+        return LockResult::TimedOut;
+
+      // Phase 9: reload the state and retry the acquisition.
+      old = RwState::spin_reload<role>(state, get_preference(), spin_count);
+    }
+  }
+
+public:
+  [[nodiscard]]
+  LIBC_INLINE LockResult
+  read_lock(cpp::optional<Futex::Timeout> 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<Role::Reader>(timeout, spin_count);
+  }
+  [[nodiscard]]
+  LIBC_INLINE LockResult
+  write_lock(cpp::optional<Futex::Timeout> 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<Role::Writer>(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.
+  [[gnu::noinline]]
+  LIBC_INLINE void notify_pending_threads() {
+    enum class WakeTarget { Readers, Writers, None };
+    WakeTarget status;
+
+    {
+      WaitingQueue::Guard guard = queue.acquire(is_pshared);
+      if (guard.pending_count<Role::Writer>() != 0) {
+        guard.serialization<Role::Writer>()++;
+        status = WakeTarget::Writers;
+      } else if (guard.pending_count<Role::Reader>() != 0) {
+        guard.serialization<Role::Reader>()++;
+        status = WakeTarget::Readers;
+      } else
+        status = WakeTarget::None;
+    }
+
+    if (status == WakeTarget::Readers)
+      queue.notify<Role::Reader>(is_pshared);
+    else if (status == WakeTarget::Writers)
+      queue.notify<Role::Writer>(is_pshared);
+  }
+
+public:
+  [[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) != 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);
+      // If there is no pending readers or writers, we are done.
+      if (!old.has_pending())
+        return LockResult::Success;
+    } else if (old.has_active_reader()) {
+      // The lock is held by readers.
+      // Decrease the reader count.
+      old = RwState::fetch_sub_reader_count(state, cpp::MemoryOrder::RELEASE);
+      // If there is no pending readers or writers, we are done.
+      if (!old.has_last_reader() || !old.has_pending())
+        return LockResult::Success;
+    } 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() {
+    RwState old = RwState::load(state, cpp::MemoryOrder::RELAXED);
+    if (old.has_acitve_owner())
+      return LockResult::Busy;
+    return LockResult::Success;
+  }
+};
+} // namespace LIBC_NAMESPACE
+
+#endif // LLVM_LIBC_SRC_SUPPORT_THREADS_LINUX_RWLOCK_H

diff  --git a/libc/src/pthread/CMakeLists.txt b/libc/src/pthread/CMakeLists.txt
index e5bebb63c6401..dc748b22e0378 100644
--- a/libc/src/pthread/CMakeLists.txt
+++ b/libc/src/pthread/CMakeLists.txt
@@ -522,6 +522,106 @@ add_entrypoint_object(
     libc.include.errno
 )
 
+add_entrypoint_object(
+  pthread_rwlock_init
+  SRCS
+    pthread_rwlock_init.cpp
+  HDRS
+    pthread_rwlock_init.h
+  DEPENDS
+    libc.include.pthread
+    libc.src.__support.threads.linux.rwlock
+    libc.src.__support.CPP.new
+)
+
+add_entrypoint_object(
+  pthread_rwlock_tryrdlock
+  SRCS
+    pthread_rwlock_tryrdlock.cpp
+  HDRS
+    pthread_rwlock_tryrdlock.h
+  DEPENDS
+    libc.include.pthread
+    libc.src.__support.threads.linux.rwlock
+)
+
+add_entrypoint_object(
+  pthread_rwlock_trywrlock
+  SRCS
+    pthread_rwlock_trywrlock.cpp
+  HDRS
+    pthread_rwlock_trywrlock.h
+  DEPENDS
+    libc.include.pthread
+    libc.src.__support.threads.linux.rwlock
+)
+
+add_entrypoint_object(
+  pthread_rwlock_timedrdlock
+  SRCS
+    pthread_rwlock_timedrdlock.cpp
+  HDRS
+    pthread_rwlock_timedrdlock.h
+  DEPENDS
+    libc.include.pthread
+    libc.src.__support.threads.linux.rwlock
+)
+
+add_entrypoint_object(
+  pthread_rwlock_timedwrlock
+  SRCS
+    pthread_rwlock_timedwrlock.cpp
+  HDRS
+    pthread_rwlock_timedwrlock.h
+  DEPENDS
+    libc.include.pthread
+    libc.src.__support.threads.linux.rwlock
+)
+
+add_entrypoint_object(
+  pthread_rwlock_rdlock
+  SRCS
+    pthread_rwlock_rdlock.cpp
+  HDRS
+    pthread_rwlock_rdlock.h
+  DEPENDS
+    libc.include.pthread
+    libc.src.__support.threads.linux.rwlock
+)
+
+add_entrypoint_object(
+  pthread_rwlock_wrlock
+  SRCS
+    pthread_rwlock_wrlock.cpp
+  HDRS
+    pthread_rwlock_wrlock.h
+  DEPENDS
+    libc.include.pthread
+    libc.src.__support.threads.linux.rwlock
+)
+
+add_entrypoint_object(
+  pthread_rwlock_unlock
+  SRCS
+    pthread_rwlock_unlock.cpp
+  HDRS
+    pthread_rwlock_unlock.h
+  DEPENDS
+    libc.include.pthread
+    libc.src.__support.threads.linux.rwlock
+)
+
+add_entrypoint_object(
+  pthread_rwlock_destroy
+  SRCS
+    pthread_rwlock_destroy.cpp
+  HDRS
+    pthread_rwlock_destroy.h
+  DEPENDS
+    libc.include.pthread
+    libc.src.__support.threads.linux.rwlock
+)
+
 add_entrypoint_object(
   pthread_once
   SRCS

diff  --git a/libc/src/pthread/pthread_rwlock_destroy.cpp b/libc/src/pthread/pthread_rwlock_destroy.cpp
new file mode 100644
index 0000000000000..d82bb376bda2c
--- /dev/null
+++ b/libc/src/pthread/pthread_rwlock_destroy.cpp
@@ -0,0 +1,33 @@
+//===-- Implementation for Rwlock's destroy 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_rwlock_destroy.h"
+
+#include "src/__support/common.h"
+#include "src/__support/threads/linux/rwlock.h"
+
+#include <errno.h>
+#include <pthread.h>
+
+namespace LIBC_NAMESPACE {
+
+LLVM_LIBC_FUNCTION(int, pthread_rwlock_destroy, (pthread_rwlock_t * rwlock)) {
+  if (!rwlock)
+    return EINVAL;
+  auto *rw = reinterpret_cast<RwLock *>(rwlock);
+  RwLock::LockResult res = rw->check_for_destroy();
+
+  // this is currently no-op, but we still call the destructor as a symmetry
+  // to its constructor call;
+  if (res == RwLock::LockResult::Success)
+    rw->~RwLock();
+
+  return static_cast<int>(res);
+}
+
+} // namespace LIBC_NAMESPACE

diff  --git a/libc/src/pthread/pthread_rwlock_destroy.h b/libc/src/pthread/pthread_rwlock_destroy.h
new file mode 100644
index 0000000000000..f845e806d6e60
--- /dev/null
+++ b/libc/src/pthread/pthread_rwlock_destroy.h
@@ -0,0 +1,20 @@
+//===-- Implementation header for Rwlock's destroy 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_RWLOCK_DESTROY_H
+#define LLVM_LIBC_SRC_PTHREAD_PTHREAD_RWLOCK_DESTROY_H
+
+#include <pthread.h>
+
+namespace LIBC_NAMESPACE {
+
+int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
+
+} // namespace LIBC_NAMESPACE
+
+#endif // LLVM_LIBC_SRC_PTHREAD_PTHREAD_RWLOCK_DESTROY_H

diff  --git a/libc/src/pthread/pthread_rwlock_init.cpp b/libc/src/pthread/pthread_rwlock_init.cpp
new file mode 100644
index 0000000000000..b1b58aac6c6ff
--- /dev/null
+++ b/libc/src/pthread/pthread_rwlock_init.cpp
@@ -0,0 +1,67 @@
+//===-- Linux implementation of the pthread_rwlock_init 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_rwlock_init.h"
+
+#include "src/__support/CPP/new.h"
+#include "src/__support/common.h"
+#include "src/__support/libc_assert.h"
+#include "src/__support/threads/linux/rwlock.h"
+
+#include <errno.h>
+#include <pthread.h>
+
+namespace LIBC_NAMESPACE {
+
+static_assert(
+    sizeof(RwLock) == sizeof(pthread_rwlock_t) &&
+        alignof(RwLock) == alignof(pthread_rwlock_t),
+    "The public pthread_rwlock_t type must be of the same size and alignment "
+    "as the internal rwlock type.");
+
+LLVM_LIBC_FUNCTION(int, pthread_rwlock_init,
+                   (pthread_rwlock_t * rwlock,
+                    const pthread_rwlockattr_t *__restrict attr)) {
+  pthread_rwlockattr_t rwlockattr{
+      /*pshared=*/PTHREAD_PROCESS_PRIVATE,
+      /*pref*/ PTHREAD_RWLOCK_PREFER_READER_NP,
+  };
+  // POSIX does not specify this check, so we add an assertion to catch it.
+  LIBC_ASSERT(rwlock && "rwlock is null");
+  if (attr)
+    rwlockattr = *attr;
+
+  // PTHREAD_RWLOCK_PREFER_WRITER_NP is not supported.
+  rwlock::Role preference;
+  switch (rwlockattr.pref) {
+  case PTHREAD_RWLOCK_PREFER_READER_NP:
+    preference = rwlock::Role::Reader;
+    break;
+  case PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP:
+    preference = rwlock::Role::Writer;
+    break;
+  default:
+    return EINVAL;
+  }
+  bool is_pshared;
+  switch (rwlockattr.pshared) {
+  case PTHREAD_PROCESS_PRIVATE:
+    is_pshared = false;
+    break;
+  case PTHREAD_PROCESS_SHARED:
+    is_pshared = true;
+    break;
+  default:
+    return EINVAL;
+  }
+
+  new (rwlock) RwLock(preference, is_pshared);
+  return 0;
+}
+
+} // namespace LIBC_NAMESPACE

diff  --git a/libc/src/pthread/pthread_rwlock_init.h b/libc/src/pthread/pthread_rwlock_init.h
new file mode 100644
index 0000000000000..78d2934882c1d
--- /dev/null
+++ b/libc/src/pthread/pthread_rwlock_init.h
@@ -0,0 +1,21 @@
+//===-- Implementation header for pthread_rwlock_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_RWLOCK_INIT_H
+#define LLVM_LIBC_SRC_PTHREAD_PTHREAD_RWLOCK_INIT_H
+
+#include <pthread.h>
+
+namespace LIBC_NAMESPACE {
+
+int pthread_rwlock_init(pthread_rwlock_t *rwlock,
+                        const pthread_rwlockattr_t *__restrict attr);
+
+} // namespace LIBC_NAMESPACE
+
+#endif // LLVM_LIBC_SRC_PTHREAD_PTHREAD_RWLOCK_INIT_H

diff  --git a/libc/src/pthread/pthread_rwlock_rdlock.cpp b/libc/src/pthread/pthread_rwlock_rdlock.cpp
new file mode 100644
index 0000000000000..e9aee5da4e7e4
--- /dev/null
+++ b/libc/src/pthread/pthread_rwlock_rdlock.cpp
@@ -0,0 +1,32 @@
+//===-- Implementation of the Rwlock's rdlock 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_rwlock_rdlock.h"
+
+#include "src/__support/common.h"
+#include "src/__support/threads/linux/rwlock.h"
+
+#include <errno.h>
+#include <pthread.h>
+
+namespace LIBC_NAMESPACE {
+
+static_assert(
+    sizeof(RwLock) == sizeof(pthread_rwlock_t) &&
+        alignof(RwLock) == alignof(pthread_rwlock_t),
+    "The public pthread_rwlock_t type must be of the same size and alignment "
+    "as the internal rwlock type.");
+
+LLVM_LIBC_FUNCTION(int, pthread_rwlock_rdlock, (pthread_rwlock_t * rwlock)) {
+  if (!rwlock)
+    return EINVAL;
+  RwLock *rw = reinterpret_cast<RwLock *>(rwlock);
+  return static_cast<int>(rw->read_lock());
+}
+
+} // namespace LIBC_NAMESPACE

diff  --git a/libc/src/pthread/pthread_rwlock_rdlock.h b/libc/src/pthread/pthread_rwlock_rdlock.h
new file mode 100644
index 0000000000000..79027739f4b7c
--- /dev/null
+++ b/libc/src/pthread/pthread_rwlock_rdlock.h
@@ -0,0 +1,20 @@
+//===-- Implementation header for Rwlock's rdlock 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_RWLOCK_RDLOCK_H
+#define LLVM_LIBC_SRC_PTHREAD_PTHREAD_RWLOCK_RDLOCK_H
+
+#include <pthread.h>
+
+namespace LIBC_NAMESPACE {
+
+int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
+
+} // namespace LIBC_NAMESPACE
+
+#endif // LLVM_LIBC_SRC_PTHREAD_PTHREAD_RWLOCK_RDLOCK_H

diff  --git a/libc/src/pthread/pthread_rwlock_timedrdlock.cpp b/libc/src/pthread/pthread_rwlock_timedrdlock.cpp
new file mode 100644
index 0000000000000..d503d50b79f6c
--- /dev/null
+++ b/libc/src/pthread/pthread_rwlock_timedrdlock.cpp
@@ -0,0 +1,49 @@
+//===-- Implementation of the Rwlock's timedrdlock 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_rwlock_timedrdlock.h"
+
+#include "src/__support/common.h"
+#include "src/__support/libc_assert.h"
+#include "src/__support/macros/optimization.h"
+#include "src/__support/threads/linux/rwlock.h"
+#include "src/__support/time/linux/abs_timeout.h"
+
+#include <errno.h>
+#include <pthread.h>
+
+namespace LIBC_NAMESPACE {
+
+static_assert(
+    sizeof(RwLock) == sizeof(pthread_rwlock_t) &&
+        alignof(RwLock) == alignof(pthread_rwlock_t),
+    "The public pthread_rwlock_t type must be of the same size and alignment "
+    "as the internal rwlock type.");
+
+LLVM_LIBC_FUNCTION(int, pthread_rwlock_timedrdlock,
+                   (pthread_rwlock_t * rwlock,
+                    const struct timespec *abstime)) {
+  if (!rwlock)
+    return EINVAL;
+  RwLock *rw = reinterpret_cast<RwLock *>(rwlock);
+  LIBC_ASSERT(abstime && "timedrdlock called with a null timeout");
+  auto timeout =
+      internal::AbsTimeout::from_timespec(*abstime, /*is_realtime=*/true);
+  if (LIBC_LIKELY(timeout.has_value()))
+    return static_cast<int>(rw->read_lock(timeout.value()));
+
+  switch (timeout.error()) {
+  case internal::AbsTimeout::Error::Invalid:
+    return EINVAL;
+  case internal::AbsTimeout::Error::BeforeEpoch:
+    return ETIMEDOUT;
+    // default: unreachable, all two cases are covered.
+  }
+}
+
+} // namespace LIBC_NAMESPACE

diff  --git a/libc/src/pthread/pthread_rwlock_timedrdlock.h b/libc/src/pthread/pthread_rwlock_timedrdlock.h
new file mode 100644
index 0000000000000..dfa43f25ba706
--- /dev/null
+++ b/libc/src/pthread/pthread_rwlock_timedrdlock.h
@@ -0,0 +1,21 @@
+//===-- Implementation header for Rwlock's timedrdlock 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_RWLOCK_TIMEDRDLOCK_H
+#define LLVM_LIBC_SRC_PTHREAD_PTHREAD_RWLOCK_TIMEDRDLOCK_H
+
+#include <pthread.h>
+
+namespace LIBC_NAMESPACE {
+
+int pthread_rwlock_timedrdlock(pthread_rwlock_t *__restrict rwlock,
+                               const struct timespec *__restrict abs_timeout);
+
+} // namespace LIBC_NAMESPACE
+
+#endif // LLVM_LIBC_SRC_PTHREAD_PTHREAD_RWLOCK_TIMEDRDLOCK_H

diff  --git a/libc/src/pthread/pthread_rwlock_timedwrlock.cpp b/libc/src/pthread/pthread_rwlock_timedwrlock.cpp
new file mode 100644
index 0000000000000..5e67730141946
--- /dev/null
+++ b/libc/src/pthread/pthread_rwlock_timedwrlock.cpp
@@ -0,0 +1,43 @@
+//===-- Implementation for Rwlock's timedwrlock 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_rwlock_timedwrlock.h"
+
+#include "src/__support/common.h"
+#include "src/__support/libc_assert.h"
+#include "src/__support/macros/optimization.h"
+#include "src/__support/threads/linux/rwlock.h"
+#include "src/__support/time/linux/abs_timeout.h"
+
+#include <errno.h>
+#include <pthread.h>
+
+namespace LIBC_NAMESPACE {
+
+LLVM_LIBC_FUNCTION(int, pthread_rwlock_timedwrlock,
+                   (pthread_rwlock_t *__restrict rwlock,
+                    const struct timespec *__restrict abstime)) {
+  if (!rwlock)
+    return EINVAL;
+  RwLock *rw = reinterpret_cast<RwLock *>(rwlock);
+  LIBC_ASSERT(abstime && "timedwrlock called with a null timeout");
+  auto timeout =
+      internal::AbsTimeout::from_timespec(*abstime, /*is_realtime=*/true);
+  if (LIBC_LIKELY(timeout.has_value()))
+    return static_cast<int>(rw->write_lock(timeout.value()));
+
+  switch (timeout.error()) {
+  case internal::AbsTimeout::Error::Invalid:
+    return EINVAL;
+  case internal::AbsTimeout::Error::BeforeEpoch:
+    return ETIMEDOUT;
+    // default: unreachable, all two cases are covered.
+  }
+}
+
+} // namespace LIBC_NAMESPACE

diff  --git a/libc/src/pthread/pthread_rwlock_timedwrlock.h b/libc/src/pthread/pthread_rwlock_timedwrlock.h
new file mode 100644
index 0000000000000..a39d8de8d330f
--- /dev/null
+++ b/libc/src/pthread/pthread_rwlock_timedwrlock.h
@@ -0,0 +1,21 @@
+//===-- Implementation header for Rwlock's timedwrlock 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_RWLOCK_TIMEDWRLOCK_H
+#define LLVM_LIBC_SRC_PTHREAD_PTHREAD_RWLOCK_TIMEDWRLOCK_H
+
+#include <pthread.h>
+
+namespace LIBC_NAMESPACE {
+
+int pthread_rwlock_timedwrlock(pthread_rwlock_t *__restrict rwlock,
+                               const struct timespec *__restrict abs_timeout);
+
+} // namespace LIBC_NAMESPACE
+
+#endif // LLVM_LIBC_SRC_PTHREAD_PTHREAD_RWLOCK_TIMEDWRLOCK_H

diff  --git a/libc/src/pthread/pthread_rwlock_tryrdlock.cpp b/libc/src/pthread/pthread_rwlock_tryrdlock.cpp
new file mode 100644
index 0000000000000..9dc1bf09bc830
--- /dev/null
+++ b/libc/src/pthread/pthread_rwlock_tryrdlock.cpp
@@ -0,0 +1,32 @@
+//===-- Implementation of the Rwlock's tryrdlock 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_rwlock_tryrdlock.h"
+
+#include "src/__support/common.h"
+#include "src/__support/threads/linux/rwlock.h"
+
+#include <errno.h>
+#include <pthread.h>
+
+namespace LIBC_NAMESPACE {
+
+static_assert(
+    sizeof(RwLock) == sizeof(pthread_rwlock_t) &&
+        alignof(RwLock) == alignof(pthread_rwlock_t),
+    "The public pthread_rwlock_t type must be of the same size and alignment "
+    "as the internal rwlock type.");
+
+LLVM_LIBC_FUNCTION(int, pthread_rwlock_tryrdlock, (pthread_rwlock_t * rwlock)) {
+  if (!rwlock)
+    return EINVAL;
+  RwLock *rw = reinterpret_cast<RwLock *>(rwlock);
+  return static_cast<int>(rw->try_read_lock());
+}
+
+} // namespace LIBC_NAMESPACE

diff  --git a/libc/src/pthread/pthread_rwlock_tryrdlock.h b/libc/src/pthread/pthread_rwlock_tryrdlock.h
new file mode 100644
index 0000000000000..b07ab5b152b1a
--- /dev/null
+++ b/libc/src/pthread/pthread_rwlock_tryrdlock.h
@@ -0,0 +1,20 @@
+//===-- Implementation header for Rwlock's tryrdlock 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_RWLOCK_TRYRDLOCK_H
+#define LLVM_LIBC_SRC_PTHREAD_PTHREAD_RWLOCK_TRYRDLOCK_H
+
+#include <pthread.h>
+
+namespace LIBC_NAMESPACE {
+
+int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
+
+} // namespace LIBC_NAMESPACE
+
+#endif // LLVM_LIBC_SRC_PTHREAD_PTHREAD_RWLOCK_TRYRDLOCK_H

diff  --git a/libc/src/pthread/pthread_rwlock_trywrlock.cpp b/libc/src/pthread/pthread_rwlock_trywrlock.cpp
new file mode 100644
index 0000000000000..e4ace3cb350af
--- /dev/null
+++ b/libc/src/pthread/pthread_rwlock_trywrlock.cpp
@@ -0,0 +1,32 @@
+//===-- Implementation for Rwlock's trywrlock 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_rwlock_trywrlock.h"
+
+#include "src/__support/common.h"
+#include "src/__support/threads/linux/rwlock.h"
+
+#include <errno.h>
+#include <pthread.h>
+
+namespace LIBC_NAMESPACE {
+
+static_assert(
+    sizeof(RwLock) == sizeof(pthread_rwlock_t) &&
+        alignof(RwLock) == alignof(pthread_rwlock_t),
+    "The public pthread_rwlock_t type must be of the same size and alignment "
+    "as the internal rwlock type.");
+
+LLVM_LIBC_FUNCTION(int, pthread_rwlock_trywrlock, (pthread_rwlock_t * rwlock)) {
+  if (!rwlock)
+    return EINVAL;
+  RwLock *rw = reinterpret_cast<RwLock *>(rwlock);
+  return static_cast<int>(rw->try_write_lock());
+}
+
+} // namespace LIBC_NAMESPACE

diff  --git a/libc/src/pthread/pthread_rwlock_trywrlock.h b/libc/src/pthread/pthread_rwlock_trywrlock.h
new file mode 100644
index 0000000000000..fc146c6db859f
--- /dev/null
+++ b/libc/src/pthread/pthread_rwlock_trywrlock.h
@@ -0,0 +1,20 @@
+//===-- Implementation header for Rwlock's trywrlock 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_RWLOCK_TRYWRLOCK_H
+#define LLVM_LIBC_SRC_PTHREAD_PTHREAD_RWLOCK_TRYWRLOCK_H
+
+#include <pthread.h>
+
+namespace LIBC_NAMESPACE {
+
+int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);
+
+} // namespace LIBC_NAMESPACE
+
+#endif // LLVM_LIBC_SRC_PTHREAD_PTHREAD_RWLOCK_TRYWRLOCK_H

diff  --git a/libc/src/pthread/pthread_rwlock_unlock.cpp b/libc/src/pthread/pthread_rwlock_unlock.cpp
new file mode 100644
index 0000000000000..21cedf42a8d50
--- /dev/null
+++ b/libc/src/pthread/pthread_rwlock_unlock.cpp
@@ -0,0 +1,26 @@
+//===-- Implementation for Rwlock's 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_rwlock_unlock.h"
+
+#include "src/__support/common.h"
+#include "src/__support/threads/linux/rwlock.h"
+
+#include <errno.h>
+#include <pthread.h>
+
+namespace LIBC_NAMESPACE {
+
+LLVM_LIBC_FUNCTION(int, pthread_rwlock_unlock, (pthread_rwlock_t * rwlock)) {
+  if (!rwlock)
+    return EINVAL;
+  auto *rw = reinterpret_cast<RwLock *>(rwlock);
+  return static_cast<int>(rw->unlock());
+}
+
+} // namespace LIBC_NAMESPACE

diff  --git a/libc/src/pthread/pthread_rwlock_unlock.h b/libc/src/pthread/pthread_rwlock_unlock.h
new file mode 100644
index 0000000000000..b9a72f1e06992
--- /dev/null
+++ b/libc/src/pthread/pthread_rwlock_unlock.h
@@ -0,0 +1,20 @@
+//===-- Implementation header for Rwlock's 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_RWLOCK_UNLOCK_H
+#define LLVM_LIBC_SRC_PTHREAD_PTHREAD_RWLOCK_UNLOCK_H
+
+#include <pthread.h>
+
+namespace LIBC_NAMESPACE {
+
+int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
+
+} // namespace LIBC_NAMESPACE
+
+#endif // LLVM_LIBC_SRC_PTHREAD_PTHREAD_RWLOCK_UNLOCK_H

diff  --git a/libc/src/pthread/pthread_rwlock_wrlock.cpp b/libc/src/pthread/pthread_rwlock_wrlock.cpp
new file mode 100644
index 0000000000000..5d3868a58f4e9
--- /dev/null
+++ b/libc/src/pthread/pthread_rwlock_wrlock.cpp
@@ -0,0 +1,32 @@
+//===-- Implementation for Rwlock's wrlock 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_rwlock_wrlock.h"
+
+#include "src/__support/common.h"
+#include "src/__support/threads/linux/rwlock.h"
+
+#include <errno.h>
+#include <pthread.h>
+
+namespace LIBC_NAMESPACE {
+
+static_assert(
+    sizeof(RwLock) == sizeof(pthread_rwlock_t) &&
+        alignof(RwLock) == alignof(pthread_rwlock_t),
+    "The public pthread_rwlock_t type must be of the same size and alignment "
+    "as the internal rwlock type.");
+
+LLVM_LIBC_FUNCTION(int, pthread_rwlock_wrlock, (pthread_rwlock_t * rwlock)) {
+  if (!rwlock)
+    return EINVAL;
+  RwLock *rw = reinterpret_cast<RwLock *>(rwlock);
+  return static_cast<int>(rw->write_lock());
+}
+
+} // namespace LIBC_NAMESPACE

diff  --git a/libc/src/pthread/pthread_rwlock_wrlock.h b/libc/src/pthread/pthread_rwlock_wrlock.h
new file mode 100644
index 0000000000000..ba77c1f1f09ac
--- /dev/null
+++ b/libc/src/pthread/pthread_rwlock_wrlock.h
@@ -0,0 +1,20 @@
+//===-- Implementation header for Rwlock's wrlock 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_RWLOCK_WRLOCK_H
+#define LLVM_LIBC_SRC_PTHREAD_PTHREAD_RWLOCK_WRLOCK_H
+
+#include <pthread.h>
+
+namespace LIBC_NAMESPACE {
+
+int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
+
+} // namespace LIBC_NAMESPACE
+
+#endif // LLVM_LIBC_SRC_PTHREAD_PTHREAD_RWLOCK_WRLOCK_H

diff  --git a/libc/test/integration/src/pthread/CMakeLists.txt b/libc/test/integration/src/pthread/CMakeLists.txt
index a10dc256200d9..16a1ff61887f8 100644
--- a/libc/test/integration/src/pthread/CMakeLists.txt
+++ b/libc/test/integration/src/pthread/CMakeLists.txt
@@ -17,6 +17,45 @@ add_integration_test(
     libc.src.pthread.pthread_join
 )
 
+add_integration_test(
+  pthread_rwlock_test
+  SUITE
+    libc-pthread-integration-tests
+  SRCS
+    pthread_rwlock_test.cpp
+  DEPENDS
+    libc.include.pthread
+    libc.include.time
+    libc.include.errno
+    libc.src.pthread.pthread_rwlock_destroy
+    libc.src.pthread.pthread_rwlock_init
+    libc.src.pthread.pthread_rwlock_rdlock
+    libc.src.pthread.pthread_rwlock_tryrdlock
+    libc.src.pthread.pthread_rwlock_timedrdlock
+    libc.src.pthread.pthread_rwlock_wrlock
+    libc.src.pthread.pthread_rwlock_trywrlock
+    libc.src.pthread.pthread_rwlock_timedwrlock
+    libc.src.pthread.pthread_rwlock_unlock
+    libc.src.pthread.pthread_create
+    libc.src.pthread.pthread_join
+    libc.src.pthread.pthread_rwlockattr_init
+    libc.src.pthread.pthread_rwlockattr_destroy
+    libc.src.pthread.pthread_rwlockattr_setpshared
+    libc.src.pthread.pthread_rwlockattr_setkind_np
+    libc.src.__support.threads.linux.raw_mutex
+    libc.src.stdio.printf
+    libc.src.stdlib.getenv
+    libc.src.sys.mman.mmap
+    libc.src.sys.mman.munmap
+    libc.src.time.clock_gettime
+    libc.src.sys.random.getrandom
+    libc.src.unistd.fork
+    libc.src.sys.wait.waitpid
+    libc.src.stdlib.exit
+    libc.src.__support.CPP.atomic
+    libc.src.__support.threads.sleep
+)
+
 add_integration_test(
   pthread_test
   SUITE

diff  --git a/libc/test/integration/src/pthread/pthread_rwlock_test.cpp b/libc/test/integration/src/pthread/pthread_rwlock_test.cpp
new file mode 100644
index 0000000000000..215db1fc5addf
--- /dev/null
+++ b/libc/test/integration/src/pthread/pthread_rwlock_test.cpp
@@ -0,0 +1,478 @@
+//===-- Tests for pthread_rwlock ------------------------------------------===//
+//
+// 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/__support/CPP/atomic.h"
+#include "src/__support/OSUtil/syscall.h"
+#include "src/__support/threads/linux/raw_mutex.h"
+#include "src/__support/threads/linux/rwlock.h"
+#include "src/__support/threads/sleep.h"
+#include "src/pthread/pthread_create.h"
+#include "src/pthread/pthread_join.h"
+#include "src/pthread/pthread_rwlock_destroy.h"
+#include "src/pthread/pthread_rwlock_init.h"
+#include "src/pthread/pthread_rwlock_rdlock.h"
+#include "src/pthread/pthread_rwlock_timedrdlock.h"
+#include "src/pthread/pthread_rwlock_timedwrlock.h"
+#include "src/pthread/pthread_rwlock_tryrdlock.h"
+#include "src/pthread/pthread_rwlock_trywrlock.h"
+#include "src/pthread/pthread_rwlock_unlock.h"
+#include "src/pthread/pthread_rwlock_wrlock.h"
+#include "src/pthread/pthread_rwlockattr_destroy.h"
+#include "src/pthread/pthread_rwlockattr_init.h"
+#include "src/pthread/pthread_rwlockattr_setkind_np.h"
+#include "src/pthread/pthread_rwlockattr_setpshared.h"
+#include "src/stdio/printf.h"
+#include "src/stdlib/exit.h"
+#include "src/stdlib/getenv.h"
+#include "src/sys/mman/mmap.h"
+#include "src/sys/mman/munmap.h"
+#include "src/sys/random/getrandom.h"
+#include "src/sys/wait/waitpid.h"
+#include "src/time/clock_gettime.h"
+#include "src/unistd/fork.h"
+#include "test/IntegrationTest/test.h"
+#include <errno.h>
+#include <optional>
+#include <pthread.h>
+#include <time.h>
+
+namespace LIBC_NAMESPACE::rwlock {
+class RwLockTester {
+public:
+  static constexpr int full_reader_state() {
+    return (~0) & (~RwState::PENDING_MASK) & (~RwState::ACTIVE_WRITER_BIT);
+  }
+};
+} // namespace LIBC_NAMESPACE::rwlock
+
+static void smoke_test() {
+  pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_init(&rwlock, nullptr), 0);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_rdlock(&rwlock), 0);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_tryrdlock(&rwlock), 0);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_trywrlock(&rwlock), EBUSY);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_unlock(&rwlock), 0);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_unlock(&rwlock), 0);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_wrlock(&rwlock), 0);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_rdlock(&rwlock), EDEADLK);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_wrlock(&rwlock), EDEADLK);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_tryrdlock(&rwlock), EBUSY);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_trywrlock(&rwlock), EBUSY);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_unlock(&rwlock), 0);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_destroy(&rwlock), 0);
+}
+
+static void deadlock_detection_test() {
+  pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_init(&rwlock, nullptr), 0);
+  // We only detect RAW, WAW deadlocks.
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_wrlock(&rwlock), 0);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_wrlock(&rwlock), EDEADLK);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_unlock(&rwlock), 0);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_destroy(&rwlock), 0);
+}
+
+static void try_lock_test() {
+  pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_init(&rwlock, nullptr), 0);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_wrlock(&rwlock), 0);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_trywrlock(&rwlock), EBUSY);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_tryrdlock(&rwlock), EBUSY);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_unlock(&rwlock), 0);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_tryrdlock(&rwlock), 0);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_rdlock(&rwlock), 0);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_tryrdlock(&rwlock), 0);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_trywrlock(&rwlock), EBUSY);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_unlock(&rwlock), 0);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_unlock(&rwlock), 0);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_unlock(&rwlock), 0);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_destroy(&rwlock), 0);
+}
+
+static void destroy_before_unlock_test() {
+  pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_init(&rwlock, nullptr), 0);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_wrlock(&rwlock), 0);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_destroy(&rwlock), EBUSY);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_unlock(&rwlock), 0);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_destroy(&rwlock), 0);
+}
+
+static void nullptr_test() {
+  timespec ts = {};
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_rdlock(nullptr), EINVAL);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_wrlock(nullptr), EINVAL);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_timedrdlock(nullptr, &ts), EINVAL);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_timedwrlock(nullptr, &ts), EINVAL);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_tryrdlock(nullptr), EINVAL);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_trywrlock(nullptr), EINVAL);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_unlock(nullptr), EINVAL);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_destroy(nullptr), EINVAL);
+}
+
+// If you are a user reading this code, please do not do something like this.
+// We manually modify the internal state of the rwlock to test high reader
+// counts.
+static void high_reader_count_test() {
+  pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;
+  rwlock.__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.
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_unlock(&rwlock), 0);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_unlock(&rwlock), 0);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_unlock(&rwlock), 0);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_unlock(&rwlock), 0);
+
+  pthread_t threads[20];
+  for (auto &i : threads)
+    ASSERT_EQ(LIBC_NAMESPACE::pthread_create(
+                  &i, nullptr,
+                  [](void *arg) -> void * {
+                    pthread_rwlock_t *rwlock =
+                        reinterpret_cast<pthread_rwlock_t *>(arg);
+                    ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_trywrlock(rwlock),
+                              EBUSY);
+                    while (LIBC_NAMESPACE::pthread_rwlock_rdlock(rwlock) ==
+                           EAGAIN)
+                      LIBC_NAMESPACE::sleep_briefly();
+                    ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_unlock(rwlock), 0);
+                    return nullptr;
+                  },
+                  &rwlock),
+              0);
+
+  for (auto &i : threads)
+    ASSERT_EQ(LIBC_NAMESPACE::pthread_join(i, nullptr), 0);
+}
+
+static void unusual_timespec_test() {
+  pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;
+  timespec ts = {0, -1};
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_timedrdlock(&rwlock, &ts), EINVAL);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_timedwrlock(&rwlock, &ts), EINVAL);
+  ts.tv_nsec = 1'000'000'000;
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_timedrdlock(&rwlock, &ts), EINVAL);
+  ts.tv_nsec += 1;
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_timedwrlock(&rwlock, &ts), EINVAL);
+  ts.tv_nsec = 0;
+  ts.tv_sec = -1;
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_timedrdlock(&rwlock, &ts),
+            ETIMEDOUT);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_timedwrlock(&rwlock, &ts),
+            ETIMEDOUT);
+}
+
+static void timedlock_with_deadlock_test() {
+  pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;
+  timespec ts{};
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_rdlock(&rwlock), 0);
+  LIBC_NAMESPACE::clock_gettime(CLOCK_REALTIME, &ts);
+  ts.tv_nsec += 50'000;
+  if (ts.tv_nsec >= 1'000'000'000) {
+    ts.tv_nsec -= 1'000'000'000;
+    ts.tv_sec += 1;
+  }
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_timedwrlock(&rwlock, &ts),
+            ETIMEDOUT);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_timedrdlock(&rwlock, &ts), 0);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_unlock(&rwlock), 0);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_unlock(&rwlock), 0);
+  // notice that ts is already expired, but the following should still succeed.
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_tryrdlock(&rwlock), 0);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_unlock(&rwlock), 0);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_trywrlock(&rwlock), 0);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_unlock(&rwlock), 0);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_rdlock(&rwlock), 0);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_unlock(&rwlock), 0);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_wrlock(&rwlock), 0);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_unlock(&rwlock), 0);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_destroy(&rwlock), 0);
+}
+
+static void attributed_initialization_test() {
+  pthread_rwlockattr_t attr{};
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlockattr_init(&attr), 0);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlockattr_setkind_np(
+                &attr, PTHREAD_RWLOCK_PREFER_READER_NP),
+            0);
+  {
+    pthread_rwlock_t rwlock{};
+    ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_init(&rwlock, &attr), 0);
+    ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_destroy(&rwlock), 0);
+  }
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlockattr_setkind_np(
+                &attr, PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP),
+            0);
+  {
+    pthread_rwlock_t rwlock{};
+    ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_init(&rwlock, &attr), 0);
+    ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_destroy(&rwlock), 0);
+  }
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlockattr_setkind_np(
+                &attr, PTHREAD_RWLOCK_PREFER_WRITER_NP),
+            0);
+  {
+    pthread_rwlock_t rwlock{};
+    ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_init(&rwlock, &attr), EINVAL);
+  }
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlockattr_setkind_np(
+                &attr, PTHREAD_RWLOCK_PREFER_READER_NP),
+            0);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlockattr_setpshared(
+                &attr, PTHREAD_PROCESS_PRIVATE),
+            0);
+  {
+    pthread_rwlock_t rwlock{};
+    ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_init(&rwlock, &attr), 0);
+    ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_destroy(&rwlock), 0);
+  }
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlockattr_setpshared(
+                &attr, PTHREAD_PROCESS_SHARED),
+            0);
+  {
+    pthread_rwlock_t rwlock{};
+    ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_init(&rwlock, &attr), 0);
+    ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_destroy(&rwlock), 0);
+  }
+  attr.pref = -1;
+  {
+    pthread_rwlock_t rwlock{};
+    ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_init(&rwlock, &attr), EINVAL);
+  }
+  attr.pref = PTHREAD_RWLOCK_PREFER_READER_NP;
+  attr.pshared = -1;
+  {
+    pthread_rwlock_t rwlock{};
+    ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_init(&rwlock, &attr), EINVAL);
+  }
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlockattr_destroy(&attr), 0);
+}
+
+struct SharedData {
+  pthread_rwlock_t lock;
+  int data;
+  LIBC_NAMESPACE::cpp::Atomic<int> reader_count;
+  bool writer_flag;
+  LIBC_NAMESPACE::cpp::Atomic<int> total_writer_count;
+};
+
+enum class Operation : int {
+  READ = 0,
+  WRITE = 1,
+  TIMED_READ = 2,
+  TIMED_WRITE = 3,
+  TRY_READ = 4,
+  TRY_WRITE = 5,
+  COUNT = 6
+};
+
+LIBC_NAMESPACE::RawMutex *io_mutex;
+struct ThreadGuard {
+  Operation record[64]{};
+  size_t cursor = 0;
+  void push(Operation op) { record[cursor++] = op; }
+  ~ThreadGuard() {
+    if (!LIBC_NAMESPACE::getenv("LIBC_PTHREAD_RWLOCK_TEST_VERBOSE"))
+      return;
+    pid_t pid = LIBC_NAMESPACE::syscall_impl(SYS_getpid);
+    pid_t tid = LIBC_NAMESPACE::syscall_impl(SYS_gettid);
+    io_mutex->lock(LIBC_NAMESPACE::cpp::nullopt, true);
+    LIBC_NAMESPACE::printf("process %d thread %d: ", pid, tid);
+    for (size_t i = 0; i < cursor; ++i)
+      LIBC_NAMESPACE::printf("%d ", static_cast<int>(record[i]));
+    LIBC_NAMESPACE::printf("\n");
+    io_mutex->unlock(true);
+  }
+};
+
+static void randomized_thread_operation(SharedData *data, ThreadGuard &guard) {
+  int buffer;
+  // We cannot reason about thread order anyway, let's go wild and randomize it
+  // directly using getrandom.
+  LIBC_NAMESPACE::getrandom(&buffer, sizeof(buffer), 0);
+  constexpr int TOTAL = static_cast<int>(Operation::COUNT);
+  Operation op = static_cast<Operation>(((buffer % TOTAL) + TOTAL) % TOTAL);
+  guard.push(op);
+  auto read_ops = [data]() {
+    ASSERT_FALSE(data->writer_flag);
+    data->reader_count.fetch_add(1, LIBC_NAMESPACE::cpp::MemoryOrder::RELAXED);
+    for (int i = 0; i < 10; ++i)
+      LIBC_NAMESPACE::sleep_briefly();
+    data->reader_count.fetch_sub(1, LIBC_NAMESPACE::cpp::MemoryOrder::RELAXED);
+  };
+  auto write_ops = [data]() {
+    ASSERT_FALSE(data->writer_flag);
+    data->data += 1;
+    data->writer_flag = true;
+    for (int i = 0; i < 10; ++i)
+      LIBC_NAMESPACE::sleep_briefly();
+    ASSERT_EQ(data->reader_count, 0);
+    data->writer_flag = false;
+    data->total_writer_count.fetch_add(1);
+  };
+  auto get_ts = []() {
+    timespec ts{};
+    LIBC_NAMESPACE::clock_gettime(CLOCK_REALTIME, &ts);
+    ts.tv_nsec += 5'000;
+    if (ts.tv_nsec >= 1'000'000'000) {
+      ts.tv_nsec -= 1'000'000'000;
+      ts.tv_sec += 1;
+    }
+    return ts;
+  };
+  switch (op) {
+  case Operation::READ: {
+    LIBC_NAMESPACE::pthread_rwlock_rdlock(&data->lock);
+    read_ops();
+    LIBC_NAMESPACE::pthread_rwlock_unlock(&data->lock);
+    break;
+  }
+  case Operation::WRITE: {
+    LIBC_NAMESPACE::pthread_rwlock_wrlock(&data->lock);
+    write_ops();
+    LIBC_NAMESPACE::pthread_rwlock_unlock(&data->lock);
+    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);
+    }
+    break;
+  }
+  case Operation::TIMED_WRITE: {
+    timespec ts = get_ts();
+    if (LIBC_NAMESPACE::pthread_rwlock_timedwrlock(&data->lock, &ts) == 0) {
+      write_ops();
+      LIBC_NAMESPACE::pthread_rwlock_unlock(&data->lock);
+    }
+    break;
+  }
+  case Operation::TRY_READ: {
+    if (LIBC_NAMESPACE::pthread_rwlock_tryrdlock(&data->lock) == 0) {
+      read_ops();
+      LIBC_NAMESPACE::pthread_rwlock_unlock(&data->lock);
+    }
+    break;
+  }
+  case Operation::TRY_WRITE: {
+    if (LIBC_NAMESPACE::pthread_rwlock_trywrlock(&data->lock) == 0) {
+      write_ops();
+      LIBC_NAMESPACE::pthread_rwlock_unlock(&data->lock);
+    }
+    break;
+  }
+  case Operation::COUNT:
+    __builtin_trap();
+  }
+}
+
+static void
+randomized_process_operation(SharedData &data,
+                             LIBC_NAMESPACE::cpp::Atomic<int> &finish_count,
+                             int expected_count) {
+  pthread_t threads[32];
+  for (auto &i : threads)
+    ASSERT_EQ(LIBC_NAMESPACE::pthread_create(
+                  &i, nullptr,
+                  [](void *arg) -> void * {
+                    ThreadGuard guard{};
+                    for (int i = 0; i < 64; ++i)
+                      randomized_thread_operation(
+                          reinterpret_cast<SharedData *>(arg), guard);
+                    return nullptr;
+                  },
+                  &data),
+              0);
+
+  for (auto &i : threads)
+    ASSERT_EQ(LIBC_NAMESPACE::pthread_join(i, nullptr), 0);
+
+  finish_count.fetch_add(1);
+  while (finish_count.load() != expected_count)
+    LIBC_NAMESPACE::sleep_briefly();
+
+  ASSERT_EQ(data.total_writer_count.load(), data.data);
+  ASSERT_FALSE(data.writer_flag);
+  ASSERT_EQ(data.reader_count, 0);
+}
+
+static void single_process_test(int preference) {
+  SharedData data{};
+  data.data = 0;
+  data.reader_count = 0;
+  data.writer_flag = false;
+  data.total_writer_count.store(0);
+  pthread_rwlockattr_t attr{};
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlockattr_init(&attr), 0);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlockattr_setkind_np(&attr, preference),
+            0);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_init(&data.lock, nullptr), 0);
+  LIBC_NAMESPACE::cpp::Atomic<int> finish_count{0};
+  randomized_process_operation(data, finish_count, 1);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_destroy(&data.lock), 0);
+}
+
+static void multiple_process_test(int preference) {
+  struct PShared {
+    SharedData data;
+    LIBC_NAMESPACE::cpp::Atomic<int> finish_count;
+  };
+  PShared *shared_data = reinterpret_cast<PShared *>(
+      LIBC_NAMESPACE::mmap(nullptr, sizeof(PShared), PROT_READ | PROT_WRITE,
+                           MAP_SHARED | MAP_ANONYMOUS, -1, 0));
+  shared_data->data.data = 0;
+  shared_data->data.reader_count = 0;
+  shared_data->data.writer_flag = false;
+  shared_data->data.total_writer_count.store(0);
+  shared_data->finish_count.store(0);
+  pthread_rwlockattr_t attr{};
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlockattr_init(&attr), 0);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlockattr_setkind_np(&attr, preference),
+            0);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlockattr_setpshared(
+                &attr, PTHREAD_PROCESS_SHARED),
+            0);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_init(&shared_data->data.lock, &attr),
+            0);
+  int pid = LIBC_NAMESPACE::fork();
+  randomized_process_operation(shared_data->data, shared_data->finish_count, 2);
+  if (pid == 0)
+    LIBC_NAMESPACE::exit(0);
+  else {
+    int status;
+    LIBC_NAMESPACE::waitpid(pid, &status, 0);
+    ASSERT_EQ(status, 0);
+  }
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_destroy(&shared_data->data.lock), 0);
+  LIBC_NAMESPACE::munmap(shared_data, sizeof(PShared));
+}
+
+TEST_MAIN() {
+  io_mutex = new (LIBC_NAMESPACE::mmap(
+      nullptr, sizeof(LIBC_NAMESPACE::RawMutex), PROT_READ | PROT_WRITE,
+      MAP_ANONYMOUS | MAP_SHARED, -1, 0)) LIBC_NAMESPACE::RawMutex();
+  smoke_test();
+  deadlock_detection_test();
+  try_lock_test();
+  destroy_before_unlock_test();
+  nullptr_test();
+  high_reader_count_test();
+  unusual_timespec_test();
+  timedlock_with_deadlock_test();
+  attributed_initialization_test();
+  single_process_test(PTHREAD_RWLOCK_PREFER_READER_NP);
+  single_process_test(PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP);
+  multiple_process_test(PTHREAD_RWLOCK_PREFER_READER_NP);
+  multiple_process_test(PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP);
+  io_mutex->~RawMutex();
+  LIBC_NAMESPACE::munmap(io_mutex, sizeof(LIBC_NAMESPACE::RawMutex));
+  return 0;
+}


        


More information about the libc-commits mailing list