Skip to content

Commit 74ed72e

Browse files
committed
[libc][darwin] use os_sync_*_timeouts and clear tests
1 parent 4cf8557 commit 74ed72e

File tree

3 files changed

+65
-31
lines changed

3 files changed

+65
-31
lines changed

libc/src/__support/threads/darwin/futex_utils.h

Lines changed: 42 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
1-
//===--- Futex utils for Darwin -----------------------------------*- C++
2-
//-*-===//
1+
//===--- Futex utils for Darwin ------------------------*- C++-*-===//
32
//
43
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
54
// See https://llvm.org/LICENSE.txt for license information.
65
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
76
//
8-
//===----------------------------------------------------------------------===//
7+
//===------------------------------------------------------------===//
98

109
#ifndef LLVM_LIBC_SRC___SUPPORT_THREADS_DARWIN_FUTEX_UTILS_H
1110
#define LLVM_LIBC_SRC___SUPPORT_THREADS_DARWIN_FUTEX_UTILS_H
@@ -25,39 +24,52 @@ struct Futex : public cpp::Atomic<FutexWordType> {
2524
using cpp::Atomic<FutexWordType>::Atomic;
2625
using Timeout = internal::AbsTimeout;
2726

28-
// The Darwin futex API does not return a value on timeout, so we have to
29-
// check for it manually. This means we can't use the return value to
30-
// distinguish between a timeout and a successful wake-up.
31-
int wait(FutexWordType val, cpp::optional<Timeout> timeout, bool) {
32-
if (timeout) {
33-
struct timespec now;
34-
clock_gettime(timeout->is_realtime() ? CLOCK_REALTIME : CLOCK_MONOTONIC,
35-
&now);
36-
const timespec &target_ts = timeout->get_timespec();
27+
LIBC_INLINE long wait(FutexWordType val, cpp::optional<Timeout> timeout,
28+
bool /* is_shared */) {
29+
// TODO(bojle): consider using OS_SYNC_WAIT_ON_ADDRESS_SHARED to sync
30+
// betweeen processes. Catch: it is recommended to only be used by shared
31+
// processes, not threads of a same process.
3732

38-
if (now.tv_sec > target_ts.tv_sec ||
39-
(now.tv_sec == target_ts.tv_sec && now.tv_nsec >= target_ts.tv_nsec))
40-
return ETIMEDOUT;
33+
for (;;) {
34+
if (this->load(cpp::MemoryOrder::RELAXED) != val)
35+
return 0;
36+
long ret = 0;
37+
if (timeout) {
38+
// Assuming, OS_CLOCK_MACH_ABSOLUTE_TIME is equivalent to CLOCK_REALTIME
39+
uint64_t tnsec = timeout->get_timespec().tv_sec * 1000000000 +
40+
timeout->get_timespec().tv_nsec;
41+
ret = os_sync_wait_on_address_with_timeout(
42+
reinterpret_cast<void *>(this), static_cast<uint64_t>(val),
43+
sizeof(FutexWordType), OS_SYNC_WAIT_ON_ADDRESS_NONE,
44+
OS_CLOCK_MACH_ABSOLUTE_TIME, tnsec);
45+
} else {
46+
ret = os_sync_wait_on_address(
47+
reinterpret_cast<void *>(this), static_cast<uint64_t>(val),
48+
sizeof(FutexWordType), OS_SYNC_WAIT_ON_ADDRESS_NONE);
49+
}
50+
if ((ret < 0) && (errno == ETIMEDOUT)) {
51+
return -ETIMEDOUT;
52+
}
53+
// case when os_sync returns early with an error. retry.
54+
if ((ret < 0) && ((errno == EINTR) || (errno == EFAULT))) {
55+
continue;
56+
}
57+
return ret;
4158
}
42-
43-
os_sync_wait_on_address(reinterpret_cast<void *>(this),
44-
static_cast<uint64_t>(val), sizeof(FutexWordType),
45-
OS_SYNC_WAIT_ON_ADDRESS_NONE);
46-
return 0;
4759
}
4860

49-
void notify_one(bool) {
50-
os_sync_wake_by_address_any(reinterpret_cast<void *>(this),
51-
sizeof(FutexWordType),
52-
OS_SYNC_WAKE_BY_ADDRESS_NONE);
61+
LIBC_INLINE long notify_one(bool /* is_shared */) {
62+
// TODO(bojle): deal with is_shared
63+
return os_sync_wake_by_address_any(reinterpret_cast<void *>(this),
64+
sizeof(FutexWordType),
65+
OS_SYNC_WAKE_BY_ADDRESS_NONE);
5366
}
5467

55-
void notify_all(bool) {
56-
// os_sync_wake_by_address_all is not available, so we use notify_one.
57-
// This is not ideal, but it's the best we can do with the available API.
58-
os_sync_wake_by_address_any(reinterpret_cast<void *>(this),
59-
sizeof(FutexWordType),
60-
OS_SYNC_WAKE_BY_ADDRESS_NONE);
68+
LIBC_INLINE long notify_all(bool /* is_shared */) {
69+
// TODO(bojle): deal with is_shared
70+
return os_sync_wake_by_address_all(reinterpret_cast<void *>(this),
71+
sizeof(FutexWordType),
72+
OS_SYNC_WAKE_BY_ADDRESS_NONE);
6173
}
6274
};
6375

libc/src/__support/threads/raw_mutex.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
#include "src/__support/time/abs_timeout.h"
1919
#include "sys/errno.h"
2020

21+
#include <stdio.h>
22+
2123
#if defined(__linux__)
2224
#include "src/__support/threads/linux/futex_utils.h"
2325
#elif defined(__APPLE__)

libc/test/src/__support/threads/darwin/mutex_test.cpp

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313

1414
TEST(LlvmLibcSupportThreadsMutexTest, SmokeTest) {
1515
LIBC_NAMESPACE::Mutex mutex(0, 0, 0, 0);
16-
;
1716
ASSERT_EQ(mutex.lock(), LIBC_NAMESPACE::MutexError::NONE);
1817
ASSERT_EQ(mutex.unlock(), LIBC_NAMESPACE::MutexError::NONE);
1918
ASSERT_EQ(mutex.try_lock(), LIBC_NAMESPACE::MutexError::NONE);
@@ -22,4 +21,25 @@ TEST(LlvmLibcSupportThreadsMutexTest, SmokeTest) {
2221
ASSERT_EQ(mutex.unlock(), LIBC_NAMESPACE::MutexError::UNLOCK_WITHOUT_LOCK);
2322
}
2423

24+
TEST(LlvmLibcSupportThreadsRawMutexTest, Timeout) {
25+
LIBC_NAMESPACE::RawMutex mutex;
26+
ASSERT_TRUE(mutex.lock());
27+
timespec ts;
28+
LIBC_NAMESPACE::internal::clock_gettime(CLOCK_MONOTONIC, &ts);
29+
ts.tv_sec += 1;
30+
// Timeout will be respected when deadlock happens.
31+
auto timeout = LIBC_NAMESPACE::internal::AbsTimeout::from_timespec(ts, false);
32+
ASSERT_TRUE(timeout.has_value());
33+
// The following will timeout
34+
ASSERT_FALSE(mutex.lock(*timeout));
35+
ASSERT_TRUE(mutex.unlock());
36+
// Test that the mutex works after the timeout.
37+
ASSERT_TRUE(mutex.lock());
38+
ASSERT_TRUE(mutex.unlock());
39+
// If a lock can be acquired directly, expired timeout will not count.
40+
// Notice that the timeout is already reached during preivous deadlock.
41+
ASSERT_TRUE(mutex.lock(*timeout));
42+
ASSERT_TRUE(mutex.unlock());
43+
}
44+
2545
// TODO(bojle): add other tests a la linux

0 commit comments

Comments
 (0)