[libc-commits] [libc] [libc] End a semaphore wait with EINTR when a signal handler runs (PR #236969)
Mike Lothian via libc-commits
libc-commits at lists.llvm.org
Fri Oct 30 01:25:12 PDT 2026
https://github.com/FireBurn updated https://github.com/llvm/llvm-project/pull/236969
>From 1aed4f0cf23e8736d26e11798902ebb27cfd181c Mon Sep 17 00:00:00 2001
From: Mike Lothian <mike at fireburn.co.uk>
Date: Sun, 11 Oct 2026 16:50:57 +0100
Subject: [PATCH] [libc] End a semaphore wait with EINTR when a signal handler
runs
The futex wait started again after every interruption, so sem_wait,
sem_timedwait and sem_clockwait never returned for a signal. POSIX has
them fail with EINTR, and Python's locks rely on it to deliver
KeyboardInterrupt while waiting.
Assisted-by: Claude
---
.../src/__support/threads/linux/futex_utils.h | 22 +++++++++++-----
libc/src/semaphore/linux/semaphore.h | 14 +++++++---
libc/test/src/semaphore/CMakeLists.txt | 6 +++++
libc/test/src/semaphore/sem_wait_test.cpp | 26 +++++++++++++++++++
4 files changed, 59 insertions(+), 9 deletions(-)
diff --git a/libc/src/__support/threads/linux/futex_utils.h b/libc/src/__support/threads/linux/futex_utils.h
index c8adc9aab981d2..3c933080237eef 100644
--- a/libc/src/__support/threads/linux/futex_utils.h
+++ b/libc/src/__support/threads/linux/futex_utils.h
@@ -1,10 +1,15 @@
-//===--- Futex 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
//
//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Wrapper around a futex word.
+///
+//===----------------------------------------------------------------------===//
#ifndef LLVM_LIBC_SRC___SUPPORT_THREADS_LINUX_FUTEX_UTILS_H
#define LLVM_LIBC_SRC___SUPPORT_THREADS_LINUX_FUTEX_UTILS_H
@@ -45,7 +50,8 @@ class Futex : public cpp::Atomic<FutexWordType> {
}
LIBC_INLINE ErrorOr<int> wait(FutexWordType expected,
cpp::optional<Timeout> timeout = cpp::nullopt,
- bool is_shared = false) {
+ bool is_shared = false,
+ bool restart_on_signal = true) {
// use bitset variants to enforce abs_time
uint32_t op = is_shared ? FUTEX_WAIT_BITSET : FUTEX_WAIT_BITSET_PRIVATE;
if (timeout && timeout->is_realtime()) {
@@ -89,10 +95,14 @@ class Futex : public cpp::Atomic<FutexWordType> {
/*ignored=*/nullptr,
/*bitset=*/FUTEX_BITSET_MATCH_ANY);
- // continue waiting if interrupted; otherwise return the result
- // which should normally be 0 or -ETIMEOUT.
- if (ret == -EINTR)
- continue;
+ // continue waiting if interrupted, unless the caller reports being
+ // interrupted itself; otherwise return the result which should normally
+ // be 0 or -ETIMEOUT.
+ if (ret == -EINTR) {
+ if (restart_on_signal)
+ continue;
+ return cpp::unexpected(EINTR);
+ }
// EAGAIN and EWOULDBLOCK will be treated as a normal finish, as the value
// has changed.
diff --git a/libc/src/semaphore/linux/semaphore.h b/libc/src/semaphore/linux/semaphore.h
index 400a913f5f4aa9..beb0e2c0e66e39 100644
--- a/libc/src/semaphore/linux/semaphore.h
+++ b/libc/src/semaphore/linux/semaphore.h
@@ -1,10 +1,15 @@
-//===-- Linux Semaphore implementation for POSIX semaphores ---------------===//
+//===----------------------------------------------------------------------===//
//
// 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 POSIX semaphores.
+///
+//===----------------------------------------------------------------------===//
#ifndef LLVM_LIBC_SRC_SEMAPHORE_LINUX_SEMAPHORE_H
#define LLVM_LIBC_SRC_SEMAPHORE_LINUX_SEMAPHORE_H
@@ -44,7 +49,9 @@ class Semaphore {
// absolute timeout.
LIBC_INLINE int wait_until(internal::AbsTimeout timeout) {
while (trywait() != 0) {
- auto wait_or = value.wait(/*expected=*/0, timeout, is_shared);
+ // POSIX has a signal handler end the wait with EINTR.
+ auto wait_or = value.wait(/*expected=*/0, timeout, is_shared,
+ /*restart_on_signal=*/false);
// returned error is not a benign race EAGAIN
if (!wait_or.has_value() && wait_or.error() != EAGAIN) {
@@ -140,7 +147,8 @@ class Semaphore {
// so a racing post() before sleep is not lost.
// Spurious wakeups just send back to trywait().
auto wait_or =
- value.wait(/*expected=*/0, /*timeout=*/cpp::nullopt, is_shared);
+ value.wait(/*expected=*/0, /*timeout=*/cpp::nullopt, is_shared,
+ /*restart_on_signal=*/false);
// A successful wake or EAGAIN both just send it back to trywait().
// Any other error is unexpected.
diff --git a/libc/test/src/semaphore/CMakeLists.txt b/libc/test/src/semaphore/CMakeLists.txt
index 1812799103bd2e..d9aa2292b196d2 100644
--- a/libc/test/src/semaphore/CMakeLists.txt
+++ b/libc/test/src/semaphore/CMakeLists.txt
@@ -25,6 +25,8 @@ add_libc_test(
libc.src.semaphore.sem_timedwait
libc.src.semaphore.sem_trywait
libc.src.semaphore.sem_wait
+ libc.src.signal.sigaction
+ libc.src.unistd.alarm
libc.test.UnitTest.ErrnoCheckingTest
libc.test.UnitTest.ErrnoSetterMatcher
)
@@ -38,6 +40,8 @@ add_libc_test(
DEPENDS
libc.hdr.errno_macros
libc.hdr.limits_macros
+ libc.hdr.signal_macros
+ libc.hdr.types.struct_sigaction
libc.hdr.time_macros
libc.hdr.types.sem_t
libc.hdr.types.struct_timespec
@@ -51,6 +55,8 @@ add_libc_test(
libc.src.semaphore.sem_timedwait
libc.src.semaphore.sem_trywait
libc.src.semaphore.sem_wait
+ libc.src.signal.sigaction
+ libc.src.unistd.alarm
libc.test.UnitTest.ErrnoCheckingTest
libc.test.UnitTest.ErrnoSetterMatcher
)
diff --git a/libc/test/src/semaphore/sem_wait_test.cpp b/libc/test/src/semaphore/sem_wait_test.cpp
index b46be16a34610a..92a198882ac84e 100644
--- a/libc/test/src/semaphore/sem_wait_test.cpp
+++ b/libc/test/src/semaphore/sem_wait_test.cpp
@@ -13,8 +13,10 @@
#include "hdr/errno_macros.h"
#include "hdr/limits_macros.h"
+#include "hdr/signal_macros.h"
#include "hdr/time_macros.h"
#include "hdr/types/sem_t.h"
+#include "hdr/types/struct_sigaction.h"
#include "hdr/types/struct_timespec.h"
#include "src/__support/time/clock_gettime.h"
#include "src/semaphore/sem_clockwait.h"
@@ -25,6 +27,8 @@
#include "src/semaphore/sem_timedwait.h"
#include "src/semaphore/sem_trywait.h"
#include "src/semaphore/sem_wait.h"
+#include "src/signal/sigaction.h"
+#include "src/unistd/alarm.h"
#include "test/UnitTest/ErrnoCheckingTest.h"
#include "test/UnitTest/ErrnoSetterMatcher.h"
#include "test/UnitTest/Test.h"
@@ -146,3 +150,25 @@ TEST_F(LlvmLibcSemWaitTest, ClockWaitUnsupportedClock) {
ASSERT_THAT(LIBC_NAMESPACE::sem_destroy(&sem), Succeeds());
}
+
+static void on_alarm(int) {}
+
+TEST_F(LlvmLibcSemWaitTest, ASignalHandlerEndsTheWaitWithEINTR) {
+ struct sigaction action{};
+ action.sa_handler = on_alarm;
+ // No SA_RESTART: the call is not to be started again.
+ ASSERT_THAT(LIBC_NAMESPACE::sigaction(SIGALRM, &action, nullptr), Succeeds());
+
+ sem_t sem;
+ ASSERT_THAT(LIBC_NAMESPACE::sem_init(&sem, 0, 0), Succeeds());
+
+ LIBC_NAMESPACE::alarm(1);
+ ASSERT_THAT(LIBC_NAMESPACE::sem_wait(&sem), Fails(EINTR));
+
+ LIBC_NAMESPACE::alarm(1);
+ timespec far = deadline_in_10ms(CLOCK_REALTIME);
+ far.tv_sec += 30;
+ ASSERT_THAT(LIBC_NAMESPACE::sem_timedwait(&sem, &far), Fails(EINTR));
+
+ ASSERT_THAT(LIBC_NAMESPACE::sem_destroy(&sem), Succeeds());
+}
More information about the libc-commits
mailing list