Skip to content

Commit 29be889

Browse files
reland "[libc] implement cached process/thread identity (#98989)" (#99765)
1 parent afbfb16 commit 29be889

30 files changed

+363
-40
lines changed

libc/config/config.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,16 @@
7777
"doc": "Default size for the constinit freelist buffer used for the freelist malloc implementation (default 1o 1GB)."
7878
}
7979
},
80+
"unistd": {
81+
"LIBC_CONF_ENABLE_TID_CACHE": {
82+
"value": true,
83+
"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."
84+
},
85+
"LIBC_CONF_ENABLE_PID_CACHE": {
86+
"value": true,
87+
"doc": "Enable caching mechanism for getpid to avoid syscall (default to true). Please refer to Undefined Behavior documentation for implications."
88+
}
89+
},
8090
"math": {
8191
"LIBC_CONF_MATH_OPTIMIZATIONS": {
8292
"value": 0,

libc/config/linux/aarch64/entrypoints.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,7 @@ set(TARGET_LIBC_ENTRYPOINTS
297297
libc.src.unistd.geteuid
298298
libc.src.unistd.getpid
299299
libc.src.unistd.getppid
300+
libc.src.unistd.gettid
300301
libc.src.unistd.getuid
301302
libc.src.unistd.isatty
302303
libc.src.unistd.link

libc/config/linux/riscv/entrypoints.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,7 @@ set(TARGET_LIBC_ENTRYPOINTS
296296
libc.src.unistd.geteuid
297297
libc.src.unistd.getpid
298298
libc.src.unistd.getppid
299+
libc.src.unistd.gettid
299300
libc.src.unistd.getuid
300301
libc.src.unistd.isatty
301302
libc.src.unistd.link

libc/config/linux/x86_64/entrypoints.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,7 @@ set(TARGET_LIBC_ENTRYPOINTS
315315
libc.src.unistd.geteuid
316316
libc.src.unistd.getpid
317317
libc.src.unistd.getppid
318+
libc.src.unistd.gettid
318319
libc.src.unistd.getuid
319320
libc.src.unistd.isatty
320321
libc.src.unistd.link

libc/docs/configure.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,3 +52,6 @@ to learn about the defaults for your platform and target.
5252
* **"string" options**
5353
- ``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.
5454
- ``LIBC_CONF_STRING_UNSAFE_WIDE_READ``: Read more than a byte at a time to perform byte-string operations like strlen.
55+
* **"unistd" options**
56+
- ``LIBC_CONF_ENABLE_PID_CACHE``: Enable caching mechanism for getpid to avoid syscall (default to true). Please refer to Undefined Behavior documentation for implications.
57+
- ``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.

libc/docs/dev/undefined_behavior.rst

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,3 +93,26 @@ direction in this case.
9393
Non-const Constant Return Values
9494
--------------------------------
9595
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.
96+
97+
Cached ``getpid/gettid``
98+
------------------------
99+
Since version ``2.25``, glibc removes its cache mechanism for ``getpid/gettid``
100+
(See the history section in https://man7.org/linux/man-pages/man2/getpid.2.html).
101+
LLVM's libc still implements the cache as it is useful for fast deadlock detection.
102+
The cache mechanism is also implemented in MUSL and bionic. The tid/pid cache can
103+
be disabled by setting ``LIBC_CONF_ENABLE_TID_CACHE`` and ``LIBC_CONF_ENABLE_PID_CACHE``
104+
to ``false`` respectively.
105+
106+
Unwrapped ``SYS_clone/SYS_fork/SYS_vfork``
107+
------------------------------------------
108+
It is highly discouraged to use unwrapped ``SYS_clone/SYS_fork/SYS_vfork``.
109+
First, calling such syscalls without provided libc wrappers ignores
110+
all the ``pthread_atfork`` entries as libc can no longer detect the ``fork``.
111+
Second, libc relies on the ``fork/clone`` wrappers to correctly maintain cache for
112+
process id and thread id, and other important process-specific states such as the list
113+
of robust mutexes. Third, even if the user is to call ``exec*`` functions immediately,
114+
there can still be other unexpected issues. For instance, there can be signal handlers
115+
inherited from parent process triggered inside the instruction window between ``fork``
116+
and ``exec*``. As libc failed to maintain its internal states correctly, even though the
117+
functions used inside the signal handlers are marked as ``async-signal-safe`` (such as
118+
``getpid``), they will still return wrong values or lead to other even worse situations.

libc/spec/posix.td

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -546,6 +546,11 @@ def POSIX : StandardSpec<"POSIX"> {
546546
RetValSpec<PidT>,
547547
[ArgSpec<VoidType>]
548548
>,
549+
FunctionSpec<
550+
"gettid",
551+
RetValSpec<PidT>,
552+
[ArgSpec<VoidType>]
553+
>,
549554
FunctionSpec<
550555
"getuid",
551556
RetValSpec<UidT>,
@@ -601,16 +606,6 @@ def POSIX : StandardSpec<"POSIX"> {
601606
RetValSpec<IntType>,
602607
[ArgSpec<ConstCharPtr>]
603608
>,
604-
FunctionSpec<
605-
"getpid",
606-
RetValSpec<IntType>,
607-
[ArgSpec<VoidType>]
608-
>,
609-
FunctionSpec<
610-
"getppid",
611-
RetValSpec<IntType>,
612-
[ArgSpec<VoidType>]
613-
>,
614609
FunctionSpec<
615610
"link",
616611
RetValSpec<IntType>,

libc/src/__support/OSUtil/CMakeLists.txt

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,20 @@ add_object_library(
1515
DEPENDS
1616
${target_os_util}
1717
)
18+
19+
if (LIBC_CONF_ENABLE_PID_CACHE)
20+
set(libc_copt_enable_pid_cache 1)
21+
else()
22+
set(libc_copt_enable_pid_cache 0)
23+
endif()
24+
25+
if(TARGET libc.src.__support.OSUtil.${LIBC_TARGET_OS}.pid)
26+
add_object_library(
27+
pid
28+
ALIAS
29+
DEPENDS
30+
.${LIBC_TARGET_OS}.pid
31+
COMPILE_OPTIONS
32+
-DLIBC_COPT_ENABLE_PID_CACHE=${libc_copt_enable_pid_cache}
33+
)
34+
endif()

libc/src/__support/OSUtil/linux/CMakeLists.txt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,16 @@ add_object_library(
2323
libc.hdr.types.struct_f_owner_ex
2424
libc.hdr.types.off_t
2525
)
26+
27+
add_object_library(
28+
pid
29+
SRCS
30+
pid.cpp
31+
HDRS
32+
../pid.h
33+
DEPENDS
34+
libc.src.__support.OSUtil.osutil
35+
libc.src.__support.common
36+
libc.hdr.types.pid_t
37+
libc.include.sys_syscall
38+
)
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
//===------------ pid_t utilities implementation ----------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "src/__support/OSUtil/pid.h"
10+
#include "src/__support/OSUtil/syscall.h"
11+
#include <sys/syscall.h>
12+
13+
namespace LIBC_NAMESPACE_DECL {
14+
15+
pid_t ProcessIdentity::cache = -1;
16+
pid_t ProcessIdentity::get_uncached() {
17+
return syscall_impl<pid_t>(SYS_getpid);
18+
}
19+
20+
} // namespace LIBC_NAMESPACE_DECL

libc/src/__support/OSUtil/pid.h

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
//===------------ pid_t utilities -------------------------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef LLVM_LIBC_SRC___SUPPORT_OSUTIL_PID_H
10+
#define LLVM_LIBC_SRC___SUPPORT_OSUTIL_PID_H
11+
#include "hdr/types/pid_t.h"
12+
#include "src/__support/macros/attributes.h"
13+
#include "src/__support/macros/optimization.h"
14+
15+
#ifndef LIBC_COPT_ENABLE_PID_CACHE
16+
#define LIBC_COPT_ENABLE_PID_CACHE 1
17+
#endif
18+
19+
namespace LIBC_NAMESPACE_DECL {
20+
21+
class ProcessIdentity {
22+
static LIBC_INLINE_VAR thread_local bool fork_inflight = true;
23+
static pid_t cache;
24+
static pid_t get_uncached();
25+
26+
public:
27+
LIBC_INLINE static void start_fork() { fork_inflight = true; }
28+
LIBC_INLINE static void end_fork() { fork_inflight = false; }
29+
LIBC_INLINE static void refresh_cache() { cache = get_uncached(); }
30+
LIBC_INLINE static pid_t get() {
31+
#if LIBC_COPT_ENABLE_PID_CACHE
32+
if (LIBC_LIKELY(!fork_inflight))
33+
return cache;
34+
#endif
35+
return get_uncached();
36+
}
37+
};
38+
39+
} // namespace LIBC_NAMESPACE_DECL
40+
41+
#endif // LLVM_LIBC_SRC___SUPPORT_OSUTIL_PID_H

libc/src/__support/threads/CMakeLists.txt

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,12 @@ if(TARGET libc.src.__support.threads.${LIBC_TARGET_OS}.mutex)
4444
)
4545
endif()
4646

47+
if (LIBC_CONF_ENABLE_TID_CACHE)
48+
set(libc_copt_enable_tid_cache 1)
49+
else()
50+
set(libc_copt_enable_tid_cache 0)
51+
endif()
52+
4753
add_header_library(
4854
thread_common
4955
HDRS
@@ -54,6 +60,9 @@ add_header_library(
5460
libc.src.__support.CPP.optional
5561
libc.src.__support.CPP.string_view
5662
libc.src.__support.CPP.stringstream
63+
libc.hdr.types.pid_t
64+
COMPILE_OPTIONS
65+
-DLIBC_COPT_ENABLE_TID_CACHE=${libc_copt_enable_tid_cache}
5766
)
5867

5968
if(TARGET libc.src.__support.threads.${LIBC_TARGET_OS}.thread)
@@ -89,3 +98,21 @@ if(TARGET libc.src.__support.threads.${LIBC_TARGET_OS}.CndVar)
8998
.${LIBC_TARGET_OS}.CndVar
9099
)
91100
endif()
101+
102+
set(tid_dep)
103+
if (LLVM_LIBC_FULL_BUILD)
104+
list(APPEND tid_dep libc.src.__support.thread)
105+
else()
106+
list(APPEND tid_dep libc.src.__support.OSUtil.osutil)
107+
list(APPEND tid_dep libc.include.sys_syscall)
108+
endif()
109+
110+
add_header_library(
111+
tid
112+
HDRS
113+
tid.h
114+
DEPENDS
115+
libc.src.__support.common
116+
libc.hdr.types.pid_t
117+
${tid_dep}
118+
)

libc/src/__support/threads/linux/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ add_header_library(
5555
libc.src.__support.common
5656
libc.src.__support.OSUtil.osutil
5757
libc.src.__support.CPP.limits
58+
libc.src.__support.threads.tid
5859
COMPILE_OPTIONS
5960
-DLIBC_COPT_RWLOCK_DEFAULT_SPIN_COUNT=${LIBC_CONF_RWLOCK_DEFAULT_SPIN_COUNT}
6061
${monotonicity_flags}

libc/src/__support/threads/linux/rwlock.h

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include "src/__support/threads/linux/futex_word.h"
2424
#include "src/__support/threads/linux/raw_mutex.h"
2525
#include "src/__support/threads/sleep.h"
26+
#include "src/__support/threads/tid.h"
2627

2728
#ifndef LIBC_COPT_RWLOCK_DEFAULT_SPIN_COUNT
2829
#define LIBC_COPT_RWLOCK_DEFAULT_SPIN_COUNT 100
@@ -336,8 +337,6 @@ class RwLock {
336337
LIBC_INLINE Role get_preference() const {
337338
return static_cast<Role>(preference);
338339
}
339-
// TODO: use cached thread id once implemented.
340-
LIBC_INLINE static pid_t gettid() { return syscall_impl<pid_t>(SYS_gettid); }
341340

342341
template <Role role> LIBC_INLINE LockResult try_lock(RwState &old) {
343342
if constexpr (role == Role::Reader) {
@@ -359,7 +358,7 @@ class RwLock {
359358
if (LIBC_LIKELY(old.compare_exchange_weak_with(
360359
state, old.set_writer_bit(), cpp::MemoryOrder::ACQUIRE,
361360
cpp::MemoryOrder::RELAXED))) {
362-
writer_tid.store(gettid(), cpp::MemoryOrder::RELAXED);
361+
writer_tid.store(gettid_inline(), cpp::MemoryOrder::RELAXED);
363362
return LockResult::Success;
364363
}
365364
// Notice that old is updated by the compare_exchange_weak_with
@@ -394,7 +393,7 @@ class RwLock {
394393
unsigned spin_count = LIBC_COPT_RWLOCK_DEFAULT_SPIN_COUNT) {
395394
// Phase 1: deadlock detection.
396395
// A deadlock happens if this is a RAW/WAW lock in the same thread.
397-
if (writer_tid.load(cpp::MemoryOrder::RELAXED) == gettid())
396+
if (writer_tid.load(cpp::MemoryOrder::RELAXED) == gettid_inline())
398397
return LockResult::Deadlock;
399398

400399
#if LIBC_COPT_TIMEOUT_ENSURE_MONOTONICITY
@@ -520,7 +519,7 @@ class RwLock {
520519
if (old.has_active_writer()) {
521520
// The lock is held by a writer.
522521
// Check if we are the owner of the lock.
523-
if (writer_tid.load(cpp::MemoryOrder::RELAXED) != gettid())
522+
if (writer_tid.load(cpp::MemoryOrder::RELAXED) != gettid_inline())
524523
return LockResult::PermissionDenied;
525524
// clear writer tid.
526525
writer_tid.store(0, cpp::MemoryOrder::RELAXED);

libc/src/__support/threads/linux/thread.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -518,4 +518,6 @@ void thread_exit(ThreadReturnValue retval, ThreadStyle style) {
518518
__builtin_unreachable();
519519
}
520520

521+
pid_t Thread::get_uncached_tid() { return syscall_impl<pid_t>(SYS_gettid); }
522+
521523
} // namespace LIBC_NAMESPACE_DECL

libc/src/__support/threads/thread.h

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@
99
#ifndef LLVM_LIBC_SRC___SUPPORT_THREADS_THREAD_H
1010
#define LLVM_LIBC_SRC___SUPPORT_THREADS_THREAD_H
1111

12+
#ifndef LIBC_COPT_ENABLE_TID_CACHE
13+
#define LIBC_COPT_ENABLE_TID_CACHE 1
14+
#endif
15+
16+
#include "hdr/types/pid_t.h"
1217
#include "src/__support/CPP/atomic.h"
1318
#include "src/__support/CPP/optional.h"
1419
#include "src/__support/CPP/string_view.h"
@@ -103,7 +108,7 @@ struct alignas(STACK_ALIGNMENT) ThreadAttributes {
103108
uintptr_t tls; // Address to the thread TLS memory
104109
uintptr_t tls_size; // The size of area pointed to by |tls|.
105110
unsigned char owned_stack; // Indicates if the thread owns this stack memory
106-
int tid;
111+
pid_t tid;
107112
ThreadStyle style;
108113
ThreadReturnValue retval;
109114
ThreadAtExitCallbackMgr *atexit_callback_mgr;
@@ -228,6 +233,26 @@ struct Thread {
228233

229234
// Return the name of the thread in |name|. Return the error number of error.
230235
int get_name(cpp::StringStream &name) const;
236+
237+
static pid_t get_uncached_tid();
238+
239+
LIBC_INLINE void refresh_tid(pid_t cached = -1) {
240+
if (cached >= 0)
241+
this->attrib->tid = cached;
242+
else
243+
this->attrib->tid = get_uncached_tid();
244+
}
245+
LIBC_INLINE void invalidate_tid() { this->attrib->tid = -1; }
246+
247+
LIBC_INLINE pid_t get_tid() {
248+
#if LIBC_COPT_ENABLE_TID_CACHE
249+
if (LIBC_UNLIKELY(this->attrib->tid < 0))
250+
return get_uncached_tid();
251+
return this->attrib->tid;
252+
#else
253+
return get_uncached_tid();
254+
#endif
255+
}
231256
};
232257

233258
extern LIBC_THREAD_LOCAL Thread self;

libc/src/__support/threads/tid.h

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
//===--- Tid wrapper --------------------------------------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef LLVM_LIBC_SRC___SUPPORT_THREADS_TID_H
10+
#define LLVM_LIBC_SRC___SUPPORT_THREADS_TID_H
11+
12+
// This header is for internal usage which automatically dispatches full build
13+
// and overlay build behaviors.
14+
15+
#include "hdr/types/pid_t.h"
16+
#include "src/__support/common.h"
17+
#ifdef LIBC_FULL_BUILD
18+
#include "src/__support/threads/thread.h"
19+
#else
20+
#include "src/__support/OSUtil/syscall.h"
21+
#include <sys/syscall.h>
22+
#endif // LIBC_FULL_BUILD
23+
24+
namespace LIBC_NAMESPACE_DECL {
25+
LIBC_INLINE pid_t gettid_inline() {
26+
#ifdef LIBC_FULL_BUILD
27+
return self.get_tid();
28+
#else
29+
return syscall_impl<pid_t>(SYS_gettid);
30+
#endif
31+
}
32+
} // namespace LIBC_NAMESPACE_DECL
33+
34+
#endif // LLVM_LIBC_SRC___SUPPORT_THREADS_TID_H

0 commit comments

Comments
 (0)