[libc-commits] [libc] 3407850 - [libc] Implement pthread_kill (#222625)
via libc-commits
libc-commits at lists.llvm.org
Fri Sep 11 03:23:28 PDT 2026
Author: Pavel Labath
Date: 2026-09-11T12:23:21+02:00
New Revision: 3407850c31e556ffb52d22b09c9405ad67a59f64
URL: https://github.com/llvm/llvm-project/commit/3407850c31e556ffb52d22b09c9405ad67a59f64
DIFF: https://github.com/llvm/llvm-project/commit/3407850c31e556ffb52d22b09c9405ad67a59f64.diff
LOG: [libc] Implement pthread_kill (#222625)
The implementation delegates to Thread::kill, which uses tgkill to
target the thread ID.
The main complication is handling zombie threads. POSIX.1-2024 requires
that pthread_kill on a terminated (zombie) thread does not return ESRCH
because the pthread_t handle is still valid. The Linux kernel reaps
threads immediately on exit, so calling tgkill on an exited thread would
return ESRCH (or worse, target a recycled TID). To handle this:
- we check the thread's detach_state first. If it's already EXITING, we
simply return success without calling tgkill (a zombie thread cannot
handle signals anyway).
- if the thread exits concurrently and tgkill returns -ESRCH, we assume
it transitioned to a zombie and treat it as success.
I've also added a tgkill syscall wrapper and replaced one raw usage of
SYS_tgkill.
I've added an integration test covering signal delivery to self and
other threads, signal 0 checks, invalid signals, zombie threads, and
signal mask interactions.
Added:
libc/src/__support/OSUtil/linux/syscall_wrappers/tgkill.h
libc/src/signal/linux/pthread_kill.cpp
libc/src/signal/pthread_kill.h
libc/test/integration/src/pthread/pthread_kill_test.cpp
Modified:
libc/config/linux/aarch64/entrypoints.txt
libc/config/linux/riscv/entrypoints.txt
libc/config/linux/x86_64/entrypoints.txt
libc/include/CMakeLists.txt
libc/include/signal.yaml
libc/src/__support/OSUtil/linux/syscall_wrappers/CMakeLists.txt
libc/src/__support/OSUtil/linux/syscall_wrappers/raise.h
libc/src/__support/threads/linux/CMakeLists.txt
libc/src/__support/threads/linux/thread.cpp
libc/src/__support/threads/thread.h
libc/src/signal/CMakeLists.txt
libc/src/signal/linux/CMakeLists.txt
libc/test/integration/src/pthread/CMakeLists.txt
utils/bazel/llvm-project-overlay/libc/BUILD.bazel
Removed:
################################################################################
diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt
index f7b2c3059df43..f0d4bf68a9abb 100644
--- a/libc/config/linux/aarch64/entrypoints.txt
+++ b/libc/config/linux/aarch64/entrypoints.txt
@@ -1293,6 +1293,7 @@ if(LLVM_LIBC_FULL_BUILD)
# signal.h entrypoints
libc.src.signal.kill
+ libc.src.signal.pthread_kill
libc.src.signal.pthread_sigmask
libc.src.signal.raise
libc.src.signal.sigaction
diff --git a/libc/config/linux/riscv/entrypoints.txt b/libc/config/linux/riscv/entrypoints.txt
index 2cabbc7031f22..5a05e58b46298 100644
--- a/libc/config/linux/riscv/entrypoints.txt
+++ b/libc/config/linux/riscv/entrypoints.txt
@@ -1483,6 +1483,7 @@ if(LLVM_LIBC_FULL_BUILD)
# signal.h entrypoints
libc.src.signal.kill
+ libc.src.signal.pthread_kill
libc.src.signal.pthread_sigmask
libc.src.signal.raise
libc.src.signal.sigaction
diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index bba74cf8907f9..1c5509160b5be 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -1498,6 +1498,7 @@ if(LLVM_LIBC_FULL_BUILD)
# signal.h entrypoints
libc.src.signal.kill
+ libc.src.signal.pthread_kill
libc.src.signal.pthread_sigmask
libc.src.signal.raise
libc.src.signal.sigaction
diff --git a/libc/include/CMakeLists.txt b/libc/include/CMakeLists.txt
index b2fce125ce0ea..b6a059e67c355 100644
--- a/libc/include/CMakeLists.txt
+++ b/libc/include/CMakeLists.txt
@@ -465,6 +465,7 @@ add_header_macro(
DEPENDS
.llvm-libc-macros.signal_macros
.llvm-libc-types.pid_t
+ .llvm-libc-types.pthread_t
.llvm-libc-types.sig_atomic_t
.llvm-libc-types.sig_t
.llvm-libc-types.sighandler_t
diff --git a/libc/include/signal.yaml b/libc/include/signal.yaml
index 88c931edd51f9..f5f110915cfc8 100644
--- a/libc/include/signal.yaml
+++ b/libc/include/signal.yaml
@@ -43,6 +43,9 @@ types:
- type_name: struct_sigaction
- type_name: struct_sigevent
- type_name: union_sigval
+ - type_name: pthread_t
+ standards:
+ - posix
enums: []
objects: []
functions:
@@ -128,3 +131,10 @@ functions:
- type: int
- type: const sigset_t *__restrict
- type: sigset_t *__restrict
+ - name: pthread_kill
+ standards:
+ - posix
+ return_type: int
+ arguments:
+ - type: pthread_t
+ - type: int
diff --git a/libc/src/__support/OSUtil/linux/syscall_wrappers/CMakeLists.txt b/libc/src/__support/OSUtil/linux/syscall_wrappers/CMakeLists.txt
index 44e505694fab1..4cd2dccb2f6fe 100644
--- a/libc/src/__support/OSUtil/linux/syscall_wrappers/CMakeLists.txt
+++ b/libc/src/__support/OSUtil/linux/syscall_wrappers/CMakeLists.txt
@@ -410,10 +410,12 @@ add_header_library(
DEPENDS
libc.src.__support.OSUtil.osutil
libc.src.__support.OSUtil.linux.syscall_wrappers.rt_sigprocmask
+ libc.src.__support.OSUtil.linux.syscall_wrappers.tgkill
libc.src.__support.common
libc.src.__support.error_or
libc.src.__support.macros.config
libc.hdr.signal_macros
+ libc.hdr.types.pid_t
libc.hdr.types.sigset_t
libc.include.sys_syscall
)
@@ -1264,6 +1266,19 @@ add_header_library(
libc.include.sys_syscall
)
+add_header_library(
+ tgkill
+ HDRS
+ tgkill.h
+ DEPENDS
+ libc.hdr.types.pid_t
+ libc.src.__support.OSUtil.osutil
+ libc.src.__support.common
+ libc.src.__support.error_or
+ libc.src.__support.macros.config
+ libc.include.sys_syscall
+)
+
add_header_library(
umask
HDRS
diff --git a/libc/src/__support/OSUtil/linux/syscall_wrappers/raise.h b/libc/src/__support/OSUtil/linux/syscall_wrappers/raise.h
index 2b61e4e624c89..adbe2ab3a6308 100644
--- a/libc/src/__support/OSUtil/linux/syscall_wrappers/raise.h
+++ b/libc/src/__support/OSUtil/linux/syscall_wrappers/raise.h
@@ -10,9 +10,11 @@
#define LLVM_LIBC_SRC___SUPPORT_OSUTIL_SYSCALL_WRAPPERS_RAISE_H
#include "hdr/signal_macros.h"
+#include "hdr/types/pid_t.h"
#include "hdr/types/sigset_t.h"
#include "src/__support/OSUtil/linux/syscall.h" // syscall_impl
#include "src/__support/OSUtil/linux/syscall_wrappers/rt_sigprocmask.h"
+#include "src/__support/OSUtil/linux/syscall_wrappers/tgkill.h"
#include "src/__support/common.h"
#include "src/__support/error_or.h"
#include "src/__support/macros/config.h"
@@ -54,9 +56,10 @@ LIBC_INLINE ErrorOr<int> raise(int sig) {
if (tid < 0)
return Error(-static_cast<int>(tid));
- int result = syscall_impl<int>(SYS_tgkill, pid, tid, sig);
- if (result < 0)
- return Error(-result);
+ auto result = linux_syscalls::tgkill(static_cast<pid_t>(pid),
+ static_cast<pid_t>(tid), sig);
+ if (!result.has_value())
+ return Error(result.error());
}
return status;
}
diff --git a/libc/src/__support/OSUtil/linux/syscall_wrappers/tgkill.h b/libc/src/__support/OSUtil/linux/syscall_wrappers/tgkill.h
new file mode 100644
index 0000000000000..094c724631ef4
--- /dev/null
+++ b/libc/src/__support/OSUtil/linux/syscall_wrappers/tgkill.h
@@ -0,0 +1,34 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Syscall wrapper for tgkill.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC___SUPPORT_OSUTIL_SYSCALL_WRAPPERS_TGKILL_H
+#define LLVM_LIBC_SRC___SUPPORT_OSUTIL_SYSCALL_WRAPPERS_TGKILL_H
+
+#include "hdr/types/pid_t.h"
+#include "src/__support/OSUtil/linux/syscall.h" // syscall_checked
+#include "src/__support/common.h"
+#include "src/__support/error_or.h"
+#include "src/__support/macros/config.h"
+#include <sys/syscall.h> // For syscall numbers
+
+namespace LIBC_NAMESPACE_DECL {
+namespace linux_syscalls {
+
+LIBC_INLINE ErrorOr<int> tgkill(pid_t tgid, pid_t tid, int sig) {
+ return syscall_checked<int>(SYS_tgkill, tgid, tid, sig);
+}
+
+} // namespace linux_syscalls
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC___SUPPORT_OSUTIL_SYSCALL_WRAPPERS_TGKILL_H
diff --git a/libc/src/__support/threads/linux/CMakeLists.txt b/libc/src/__support/threads/linux/CMakeLists.txt
index 51e9d4005ad0d..ae449c1af799d 100644
--- a/libc/src/__support/threads/linux/CMakeLists.txt
+++ b/libc/src/__support/threads/linux/CMakeLists.txt
@@ -51,6 +51,7 @@ add_object_library(
libc.hdr.fcntl_macros
libc.hdr.errno_macros
libc.hdr.sched_macros
+ libc.hdr.signal_macros
libc.hdr.sys_mman_macros
libc.src.errno.errno
libc.src.__support.CPP.atomic
@@ -59,6 +60,7 @@ add_object_library(
libc.src.__support.common
libc.src.__support.error_or
libc.src.__support.OSUtil.linux.syscall_wrappers.close
+ libc.src.__support.OSUtil.linux.syscall_wrappers.getpid
libc.src.__support.OSUtil.linux.syscall_wrappers.mmap
libc.src.__support.OSUtil.linux.syscall_wrappers.mprotect
libc.src.__support.OSUtil.linux.syscall_wrappers.munmap
@@ -67,8 +69,10 @@ add_object_library(
libc.src.__support.OSUtil.linux.syscall_wrappers.sched_getparam
libc.src.__support.OSUtil.linux.syscall_wrappers.sched_getscheduler
libc.src.__support.OSUtil.linux.syscall_wrappers.sched_setscheduler
+ libc.src.__support.OSUtil.linux.syscall_wrappers.tgkill
libc.src.__support.OSUtil.linux.syscall_wrappers.write
libc.src.__support.threads.thread_common
+ libc.src.__support.threads.thread_attributes
COMPILE_OPTIONS
${libc_opt_high_flag}
-fno-omit-frame-pointer # This allows us to sniff out the thread args from
diff --git a/libc/src/__support/threads/linux/thread.cpp b/libc/src/__support/threads/linux/thread.cpp
index 20b3e5b88fa8d..136471edf39c7 100644
--- a/libc/src/__support/threads/linux/thread.cpp
+++ b/libc/src/__support/threads/linux/thread.cpp
@@ -12,6 +12,7 @@
#include "src/__support/CPP/string_view.h"
#include "src/__support/CPP/stringstream.h"
#include "src/__support/OSUtil/linux/syscall_wrappers/close.h"
+#include "src/__support/OSUtil/linux/syscall_wrappers/getpid.h"
#include "src/__support/OSUtil/linux/syscall_wrappers/mmap.h"
#include "src/__support/OSUtil/linux/syscall_wrappers/mprotect.h"
#include "src/__support/OSUtil/linux/syscall_wrappers/munmap.h"
@@ -20,6 +21,7 @@
#include "src/__support/OSUtil/linux/syscall_wrappers/sched_getparam.h"
#include "src/__support/OSUtil/linux/syscall_wrappers/sched_getscheduler.h"
#include "src/__support/OSUtil/linux/syscall_wrappers/sched_setscheduler.h"
+#include "src/__support/OSUtil/linux/syscall_wrappers/tgkill.h"
#include "src/__support/OSUtil/linux/syscall_wrappers/write.h"
#include "src/__support/OSUtil/syscall.h" // For syscall functions.
#include "src/__support/common.h"
@@ -27,6 +29,7 @@
#include "src/__support/libc_errno.h" // For error macros
#include "src/__support/macros/config.h"
#include "src/__support/threads/linux/futex_utils.h" // For FutexWordType
+#include "src/__support/threads/thread_attributes.h"
#ifdef LIBC_TARGET_ARCH_IS_AARCH64
#include <arm_acle.h>
@@ -35,6 +38,7 @@
#include "hdr/errno_macros.h"
#include "hdr/fcntl_macros.h"
#include "hdr/sched_macros.h" // For CLONE_* flags.
+#include "hdr/signal_macros.h"
#include "hdr/stdint_proxy.h"
#include "hdr/sys_mman_macros.h" // For PROT_* and MAP_* definitions.
#include <linux/param.h> // For EXEC_PAGESIZE.
@@ -519,6 +523,42 @@ ErrorOr<SchedParameters> Thread::getschedparam() const {
return SchedParameters{pol_result.value(), param};
}
+ErrorOr<void> Thread::kill(int sig) {
+ auto state = static_cast<DetachState>(
+ attrib->detach_state.load(cpp::MemoryOrder::RELAXED));
+ switch (state) {
+ case DetachState::EXITING:
+ // The thread is exiting, or has already exited. POSIX.1-2024 requires that
+ // pthread_kill does not return ESRCH because the pthread_t (unlike the OS
+ // TID) is still valid. Calling tgkill would return ESRCH (or target a
+ // recycled TID), so we return success directly. We only need to "request
+ // that a signal be delivered", and not actually make sure it has been
+ // handled. A zombie thread cannot handle signals.
+ return {};
+ case DetachState::JOINABLE:
+ case DetachState::DETACHED:
+ // A thread in these states can handle a signal. Note that a JOINABLE thread
+ // can transition to the EXITING state at any moment (and a DETACHED thread
+ // can disappear), but we're not allowed to take any locks to prevent that
+ // from happening (this function needs to be async-signal-safe).
+ break;
+ }
+
+ pid_t pid = linux_syscalls::getpid();
+ auto result = linux_syscalls::tgkill(pid, attrib->tid, sig);
+
+ if (!result.has_value()) {
+ if (result.error() == ESRCH) {
+ // Either the thread has exited since we've checked its state, or this
+ // object is corrupted. The latter is UB, so we're going to assume the
+ // former.
+ return {};
+ }
+ return Error(result.error());
+ }
+ return {};
+}
+
void thread_exit(ThreadReturnValue retval, ThreadStyle style) {
auto attrib = current_thread().attrib;
diff --git a/libc/src/__support/threads/thread.h b/libc/src/__support/threads/thread.h
index c42f87a1a3c57..fcff23a98bb6c 100644
--- a/libc/src/__support/threads/thread.h
+++ b/libc/src/__support/threads/thread.h
@@ -151,6 +151,9 @@ struct Thread {
// Get the scheduling policy and parameters of the thread.
// Return SchedParameters on success, or an error number on failure.
ErrorOr<SchedParameters> getschedparam() const;
+
+ // Send a signal to the thread.
+ ErrorOr<void> kill(int sig);
};
// Platforms should implement this function.
diff --git a/libc/src/signal/CMakeLists.txt b/libc/src/signal/CMakeLists.txt
index a55b55e54ed1c..5611a5da853b9 100644
--- a/libc/src/signal/CMakeLists.txt
+++ b/libc/src/signal/CMakeLists.txt
@@ -44,6 +44,13 @@ add_entrypoint_object(
.${LIBC_TARGET_OS}.pthread_sigmask
)
+add_entrypoint_object(
+ pthread_kill
+ ALIAS
+ DEPENDS
+ .${LIBC_TARGET_OS}.pthread_kill
+)
+
add_entrypoint_object(
sigemptyset
ALIAS
diff --git a/libc/src/signal/linux/CMakeLists.txt b/libc/src/signal/linux/CMakeLists.txt
index 5d7c8c25dccb6..e585aa1a01fce 100644
--- a/libc/src/signal/linux/CMakeLists.txt
+++ b/libc/src/signal/linux/CMakeLists.txt
@@ -107,6 +107,22 @@ add_entrypoint_object(
libc.src.__support.macros.config
)
+add_entrypoint_object(
+ pthread_kill
+ SRCS
+ pthread_kill.cpp
+ HDRS
+ ../pthread_kill.h
+ DEPENDS
+ libc.hdr.errno_macros
+ libc.hdr.signal_macros
+ libc.hdr.types.pthread_t
+ libc.src.__support.common
+ libc.src.__support.macros.config
+ libc.src.__support.macros.null_check
+ libc.src.__support.threads.thread
+)
+
add_entrypoint_object(
sigemptyset
SRCS
diff --git a/libc/src/signal/linux/pthread_kill.cpp b/libc/src/signal/linux/pthread_kill.cpp
new file mode 100644
index 0000000000000..2a897068a18c5
--- /dev/null
+++ b/libc/src/signal/linux/pthread_kill.cpp
@@ -0,0 +1,42 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Linux implementation of pthread_kill.
+///
+//===----------------------------------------------------------------------===//
+
+#include "src/signal/pthread_kill.h"
+
+#include "hdr/errno_macros.h"
+#include "hdr/signal_macros.h"
+#include "hdr/types/pthread_t.h"
+#include "src/__support/common.h"
+#include "src/__support/macros/config.h"
+#include "src/__support/macros/null_check.h"
+#include "src/__support/threads/thread.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+static_assert(sizeof(pthread_t) == sizeof(LIBC_NAMESPACE::Thread),
+ "Mismatch between pthread_t and internal Thread.");
+
+LLVM_LIBC_FUNCTION(int, pthread_kill, (pthread_t th, int sig)) {
+ auto *thread = reinterpret_cast<Thread *>(&th);
+ LIBC_CRASH_ON_NULLPTR(thread->attrib);
+
+ // We can't delegate this check to the kernel since some of the code paths
+ // don't go through the syscall.
+ if (sig < 0 || sig >= NSIG)
+ return EINVAL;
+
+ auto res = thread->kill(sig);
+ return res ? 0 : res.error();
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/signal/pthread_kill.h b/libc/src/signal/pthread_kill.h
new file mode 100644
index 0000000000000..dca33e8a02b92
--- /dev/null
+++ b/libc/src/signal/pthread_kill.h
@@ -0,0 +1,31 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Implementation header for pthread_kill.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_SIGNAL_PTHREAD_KILL_H
+#define LLVM_LIBC_SRC_SIGNAL_PTHREAD_KILL_H
+
+#include "hdr/types/pthread_t.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+/// Request that a signal be delivered to a thread.
+///
+/// \param thread The thread to receive the signal.
+/// \param sig The signal to deliver.
+/// \return 0 on success, or an error number on failure.
+int pthread_kill(pthread_t thread, int sig);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_SIGNAL_PTHREAD_KILL_H
diff --git a/libc/test/integration/src/pthread/CMakeLists.txt b/libc/test/integration/src/pthread/CMakeLists.txt
index 75c9fed78a713..6ec434cc51681 100644
--- a/libc/test/integration/src/pthread/CMakeLists.txt
+++ b/libc/test/integration/src/pthread/CMakeLists.txt
@@ -365,3 +365,29 @@ add_integration_test(
libc.src.pthread.pthread_self
libc.src.pthread.pthread_setschedparam
)
+
+add_integration_test(
+ pthread_kill_test
+ SUITE
+ libc-pthread-integration-tests
+ SRCS
+ pthread_kill_test.cpp
+ DEPENDS
+ libc.hdr.errno_macros
+ libc.hdr.signal_macros
+ libc.hdr.stdint_proxy
+ libc.hdr.types.pthread_t
+ libc.hdr.types.sigset_t
+ libc.hdr.types.struct_sigaction
+ libc.src.__support.CPP.atomic
+ libc.src.__support.threads.futex_utils
+ libc.src.__support.threads.thread
+ libc.src.pthread.pthread_create
+ libc.src.pthread.pthread_join
+ libc.src.pthread.pthread_self
+ libc.src.signal.pthread_kill
+ libc.src.signal.pthread_sigmask
+ libc.src.signal.sigaction
+ libc.src.signal.sigaddset
+ libc.src.signal.sigemptyset
+)
diff --git a/libc/test/integration/src/pthread/pthread_kill_test.cpp b/libc/test/integration/src/pthread/pthread_kill_test.cpp
new file mode 100644
index 0000000000000..205e8b879ba75
--- /dev/null
+++ b/libc/test/integration/src/pthread/pthread_kill_test.cpp
@@ -0,0 +1,170 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Integration tests for pthread_kill.
+///
+//===----------------------------------------------------------------------===//
+
+#include "hdr/errno_macros.h"
+#include "hdr/signal_macros.h"
+#include "hdr/stdint_proxy.h"
+#include "hdr/types/pthread_t.h"
+#include "hdr/types/sigset_t.h"
+#include "hdr/types/struct_sigaction.h"
+#include "src/__support/CPP/atomic.h"
+#include "src/__support/threads/futex_utils.h"
+#include "src/__support/threads/thread.h"
+#include "src/pthread/pthread_create.h"
+#include "src/pthread/pthread_join.h"
+#include "src/pthread/pthread_self.h"
+#include "src/signal/pthread_kill.h"
+#include "src/signal/pthread_sigmask.h"
+#include "src/signal/sigaction.h"
+#include "src/signal/sigaddset.h"
+#include "src/signal/sigemptyset.h"
+#include "test/IntegrationTest/test.h"
+
+static LIBC_NAMESPACE::Futex usr1_count(0);
+static LIBC_NAMESPACE::Futex usr2_count(0);
+
+static void sigusr1_handler(int) {
+ usr1_count.fetch_add(1, LIBC_NAMESPACE::cpp::MemoryOrder::RELAXED);
+}
+
+static void sigusr2_handler(int) {
+ usr2_count.fetch_add(1, LIBC_NAMESPACE::cpp::MemoryOrder::RELAXED);
+}
+
+static void setup_signal_handlers() {
+ struct sigaction sa = {};
+ sa.sa_handler = sigusr1_handler;
+ sa.sa_flags = 0;
+ LIBC_NAMESPACE::sigemptyset(&sa.sa_mask);
+ ASSERT_EQ(LIBC_NAMESPACE::sigaction(SIGUSR1, &sa, nullptr), 0);
+
+ sa.sa_handler = sigusr2_handler;
+ ASSERT_EQ(LIBC_NAMESPACE::sigaction(SIGUSR2, &sa, nullptr), 0);
+}
+
+static void test_invalid_signal() {
+ pthread_t self = LIBC_NAMESPACE::pthread_self();
+ ASSERT_EQ(LIBC_NAMESPACE::pthread_kill(self, -1), EINVAL);
+ ASSERT_EQ(LIBC_NAMESPACE::pthread_kill(self, 1000), EINVAL);
+}
+
+static void test_self_signal_zero() {
+ pthread_t self = LIBC_NAMESPACE::pthread_self();
+ ASSERT_EQ(LIBC_NAMESPACE::pthread_kill(self, 0), 0);
+}
+
+static void test_self_signal_delivery() {
+ pthread_t self = LIBC_NAMESPACE::pthread_self();
+ uint32_t initial = usr1_count.load(LIBC_NAMESPACE::cpp::MemoryOrder::RELAXED);
+ ASSERT_EQ(LIBC_NAMESPACE::pthread_kill(self, SIGUSR1), 0);
+ ASSERT_EQ(usr1_count.load(LIBC_NAMESPACE::cpp::MemoryOrder::RELAXED),
+ initial + 1);
+}
+
+static void *cross_thread_worker(void *initial_opaque) {
+ uint32_t initial =
+ static_cast<uint32_t>(reinterpret_cast<uintptr_t>(initial_opaque));
+ usr1_count.wait(initial);
+ return nullptr;
+}
+
+static void test_cross_thread_signal() {
+ pthread_t th;
+ uint32_t initial = usr1_count.load(LIBC_NAMESPACE::cpp::MemoryOrder::RELAXED);
+ ASSERT_EQ(LIBC_NAMESPACE::pthread_create(&th, nullptr, cross_thread_worker,
+ reinterpret_cast<void *>(initial)),
+ 0);
+
+ ASSERT_EQ(LIBC_NAMESPACE::pthread_kill(th, 0), 0);
+ ASSERT_EQ(LIBC_NAMESPACE::pthread_kill(th, SIGUSR1), 0);
+
+ void *retval;
+ ASSERT_EQ(LIBC_NAMESPACE::pthread_join(th, &retval), 0);
+}
+
+static void *zombie_worker(void *) { return nullptr; }
+
+static void test_zombie_thread() {
+ pthread_t th;
+ ASSERT_EQ(
+ LIBC_NAMESPACE::pthread_create(&th, nullptr, zombie_worker, nullptr), 0);
+
+ // Wait until thread has exited.
+ auto *thread_internal = reinterpret_cast<LIBC_NAMESPACE::Thread *>(&th);
+ thread_internal->wait();
+
+ // POSIX.1-2024 requires that a zombie thread ID does not return ESRCH.
+ ASSERT_EQ(LIBC_NAMESPACE::pthread_kill(th, 0), 0);
+ ASSERT_EQ(LIBC_NAMESPACE::pthread_kill(th, -1), EINVAL);
+
+ void *retval;
+ ASSERT_EQ(LIBC_NAMESPACE::pthread_join(th, &retval), 0);
+}
+
+static LIBC_NAMESPACE::Futex mask_ready(0);
+static LIBC_NAMESPACE::Futex unblock_signal(0);
+
+static void *mask_worker(void *) {
+ sigset_t set;
+ LIBC_NAMESPACE::sigemptyset(&set);
+ LIBC_NAMESPACE::sigaddset(&set, SIGUSR2);
+ ASSERT_EQ(LIBC_NAMESPACE::pthread_sigmask(SIG_BLOCK, &set, nullptr), 0);
+
+ mask_ready.store(1, LIBC_NAMESPACE::cpp::MemoryOrder::RELEASE);
+ mask_ready.notify_one();
+
+ unblock_signal.wait(0);
+
+ // Signal was sent while blocked. It should not have been delivered yet.
+ ASSERT_EQ(usr2_count.load(LIBC_NAMESPACE::cpp::MemoryOrder::RELAXED), 0);
+
+ // Unblocking the signal should cause immediate delivery.
+ ASSERT_EQ(LIBC_NAMESPACE::pthread_sigmask(SIG_UNBLOCK, &set, nullptr), 0);
+
+ ASSERT_EQ(usr2_count.load(LIBC_NAMESPACE::cpp::MemoryOrder::RELAXED), 1);
+ return nullptr;
+}
+
+static void test_signal_mask_interaction() {
+ mask_ready.store(0, LIBC_NAMESPACE::cpp::MemoryOrder::RELAXED);
+ unblock_signal.store(0, LIBC_NAMESPACE::cpp::MemoryOrder::RELAXED);
+ usr2_count.store(0, LIBC_NAMESPACE::cpp::MemoryOrder::RELAXED);
+
+ pthread_t th;
+ ASSERT_EQ(LIBC_NAMESPACE::pthread_create(&th, nullptr, mask_worker, nullptr),
+ 0);
+
+ mask_ready.wait(0);
+
+ ASSERT_EQ(LIBC_NAMESPACE::pthread_kill(th, SIGUSR2), 0);
+
+ unblock_signal.store(1, LIBC_NAMESPACE::cpp::MemoryOrder::RELEASE);
+ unblock_signal.notify_one();
+
+ void *retval;
+ ASSERT_EQ(LIBC_NAMESPACE::pthread_join(th, &retval), 0);
+ ASSERT_EQ(usr2_count.load(LIBC_NAMESPACE::cpp::MemoryOrder::RELAXED), 1);
+}
+
+TEST_MAIN() {
+ setup_signal_handlers();
+
+ test_invalid_signal();
+ test_self_signal_zero();
+ test_self_signal_delivery();
+ test_cross_thread_signal();
+ test_zombie_thread();
+ test_signal_mask_interaction();
+
+ return 0;
+}
diff --git a/utils/bazel/llvm-project-overlay/libc/BUILD.bazel b/utils/bazel/llvm-project-overlay/libc/BUILD.bazel
index f0ec6eac91e9c..1020275b87944 100644
--- a/utils/bazel/llvm-project-overlay/libc/BUILD.bazel
+++ b/utils/bazel/llvm-project-overlay/libc/BUILD.bazel
@@ -2460,8 +2460,10 @@ libc_support_library(
":__support_error_or",
":__support_macros_config",
":__support_osutil_linux_syscall_wrappers_rt_sigprocmask",
+ ":__support_osutil_linux_syscall_wrappers_tgkill",
":__support_osutil_syscall",
":hdr_signal_macros",
+ ":types_pid_t",
":types_sigset_t",
],
)
@@ -2812,6 +2814,22 @@ libc_support_library(
],
)
+libc_support_library(
+ name = "__support_osutil_linux_syscall_wrappers_tgkill",
+ hdrs = ["src/__support/OSUtil/linux/syscall_wrappers/tgkill.h"],
+ target_compatible_with = select({
+ "@platforms//os:linux": [],
+ "//conditions:default": ["@platforms//:incompatible"],
+ }),
+ deps = [
+ ":__support_common",
+ ":__support_error_or",
+ ":__support_macros_config",
+ ":__support_osutil_syscall",
+ ":types_pid_t",
+ ],
+)
+
libc_support_library(
name = "__support_printf_core_printf_config",
hdrs = ["src/__support/printf_core/printf_config.h"],
@@ -4394,6 +4412,7 @@ libc_support_library(
":__support_macros_attributes",
":__support_macros_config",
":__support_osutil_linux_syscall_wrappers_close",
+ ":__support_osutil_linux_syscall_wrappers_getpid",
":__support_osutil_linux_syscall_wrappers_mmap",
":__support_osutil_linux_syscall_wrappers_mprotect",
":__support_osutil_linux_syscall_wrappers_munmap",
@@ -4402,17 +4421,20 @@ libc_support_library(
":__support_osutil_linux_syscall_wrappers_sched_getparam",
":__support_osutil_linux_syscall_wrappers_sched_getscheduler",
":__support_osutil_linux_syscall_wrappers_sched_setscheduler",
+ ":__support_osutil_linux_syscall_wrappers_tgkill",
":__support_osutil_linux_syscall_wrappers_write",
":__support_osutil_syscall",
":__support_threads_futex_utils",
":__support_threads_linux_futex_utils",
":__support_threads_linux_futex_word_type",
":__support_threads_mutex",
+ ":__support_threads_thread_attributes",
":__support_threads_thread_headers",
":config_app_h",
":hdr_errno_macros",
":hdr_fcntl_macros",
":hdr_sched_macros",
+ ":hdr_signal_macros",
":hdr_stdint_proxy",
":hdr_sys_mman_macros",
],
More information about the libc-commits
mailing list