[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