Skip to content

[libc] add pthread_rwlock_clockrdlock and pthread_rwlock_clockwrlock … #100543

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jul 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions libc/config/linux/aarch64/entrypoints.txt
Original file line number Diff line number Diff line change
Expand Up @@ -692,6 +692,8 @@ 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_clockrdlock
libc.src.pthread.pthread_rwlock_clockwrlock
libc.src.pthread.pthread_rwlock_destroy
libc.src.pthread.pthread_rwlock_init
libc.src.pthread.pthread_rwlock_rdlock
Expand Down
2 changes: 2 additions & 0 deletions libc/config/linux/riscv/entrypoints.txt
Original file line number Diff line number Diff line change
Expand Up @@ -703,6 +703,8 @@ 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_clockrdlock
libc.src.pthread.pthread_rwlock_clockwrlock
libc.src.pthread.pthread_rwlock_destroy
libc.src.pthread.pthread_rwlock_init
libc.src.pthread.pthread_rwlock_rdlock
Expand Down
2 changes: 2 additions & 0 deletions libc/config/linux/x86_64/entrypoints.txt
Original file line number Diff line number Diff line change
Expand Up @@ -790,6 +790,8 @@ 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_clockrdlock
libc.src.pthread.pthread_rwlock_clockwrlock
libc.src.pthread.pthread_rwlock_destroy
libc.src.pthread.pthread_rwlock_init
libc.src.pthread.pthread_rwlock_rdlock
Expand Down
5 changes: 5 additions & 0 deletions libc/docs/dev/undefined_behavior.rst
Original file line number Diff line number Diff line change
Expand Up @@ -93,3 +93,8 @@ direction in this case.
Non-const Constant Return Values
--------------------------------
Some libc functions, like ``dlerror()``, return ``char *`` instead of ``const char *`` and then tell the caller they promise not to to modify this value. Any modification of this value is undefined behavior.

Unrecognized ``clockid_t`` values for ``pthread_rwlock_clock*`` APIs
----------------------------------------------------------------------
POSIX.1-2024 only demands support for ``CLOCK_REALTIME`` and ``CLOCK_MONOTONIC``. Currently,
as in LLVM libc, if other clock ids are used, they will be treated as monotonic clocks.
14 changes: 14 additions & 0 deletions libc/newhdrgen/yaml/pthread.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,20 @@ functions:
arguments:
- type: pthread_rwlock_t *__restrict
- type: const struct timespec *__restrict
- name: pthread_rwlock_clockrdlock
standards: POSIX
return_type: int
arguments:
- type: pthread_rwlock_t *__restrict
- type: clockid_t
- type: const struct timespec *__restrict
- name: pthread_rwlock_clockwrlock
standards: POSIX
return_type: int
arguments:
- type: pthread_rwlock_t *__restrict
- type: clockid_t
- type: const struct timespec *__restrict
- name: pthread_rwlock_rdlock
standards: POSIX
return_type: int
Expand Down
10 changes: 10 additions & 0 deletions libc/spec/posix.td
Original file line number Diff line number Diff line change
Expand Up @@ -1325,6 +1325,16 @@ def POSIX : StandardSpec<"POSIX"> {
RetValSpec<IntType>,
[ArgSpec<RestrictedPThreadRWLockTPtr>, ArgSpec<ConstRestrictStructTimeSpecPtr>]
>,
FunctionSpec<
"pthread_rwlock_clockrdlock",
RetValSpec<IntType>,
[ArgSpec<RestrictedPThreadRWLockTPtr>, ArgSpec<ClockIdT>, ArgSpec<ConstRestrictStructTimeSpecPtr>]
>,
FunctionSpec<
"pthread_rwlock_clockwrlock",
RetValSpec<IntType>,
[ArgSpec<RestrictedPThreadRWLockTPtr>, ArgSpec<ClockIdT>, ArgSpec<ConstRestrictStructTimeSpecPtr>]
>,
FunctionSpec<
"pthread_rwlock_rdlock",
RetValSpec<IntType>,
Expand Down
22 changes: 22 additions & 0 deletions libc/src/pthread/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -556,6 +556,28 @@ add_entrypoint_object(
libc.src.__support.threads.linux.rwlock
)

add_entrypoint_object(
pthread_rwlock_clockrdlock
SRCS
pthread_rwlock_clockrdlock.cpp
HDRS
pthread_rwlock_clockrdlock.h
DEPENDS
libc.include.pthread
libc.src.__support.threads.linux.rwlock
)

add_entrypoint_object(
pthread_rwlock_clockwrlock
SRCS
pthread_rwlock_clockwrlock.cpp
HDRS
pthread_rwlock_clockwrlock.h
DEPENDS
libc.include.pthread
libc.src.__support.threads.linux.rwlock
)

add_entrypoint_object(
pthread_rwlock_timedrdlock
SRCS
Expand Down
50 changes: 50 additions & 0 deletions libc/src/pthread/pthread_rwlock_clockrdlock.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
//===-- Implementation of the Rwlock's clockrdlock 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_clockrdlock.h"

#include "hdr/errno_macros.h"
#include "src/__support/common.h"
#include "src/__support/macros/config.h"
#include "src/__support/threads/linux/rwlock.h"

#include <pthread.h>

namespace LIBC_NAMESPACE_DECL {

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_clockrdlock,
(pthread_rwlock_t * rwlock, clockid_t clockid,
const timespec *abstime)) {
if (!rwlock)
return EINVAL;
if (clockid != CLOCK_MONOTONIC && clockid != CLOCK_REALTIME)
return EINVAL;
bool is_realtime = (clockid == CLOCK_REALTIME);
RwLock *rw = reinterpret_cast<RwLock *>(rwlock);
LIBC_ASSERT(abstime && "clockrdlock called with a null timeout");
auto timeout = internal::AbsTimeout::from_timespec(
*abstime, /*is_realtime=*/is_realtime);
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;
}
__builtin_unreachable();
}

} // namespace LIBC_NAMESPACE_DECL
23 changes: 23 additions & 0 deletions libc/src/pthread/pthread_rwlock_clockrdlock.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//===-- Implementation header for Rwlock's clockrdlock 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_CLOCKRDLOCK_H
#define LLVM_LIBC_SRC_PTHREAD_PTHREAD_RWLOCK_CLOCKRDLOCK_H

#include "src/__support/macros/config.h"
#include <pthread.h>

namespace LIBC_NAMESPACE_DECL {

int pthread_rwlock_clockrdlock(pthread_rwlock_t *__restrict rwlock,
clockid_t clockid,
const timespec *__restrict abstime);

} // namespace LIBC_NAMESPACE_DECL

#endif // LLVM_LIBC_SRC_PTHREAD_PTHREAD_RWLOCK_CLOCKRDLOCK_H
51 changes: 51 additions & 0 deletions libc/src/pthread/pthread_rwlock_clockwrlock.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
//===-- Implementation of the Rwlock's clockwrlock 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_clockwrlock.h"

#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/time/linux/abs_timeout.h"

#include <pthread.h>

namespace LIBC_NAMESPACE_DECL {

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_clockwrlock,
(pthread_rwlock_t * rwlock, clockid_t clockid,
const timespec *abstime)) {
if (!rwlock)
return EINVAL;
if (clockid != CLOCK_MONOTONIC && clockid != CLOCK_REALTIME)
return EINVAL;
bool is_realtime = (clockid == CLOCK_REALTIME);
RwLock *rw = reinterpret_cast<RwLock *>(rwlock);
LIBC_ASSERT(abstime && "clockwrlock called with a null timeout");
auto timeout = internal::AbsTimeout::from_timespec(
*abstime, /*is_realtime=*/is_realtime);
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;
}
__builtin_unreachable();
}

} // namespace LIBC_NAMESPACE_DECL
23 changes: 23 additions & 0 deletions libc/src/pthread/pthread_rwlock_clockwrlock.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//===-- Implementation header for Rwlock's clockwrlock 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_CLOCKWRLOCK_H
#define LLVM_LIBC_SRC_PTHREAD_PTHREAD_RWLOCK_CLOCKWRLOCK_H

#include "src/__support/macros/config.h"
#include <pthread.h>

namespace LIBC_NAMESPACE_DECL {

int pthread_rwlock_clockwrlock(pthread_rwlock_t *__restrict rwlock,
clockid_t clockid,
const timespec *__restrict abstime);

} // namespace LIBC_NAMESPACE_DECL

#endif // LLVM_LIBC_SRC_PTHREAD_PTHREAD_RWLOCK_CLOCKWRLOCK_H
2 changes: 2 additions & 0 deletions libc/test/integration/src/pthread/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,11 @@ add_integration_test(
libc.src.pthread.pthread_rwlock_rdlock
libc.src.pthread.pthread_rwlock_tryrdlock
libc.src.pthread.pthread_rwlock_timedrdlock
libc.src.pthread.pthread_rwlock_clockrdlock
libc.src.pthread.pthread_rwlock_wrlock
libc.src.pthread.pthread_rwlock_trywrlock
libc.src.pthread.pthread_rwlock_timedwrlock
libc.src.pthread.pthread_rwlock_clockwrlock
libc.src.pthread.pthread_rwlock_unlock
libc.src.pthread.pthread_create
libc.src.pthread.pthread_join
Expand Down
64 changes: 61 additions & 3 deletions libc/test/integration/src/pthread/pthread_rwlock_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
#include "src/__support/threads/sleep.h"
#include "src/pthread/pthread_create.h"
#include "src/pthread/pthread_join.h"
#include "src/pthread/pthread_rwlock_clockrdlock.h"
#include "src/pthread/pthread_rwlock_clockwrlock.h"
#include "src/pthread/pthread_rwlock_destroy.h"
#include "src/pthread/pthread_rwlock_init.h"
#include "src/pthread/pthread_rwlock_rdlock.h"
Expand Down Expand Up @@ -112,6 +114,12 @@ static void nullptr_test() {
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_clockrdlock(nullptr, CLOCK_MONOTONIC, &ts),
EINVAL);
ASSERT_EQ(
LIBC_NAMESPACE::pthread_rwlock_clockwrlock(nullptr, CLOCK_MONOTONIC, &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);
Expand Down Expand Up @@ -159,16 +167,40 @@ static void unusual_timespec_test() {
timespec ts = {0, -1};
ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_timedrdlock(&rwlock, &ts), EINVAL);
ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_timedwrlock(&rwlock, &ts), EINVAL);
ASSERT_EQ(
LIBC_NAMESPACE::pthread_rwlock_clockrdlock(&rwlock, CLOCK_MONOTONIC, &ts),
EINVAL);
ASSERT_EQ(
LIBC_NAMESPACE::pthread_rwlock_clockwrlock(&rwlock, CLOCK_MONOTONIC, &ts),
EINVAL);
ts.tv_nsec = 1'000'000'000;
ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_timedrdlock(&rwlock, &ts), EINVAL);
ASSERT_EQ(
LIBC_NAMESPACE::pthread_rwlock_clockrdlock(&rwlock, CLOCK_MONOTONIC, &ts),
EINVAL);
ASSERT_EQ(
LIBC_NAMESPACE::pthread_rwlock_clockwrlock(&rwlock, CLOCK_MONOTONIC, &ts),
EINVAL);
ts.tv_nsec += 1;
ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_timedwrlock(&rwlock, &ts), EINVAL);
ASSERT_EQ(
LIBC_NAMESPACE::pthread_rwlock_clockrdlock(&rwlock, CLOCK_MONOTONIC, &ts),
EINVAL);
ASSERT_EQ(
LIBC_NAMESPACE::pthread_rwlock_clockwrlock(&rwlock, CLOCK_MONOTONIC, &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);
ASSERT_EQ(
LIBC_NAMESPACE::pthread_rwlock_clockrdlock(&rwlock, CLOCK_MONOTONIC, &ts),
ETIMEDOUT);
ASSERT_EQ(
LIBC_NAMESPACE::pthread_rwlock_clockwrlock(&rwlock, CLOCK_MONOTONIC, &ts),
ETIMEDOUT);
}

static void timedlock_with_deadlock_test() {
Expand All @@ -184,6 +216,12 @@ static void timedlock_with_deadlock_test() {
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_clockwrlock(&rwlock, CLOCK_MONOTONIC, &ts),
ETIMEDOUT);
ASSERT_EQ(
LIBC_NAMESPACE::pthread_rwlock_clockrdlock(&rwlock, CLOCK_MONOTONIC, &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.
Expand Down Expand Up @@ -270,9 +308,11 @@ enum class Operation : int {
WRITE = 1,
TIMED_READ = 2,
TIMED_WRITE = 3,
TRY_READ = 4,
TRY_WRITE = 5,
COUNT = 6
CLOCK_READ = 4,
CLOCK_WRITE = 5,
TRY_READ = 6,
TRY_WRITE = 7,
COUNT = 8
};

LIBC_NAMESPACE::RawMutex *io_mutex;
Expand Down Expand Up @@ -358,6 +398,24 @@ static void randomized_thread_operation(SharedData *data, ThreadGuard &guard) {
}
break;
}
case Operation::CLOCK_READ: {
timespec ts = get_ts();
if (LIBC_NAMESPACE::pthread_rwlock_clockrdlock(&data->lock, CLOCK_MONOTONIC,
&ts) == 0) {
read_ops();
LIBC_NAMESPACE::pthread_rwlock_unlock(&data->lock);
}
break;
}
case Operation::CLOCK_WRITE: {
timespec ts = get_ts();
if (LIBC_NAMESPACE::pthread_rwlock_clockwrlock(&data->lock, CLOCK_MONOTONIC,
&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();
Expand Down
Loading