Skip to content

reland "[libc] implement cached process/thread identity (#98989)" #99765

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 2 commits into from
Jul 20, 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
10 changes: 10 additions & 0 deletions libc/config/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,16 @@
"doc": "Default size for the constinit freelist buffer used for the freelist malloc implementation (default 1o 1GB)."
}
},
"unistd": {
"LIBC_CONF_ENABLE_TID_CACHE": {
"value": true,
"doc": "Enable caching mechanism for gettid to avoid syscall (only effective in fullbuild mode, default to true). Please refer to Undefined Behavior documentation for implications."
},
"LIBC_CONF_ENABLE_PID_CACHE": {
"value": true,
"doc": "Enable caching mechanism for getpid to avoid syscall (default to true). Please refer to Undefined Behavior documentation for implications."
}
},
"math": {
"LIBC_CONF_MATH_OPTIMIZATIONS": {
"value": 0,
Expand Down
1 change: 1 addition & 0 deletions libc/config/linux/aarch64/entrypoints.txt
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,7 @@ set(TARGET_LIBC_ENTRYPOINTS
libc.src.unistd.geteuid
libc.src.unistd.getpid
libc.src.unistd.getppid
libc.src.unistd.gettid
libc.src.unistd.getuid
libc.src.unistd.isatty
libc.src.unistd.link
Expand Down
1 change: 1 addition & 0 deletions libc/config/linux/riscv/entrypoints.txt
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,7 @@ set(TARGET_LIBC_ENTRYPOINTS
libc.src.unistd.geteuid
libc.src.unistd.getpid
libc.src.unistd.getppid
libc.src.unistd.gettid
libc.src.unistd.getuid
libc.src.unistd.isatty
libc.src.unistd.link
Expand Down
1 change: 1 addition & 0 deletions libc/config/linux/x86_64/entrypoints.txt
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,7 @@ set(TARGET_LIBC_ENTRYPOINTS
libc.src.unistd.geteuid
libc.src.unistd.getpid
libc.src.unistd.getppid
libc.src.unistd.gettid
libc.src.unistd.getuid
libc.src.unistd.isatty
libc.src.unistd.link
Expand Down
3 changes: 3 additions & 0 deletions libc/docs/configure.rst
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,6 @@ to learn about the defaults for your platform and target.
* **"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.
- ``LIBC_CONF_STRING_UNSAFE_WIDE_READ``: Read more than a byte at a time to perform byte-string operations like strlen.
* **"unistd" options**
- ``LIBC_CONF_ENABLE_PID_CACHE``: Enable caching mechanism for getpid to avoid syscall (default to true). Please refer to Undefined Behavior documentation for implications.
- ``LIBC_CONF_ENABLE_TID_CACHE``: Enable caching mechanism for gettid to avoid syscall (only effective in fullbuild mode, default to true). Please refer to Undefined Behavior documentation for implications.
23 changes: 23 additions & 0 deletions libc/docs/dev/undefined_behavior.rst
Original file line number Diff line number Diff line change
Expand Up @@ -93,3 +93,26 @@ 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.

Cached ``getpid/gettid``
------------------------
Since version ``2.25``, glibc removes its cache mechanism for ``getpid/gettid``
(See the history section in https://man7.org/linux/man-pages/man2/getpid.2.html).
LLVM's libc still implements the cache as it is useful for fast deadlock detection.
The cache mechanism is also implemented in MUSL and bionic. The tid/pid cache can
be disabled by setting ``LIBC_CONF_ENABLE_TID_CACHE`` and ``LIBC_CONF_ENABLE_PID_CACHE``
to ``false`` respectively.

Unwrapped ``SYS_clone/SYS_fork/SYS_vfork``
------------------------------------------
It is highly discouraged to use unwrapped ``SYS_clone/SYS_fork/SYS_vfork``.
First, calling such syscalls without provided libc wrappers ignores
all the ``pthread_atfork`` entries as libc can no longer detect the ``fork``.
Second, libc relies on the ``fork/clone`` wrappers to correctly maintain cache for
process id and thread id, and other important process-specific states such as the list
of robust mutexes. Third, even if the user is to call ``exec*`` functions immediately,
there can still be other unexpected issues. For instance, there can be signal handlers
inherited from parent process triggered inside the instruction window between ``fork``
and ``exec*``. As libc failed to maintain its internal states correctly, even though the
functions used inside the signal handlers are marked as ``async-signal-safe`` (such as
``getpid``), they will still return wrong values or lead to other even worse situations.
15 changes: 5 additions & 10 deletions libc/spec/posix.td
Original file line number Diff line number Diff line change
Expand Up @@ -546,6 +546,11 @@ def POSIX : StandardSpec<"POSIX"> {
RetValSpec<PidT>,
[ArgSpec<VoidType>]
>,
FunctionSpec<
"gettid",
RetValSpec<PidT>,
[ArgSpec<VoidType>]
>,
FunctionSpec<
"getuid",
RetValSpec<UidT>,
Expand Down Expand Up @@ -601,16 +606,6 @@ def POSIX : StandardSpec<"POSIX"> {
RetValSpec<IntType>,
[ArgSpec<ConstCharPtr>]
>,
FunctionSpec<
"getpid",
RetValSpec<IntType>,
[ArgSpec<VoidType>]
>,
FunctionSpec<
"getppid",
RetValSpec<IntType>,
[ArgSpec<VoidType>]
>,
FunctionSpec<
"link",
RetValSpec<IntType>,
Expand Down
17 changes: 17 additions & 0 deletions libc/src/__support/OSUtil/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,20 @@ add_object_library(
DEPENDS
${target_os_util}
)

if (LIBC_CONF_ENABLE_PID_CACHE)
set(libc_copt_enable_pid_cache 1)
else()
set(libc_copt_enable_pid_cache 0)
endif()

if(TARGET libc.src.__support.OSUtil.${LIBC_TARGET_OS}.pid)
add_object_library(
pid
ALIAS
DEPENDS
.${LIBC_TARGET_OS}.pid
COMPILE_OPTIONS
-DLIBC_COPT_ENABLE_PID_CACHE=${libc_copt_enable_pid_cache}
)
endif()
13 changes: 13 additions & 0 deletions libc/src/__support/OSUtil/linux/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,16 @@ add_object_library(
libc.hdr.types.struct_f_owner_ex
libc.hdr.types.off_t
)

add_object_library(
pid
SRCS
pid.cpp
HDRS
../pid.h
DEPENDS
libc.src.__support.OSUtil.osutil
libc.src.__support.common
libc.hdr.types.pid_t
libc.include.sys_syscall
)
20 changes: 20 additions & 0 deletions libc/src/__support/OSUtil/linux/pid.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//===------------ pid_t utilities implementation ----------------*- 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
//
//===----------------------------------------------------------------------===//

#include "src/__support/OSUtil/pid.h"
#include "src/__support/OSUtil/syscall.h"
#include <sys/syscall.h>

namespace LIBC_NAMESPACE_DECL {

pid_t ProcessIdentity::cache = -1;
pid_t ProcessIdentity::get_uncached() {
return syscall_impl<pid_t>(SYS_getpid);
}

} // namespace LIBC_NAMESPACE_DECL
41 changes: 41 additions & 0 deletions libc/src/__support/OSUtil/pid.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
//===------------ pid_t utilities -------------------------------*- 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_OSUTIL_PID_H
#define LLVM_LIBC_SRC___SUPPORT_OSUTIL_PID_H
#include "hdr/types/pid_t.h"
#include "src/__support/macros/attributes.h"
#include "src/__support/macros/optimization.h"

#ifndef LIBC_COPT_ENABLE_PID_CACHE
#define LIBC_COPT_ENABLE_PID_CACHE 1
#endif

namespace LIBC_NAMESPACE_DECL {

class ProcessIdentity {
static LIBC_INLINE_VAR thread_local bool fork_inflight = true;
static pid_t cache;
static pid_t get_uncached();

public:
LIBC_INLINE static void start_fork() { fork_inflight = true; }
LIBC_INLINE static void end_fork() { fork_inflight = false; }
LIBC_INLINE static void refresh_cache() { cache = get_uncached(); }
LIBC_INLINE static pid_t get() {
#if LIBC_COPT_ENABLE_PID_CACHE
if (LIBC_LIKELY(!fork_inflight))
return cache;
#endif
return get_uncached();
}
};

} // namespace LIBC_NAMESPACE_DECL

#endif // LLVM_LIBC_SRC___SUPPORT_OSUTIL_PID_H
27 changes: 27 additions & 0 deletions libc/src/__support/threads/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,12 @@ if(TARGET libc.src.__support.threads.${LIBC_TARGET_OS}.mutex)
)
endif()

if (LIBC_CONF_ENABLE_TID_CACHE)
set(libc_copt_enable_tid_cache 1)
else()
set(libc_copt_enable_tid_cache 0)
endif()

add_header_library(
thread_common
HDRS
Expand All @@ -54,6 +60,9 @@ add_header_library(
libc.src.__support.CPP.optional
libc.src.__support.CPP.string_view
libc.src.__support.CPP.stringstream
libc.hdr.types.pid_t
COMPILE_OPTIONS
-DLIBC_COPT_ENABLE_TID_CACHE=${libc_copt_enable_tid_cache}
)

if(TARGET libc.src.__support.threads.${LIBC_TARGET_OS}.thread)
Expand Down Expand Up @@ -89,3 +98,21 @@ if(TARGET libc.src.__support.threads.${LIBC_TARGET_OS}.CndVar)
.${LIBC_TARGET_OS}.CndVar
)
endif()

set(tid_dep)
if (LLVM_LIBC_FULL_BUILD)
list(APPEND tid_dep libc.src.__support.thread)
else()
list(APPEND tid_dep libc.src.__support.OSUtil.osutil)
list(APPEND tid_dep libc.include.sys_syscall)
endif()

add_header_library(
tid
HDRS
tid.h
DEPENDS
libc.src.__support.common
libc.hdr.types.pid_t
${tid_dep}
)
1 change: 1 addition & 0 deletions libc/src/__support/threads/linux/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ add_header_library(
libc.src.__support.common
libc.src.__support.OSUtil.osutil
libc.src.__support.CPP.limits
libc.src.__support.threads.tid
COMPILE_OPTIONS
-DLIBC_COPT_RWLOCK_DEFAULT_SPIN_COUNT=${LIBC_CONF_RWLOCK_DEFAULT_SPIN_COUNT}
${monotonicity_flags}
Expand Down
9 changes: 4 additions & 5 deletions libc/src/__support/threads/linux/rwlock.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "src/__support/threads/linux/futex_word.h"
#include "src/__support/threads/linux/raw_mutex.h"
#include "src/__support/threads/sleep.h"
#include "src/__support/threads/tid.h"

#ifndef LIBC_COPT_RWLOCK_DEFAULT_SPIN_COUNT
#define LIBC_COPT_RWLOCK_DEFAULT_SPIN_COUNT 100
Expand Down Expand Up @@ -336,8 +337,6 @@ class RwLock {
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) {
Expand All @@ -359,7 +358,7 @@ class RwLock {
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);
writer_tid.store(gettid_inline(), cpp::MemoryOrder::RELAXED);
return LockResult::Success;
}
// Notice that old is updated by the compare_exchange_weak_with
Expand Down Expand Up @@ -394,7 +393,7 @@ class RwLock {
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())
if (writer_tid.load(cpp::MemoryOrder::RELAXED) == gettid_inline())
return LockResult::Deadlock;

#if LIBC_COPT_TIMEOUT_ENSURE_MONOTONICITY
Expand Down Expand Up @@ -520,7 +519,7 @@ class RwLock {
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())
if (writer_tid.load(cpp::MemoryOrder::RELAXED) != gettid_inline())
return LockResult::PermissionDenied;
// clear writer tid.
writer_tid.store(0, cpp::MemoryOrder::RELAXED);
Expand Down
2 changes: 2 additions & 0 deletions libc/src/__support/threads/linux/thread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -518,4 +518,6 @@ void thread_exit(ThreadReturnValue retval, ThreadStyle style) {
__builtin_unreachable();
}

pid_t Thread::get_uncached_tid() { return syscall_impl<pid_t>(SYS_gettid); }

} // namespace LIBC_NAMESPACE_DECL
27 changes: 26 additions & 1 deletion libc/src/__support/threads/thread.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@
#ifndef LLVM_LIBC_SRC___SUPPORT_THREADS_THREAD_H
#define LLVM_LIBC_SRC___SUPPORT_THREADS_THREAD_H

#ifndef LIBC_COPT_ENABLE_TID_CACHE
#define LIBC_COPT_ENABLE_TID_CACHE 1
#endif

#include "hdr/types/pid_t.h"
#include "src/__support/CPP/atomic.h"
#include "src/__support/CPP/optional.h"
#include "src/__support/CPP/string_view.h"
Expand Down Expand Up @@ -103,7 +108,7 @@ struct alignas(STACK_ALIGNMENT) ThreadAttributes {
uintptr_t tls; // Address to the thread TLS memory
uintptr_t tls_size; // The size of area pointed to by |tls|.
unsigned char owned_stack; // Indicates if the thread owns this stack memory
int tid;
pid_t tid;
ThreadStyle style;
ThreadReturnValue retval;
ThreadAtExitCallbackMgr *atexit_callback_mgr;
Expand Down Expand Up @@ -228,6 +233,26 @@ struct Thread {

// Return the name of the thread in |name|. Return the error number of error.
int get_name(cpp::StringStream &name) const;

static pid_t get_uncached_tid();

LIBC_INLINE void refresh_tid(pid_t cached = -1) {
if (cached >= 0)
this->attrib->tid = cached;
else
this->attrib->tid = get_uncached_tid();
}
LIBC_INLINE void invalidate_tid() { this->attrib->tid = -1; }

LIBC_INLINE pid_t get_tid() {
#if LIBC_COPT_ENABLE_TID_CACHE
if (LIBC_UNLIKELY(this->attrib->tid < 0))
return get_uncached_tid();
return this->attrib->tid;
#else
return get_uncached_tid();
#endif
}
};

extern LIBC_THREAD_LOCAL Thread self;
Expand Down
34 changes: 34 additions & 0 deletions libc/src/__support/threads/tid.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
//===--- Tid wrapper --------------------------------------------*- 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_TID_H
#define LLVM_LIBC_SRC___SUPPORT_THREADS_TID_H

// This header is for internal usage which automatically dispatches full build
// and overlay build behaviors.

#include "hdr/types/pid_t.h"
#include "src/__support/common.h"
#ifdef LIBC_FULL_BUILD
#include "src/__support/threads/thread.h"
#else
#include "src/__support/OSUtil/syscall.h"
#include <sys/syscall.h>
#endif // LIBC_FULL_BUILD

namespace LIBC_NAMESPACE_DECL {
LIBC_INLINE pid_t gettid_inline() {
#ifdef LIBC_FULL_BUILD
return self.get_tid();
#else
return syscall_impl<pid_t>(SYS_gettid);
#endif
}
} // namespace LIBC_NAMESPACE_DECL

#endif // LLVM_LIBC_SRC___SUPPORT_THREADS_TID_H
Loading
Loading