Skip to content
Open
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
4 changes: 3 additions & 1 deletion libc/hdr/errno_macros.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@
#include <linux/errno.h>

#include "include/llvm-libc-macros/error-number-macros.h"
#else // __linux__
#elif __APPLE__
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

defined(__APPLE__)

#include <sys/errno.h>
#else // __APPLE__
#include "include/llvm-libc-macros/generic-error-number-macros.h"
#endif

Expand Down
8 changes: 7 additions & 1 deletion libc/include/errno.h.def
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,14 @@

#include "llvm-libc-macros/linux/error-number-macros.h"

#else // __linux__
#elif __APPLE__
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

defined(__APPLE__)


#include <sys/errno.h>

#else // __APPLE__

#include "llvm-libc-macros/generic-error-number-macros.h"

#endif

__BEGIN_C_DECLS
Expand Down
29 changes: 26 additions & 3 deletions libc/src/__support/threads/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,36 @@ if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS})
add_subdirectory(${LIBC_TARGET_OS})
endif()

if(TARGET libc.src.__support.threads.${LIBC_TARGET_OS}.mutex)
if(TARGET libc.src.__support.threads.${LIBC_TARGET_OS}.futex_utils)
add_header_library(
mutex
HDRS
mutex.h
mutex.h
DEPENDS
.unix_mutex
)
add_header_library(
unix_mutex
HDRS
unix_mutex.h
DEPENDS
.raw_mutex
)
Comment on lines +34 to +40
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move this above mutex


add_header_library(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

move this above unix_mutex

raw_mutex
HDRS
raw_mutex.h
DEPENDS
.${LIBC_TARGET_OS}.mutex
.${LIBC_TARGET_OS}.futex_utils
libc.src.__support.threads.sleep
libc.src.__support.time.abs_timeout
libc.src.__support.time.monotonicity
libc.src.__support.CPP.optional
libc.hdr.types.pid_t
COMPILE_OPTIONS
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

move COMPILE_OPTIONS above DEPENDS.

-DLIBC_COPT_RAW_MUTEX_DEFAULT_SPIN_COUNT=${LIBC_CONF_RAW_MUTEX_DEFAULT_SPIN_COUNT}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

${monotonicity_flags}
)

add_object_library(
Expand Down
16 changes: 16 additions & 0 deletions libc/src/__support/threads/darwin/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
if(NOT TARGET libc.src.__support.OSUtil.osutil)
return()
endif()

add_header_library(
futex_utils
HDRS
futex_utils.h
DEPENDS
libc.include.sys_syscall
libc.src.__support.OSUtil.osutil
libc.src.__support.CPP.atomic
libc.src.__support.CPP.limits
libc.src.__support.CPP.optional
libc.src.__support.threads.mutex_common
)
78 changes: 78 additions & 0 deletions libc/src/__support/threads/darwin/futex_utils.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
//===--- Futex utils for Darwin ------------------------*- C++-*-===//
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The license header line length seems strange. It should be 80-characters long in total.

//
// 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_DARWIN_FUTEX_UTILS_H
#define LLVM_LIBC_SRC___SUPPORT_THREADS_DARWIN_FUTEX_UTILS_H

#include "src/__support/CPP/atomic.h"
#include "src/__support/CPP/optional.h"
#include "src/__support/time/abs_timeout.h"
#include "src/__support/time/clock_conversion.h"

#include <os/os_sync_wait_on_address.h>

namespace LIBC_NAMESPACE_DECL {

using FutexWordType = uint32_t;

struct Futex : public cpp::Atomic<FutexWordType> {
using cpp::Atomic<FutexWordType>::Atomic;
using Timeout = internal::AbsTimeout;

LIBC_INLINE long wait(FutexWordType val, cpp::optional<Timeout> timeout,
bool /* is_shared */) {
// TODO(bojle): consider using OS_SYNC_WAIT_ON_ADDRESS_SHARED to sync
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So the document says:

Use this flag when you pass an address allocated in shared memory to any futex wait function. Shared memory can be used for waits and wakes within a single process, but incur a performance hit.

This extra cost is expected and it is the reason why POSIX API also distinguish shared memory locks and normal private locks.

// betweeen processes. Catch: it is recommended to only be used by shared
// processes, not threads of a same process.

for (;;) {
if (this->load(cpp::MemoryOrder::RELAXED) != val)
return 0;
long ret = 0;
if (timeout) {
// Assuming, OS_CLOCK_MACH_ABSOLUTE_TIME is equivalent to CLOCK_REALTIME
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this assumption due to we have not port time conversion yet in this PR?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No. I couldn't find a reference stating what "absolute" means (in the macos context). Is it closer to CLOCK_REALTIME or CLOCK_MONOTONIC? The assumption is based on semantic similarity where I thought it could mean realtime more than monotonic.

uint64_t tnsec = timeout->get_timespec().tv_sec * 1000000000 +
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

try use existing unit conversion utilities if possible

timeout->get_timespec().tv_nsec;
ret = os_sync_wait_on_address_with_timeout(
reinterpret_cast<void *>(this), static_cast<uint64_t>(val),
sizeof(FutexWordType), OS_SYNC_WAIT_ON_ADDRESS_NONE,
OS_CLOCK_MACH_ABSOLUTE_TIME, tnsec);
} else {
ret = os_sync_wait_on_address(
reinterpret_cast<void *>(this), static_cast<uint64_t>(val),
sizeof(FutexWordType), OS_SYNC_WAIT_ON_ADDRESS_NONE);
}
if ((ret < 0) && (errno == ETIMEDOUT)) {
return -ETIMEDOUT;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove extra brace to conform LLVM's guide line.

}
// case when os_sync returns early with an error. retry.
if ((ret < 0) && ((errno == EINTR) || (errno == EFAULT))) {
continue;
}
return ret;
}
}

LIBC_INLINE long notify_one(bool /* is_shared */) {
// TODO(bojle): deal with is_shared
return os_sync_wake_by_address_any(reinterpret_cast<void *>(this),
sizeof(FutexWordType),
OS_SYNC_WAKE_BY_ADDRESS_NONE);
}

LIBC_INLINE long notify_all(bool /* is_shared */) {
// TODO(bojle): deal with is_shared
return os_sync_wake_by_address_all(reinterpret_cast<void *>(this),
sizeof(FutexWordType),
OS_SYNC_WAKE_BY_ADDRESS_NONE);
}
};

} // namespace LIBC_NAMESPACE_DECL

#endif // LLVM_LIBC_SRC___SUPPORT_THREADS_DARWIN_FUTEX_UTILS_H
131 changes: 131 additions & 0 deletions libc/src/__support/threads/darwin/mutex.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
//===--- Implementation of a Darwin mutex class -----------*- C++-*-===//
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer to lift POSIX/UNIX mutex to a shared design instead of making this platform specific, as most parts of the design should still be the same.

If constants/headers are different, you can add platform macro guards.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Understood. I was uncertain as to the extent of generality we were looking for. I'll take this as a signal to make things as general as they can be (starting with moving mutex.h out of os folders) and into __support/thread/.

//
// 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_DARWIN_MUTEX_H
#define LLVM_LIBC_SRC___SUPPORT_THREADS_DARWIN_MUTEX_H

#include "src/__support/libc_assert.h"
#include "src/__support/macros/config.h"
#include "src/__support/threads/mutex_common.h"
#include "src/__support/threads/sleep.h" // For sleep_briefly
#include "src/__support/time/abs_timeout.h"

#include <mach/mach_init.h> // For mach_thread_self
#include <mach/mach_port.h> // For mach_port_t and MACH_PORT_NULL
#include <os/lock.h> // For os_unfair_lock
#include <time.h> // For clock_gettime

namespace LIBC_NAMESPACE_DECL {

// This file is an implementation of `LIBC_NAMESPACE::mutex` for Darwin-based
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I guess this implementation can be removed as futex-like lock is now ready to use?

// platforms. It is a wrapper around `os_unfair_lock`, which is a low-level,
// high-performance locking primitive provided by the kernel.
//
// `os_unfair_lock` is a non-recursive, thread-owned lock that blocks waiters
// efficiently in the kernel. As the name implies, it is "unfair," meaning
// it does not guarantee the order in which waiting threads acquire the lock.
// This trade-off allows for higher performance in contended scenarios.
//
// The lock must be unlocked from the same thread that locked it. Attempting
// to unlock from a different thread will result in a runtime error.
//
// This implementation is suitable for simple critical sections where fairness
// and reentrancy are not concerns.

class Mutex final {
os_unfair_lock_s lock_val = OS_UNFAIR_LOCK_INIT;
mach_port_t owner = MACH_PORT_NULL;

// API compatibility fields.
unsigned char timed;
unsigned char recursive;
unsigned char robust;
unsigned char pshared;

public:
LIBC_INLINE constexpr Mutex(bool is_timed, bool is_recursive, bool is_robust,
bool is_pshared)
: owner(MACH_PORT_NULL), timed(is_timed), recursive(is_recursive),
robust(is_robust), pshared(is_pshared) {}

LIBC_INLINE constexpr Mutex()
: owner(MACH_PORT_NULL), timed(0), recursive(0), robust(0), pshared(0) {}

LIBC_INLINE static MutexError init(Mutex *mutex, bool is_timed, bool is_recur,
bool is_robust, bool is_pshared) {
mutex->lock_val = OS_UNFAIR_LOCK_INIT;
mutex->owner = MACH_PORT_NULL;
mutex->timed = is_timed;
mutex->recursive = is_recur;
mutex->robust = is_robust;
mutex->pshared = is_pshared;
return MutexError::NONE;
}

LIBC_INLINE static MutexError destroy(Mutex *lock) {
LIBC_ASSERT(lock->owner == MACH_PORT_NULL &&
"Mutex destroyed while locked.");
return MutexError::NONE;
}

LIBC_INLINE MutexError lock() {
os_unfair_lock_lock(&lock_val);
owner = mach_thread_self();
return MutexError::NONE;
}

LIBC_INLINE MutexError timed_lock(internal::AbsTimeout abs_time) {
while (true) {
if (try_lock() == MutexError::NONE) {
return MutexError::NONE;
}

// Manually check if the timeout has expired.
struct timespec now;
// The clock used here must match the clock used to create the
// absolute timeout.
clock_gettime(abs_time.is_realtime() ? CLOCK_REALTIME : CLOCK_MONOTONIC,
&now);
const timespec &target_ts = abs_time.get_timespec();

if (now.tv_sec > target_ts.tv_sec || (now.tv_sec == target_ts.tv_sec &&
now.tv_nsec >= target_ts.tv_nsec)) {
// We might have acquired the lock between the last try_lock() and now.
// To avoid returning TIMEOUT incorrectly, we do one last try_lock().
if (try_lock() == MutexError::NONE)
return MutexError::NONE;
return MutexError::TIMEOUT;
}

sleep_briefly();
}
}

LIBC_INLINE MutexError unlock() {
// This check is crucial. It prevents both double-unlocks and unlocks
// by threads that do not own the mutex.
if (owner != mach_thread_self()) {
return MutexError::UNLOCK_WITHOUT_LOCK;
}
owner = MACH_PORT_NULL;
os_unfair_lock_unlock(&lock_val);
return MutexError::NONE;
}

LIBC_INLINE MutexError try_lock() {
if (os_unfair_lock_trylock(&lock_val)) {
owner = mach_thread_self();
return MutexError::NONE;
}
return MutexError::BUSY;
}
};

} // namespace LIBC_NAMESPACE_DECL

#endif // LLVM_LIBC_SRC___SUPPORT_THREADS_DARWIN_MUTEX_H
6 changes: 3 additions & 3 deletions libc/src/__support/threads/linux/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ add_header_library(
libc.src.__support.CPP.atomic
libc.src.__support.CPP.limits
libc.src.__support.CPP.optional
libc.src.__support.time.linux.abs_timeout
libc.src.__support.time.abs_timeout
)

set(monotonicity_flags)
Expand All @@ -38,8 +38,8 @@ add_header_library(
DEPENDS
.futex_utils
libc.src.__support.threads.sleep
libc.src.__support.time.linux.abs_timeout
libc.src.__support.time.linux.monotonicity
libc.src.__support.time.abs_timeout
libc.src.__support.time.monotonicity
libc.src.__support.CPP.optional
libc.hdr.types.pid_t
COMPILE_OPTIONS
Expand Down
2 changes: 1 addition & 1 deletion libc/src/__support/threads/linux/futex_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
#include "src/__support/macros/attributes.h"
#include "src/__support/macros/config.h"
#include "src/__support/threads/linux/futex_word.h"
#include "src/__support/time/linux/abs_timeout.h"
#include "src/__support/time/abs_timeout.h"
#include <linux/errno.h>
#include <linux/futex.h>

Expand Down
2 changes: 1 addition & 1 deletion libc/src/__support/threads/linux/rwlock.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
#endif

#if LIBC_COPT_TIMEOUT_ENSURE_MONOTONICITY
#include "src/__support/time/linux/monotonicity.h"
#include "src/__support/time/monotonicity.h"
#endif

namespace LIBC_NAMESPACE_DECL {
Expand Down
6 changes: 3 additions & 3 deletions libc/src/__support/threads/mutex.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,9 @@
// few global locks. So, to avoid static initialization order fiasco, we
// want the constructors of the Mutex classes to be constexprs.

#if defined(__linux__)
#include "src/__support/threads/linux/mutex.h"
#endif // __linux__
#if defined(__linux__) || defined(__APPLE__)
#include "src/__support/threads/unix_mutex.h"
#endif

#elif LIBC_THREAD_MODE == LIBC_THREAD_MODE_SINGLE

Expand Down
Loading
Loading