[libc-commits] [libc] [libc] Skip the RPC doorbell interrupt when the mailbox is uninitialized (PR #214037)
Minseob Shin via libc-commits
libc-commits at lists.llvm.org
Tue Aug 4 13:48:12 PDT 2026
https://github.com/minseobshin11 updated https://github.com/llvm/llvm-project/pull/214037
>From 9e8a6a35589f84f8ecdea4763e48aaa72e580610 Mon Sep 17 00:00:00 2001
From: Minseob Shin <minseob.shin11 at gmail.com>
Date: Tue, 4 Aug 2026 13:39:03 -0500
Subject: [PATCH 1/2] [libc] Skip the RPC doorbell interrupt when the mailbox
is uninitialized
---
libc/shared/rpc.h | 15 +++--
libc/test/src/__support/RPC/CMakeLists.txt | 10 ++++
.../src/__support/RPC/rpc_doorbell_test.cpp | 57 +++++++++++++++++++
3 files changed, 77 insertions(+), 5 deletions(-)
create mode 100644 libc/test/src/__support/RPC/rpc_doorbell_test.cpp
diff --git a/libc/shared/rpc.h b/libc/shared/rpc.h
index 5bc518ab97af7..eb6051f329e4a 100644
--- a/libc/shared/rpc.h
+++ b/libc/shared/rpc.h
@@ -158,11 +158,16 @@ template <bool Invert> struct Process {
if (rpc::is_first_lane(lane_mask)) {
if (!__scoped_atomic_fetch_add(doorbell->value, 1UL, __ATOMIC_RELAXED,
__MEMORY_SCOPE_SYSTEM)) {
- __scoped_atomic_store_n(doorbell->mailbox,
- static_cast<uint64_t>(doorbell->event_id),
- __ATOMIC_RELAXED, __MEMORY_SCOPE_SYSTEM);
- __scoped_atomic_thread_fence(__ATOMIC_RELEASE, __MEMORY_SCOPE_SYSTEM);
- signal_interrupt(event_id);
+ // A doorbell that is not interrupt-backed has no mailbox. The counter
+ // incremented above already publishes the work, so skipping the
+ // interrupt is safe.
+ if (doorbell->mailbox) {
+ __scoped_atomic_store_n(doorbell->mailbox,
+ static_cast<uint64_t>(doorbell->event_id),
+ __ATOMIC_RELAXED, __MEMORY_SCOPE_SYSTEM);
+ __scoped_atomic_thread_fence(__ATOMIC_RELEASE, __MEMORY_SCOPE_SYSTEM);
+ signal_interrupt(event_id);
+ }
}
}
#endif
diff --git a/libc/test/src/__support/RPC/CMakeLists.txt b/libc/test/src/__support/RPC/CMakeLists.txt
index 8f79a49cec40c..11d09226077c8 100644
--- a/libc/test/src/__support/RPC/CMakeLists.txt
+++ b/libc/test/src/__support/RPC/CMakeLists.txt
@@ -9,3 +9,13 @@ add_libc_test(
DEPENDS
libc.src.__support.RPC.rpc
)
+
+add_libc_test(
+ rpc_doorbell_test
+ SUITE
+ libc-rpc-tests
+ SRCS
+ rpc_doorbell_test.cpp
+ DEPENDS
+ libc.src.__support.RPC.rpc
+)
diff --git a/libc/test/src/__support/RPC/rpc_doorbell_test.cpp b/libc/test/src/__support/RPC/rpc_doorbell_test.cpp
new file mode 100644
index 0000000000000..f836b29f6bcce
--- /dev/null
+++ b/libc/test/src/__support/RPC/rpc_doorbell_test.cpp
@@ -0,0 +1,57 @@
+//===-- tests for the RPC doorbell ----------------------------------------===//
+//
+// 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/RPC/rpc.h"
+
+#include "test/UnitTest/Test.h"
+
+namespace {
+enum { port_count = 4 };
+
+using ProcType = LIBC_NAMESPACE::rpc::Process<false>;
+
+enum { alloc_size = ProcType::allocation_size(port_count, 1) };
+
+alignas(64) char buffer[alloc_size] = {0};
+
+// At namespace scope so the doorbell never refers to a dead local.
+uint64_t pending = 0;
+} // namespace
+
+// A doorbell can be only partially initialized. The AMDGPU offload plugin sets
+// 'value' to the address of a field inside its HSA signal, so it is never null,
+// while 'mailbox' is that signal's event mailbox pointer, which is null unless
+// the signal is backed by an interrupt. Ringing such a doorbell must not
+// dereference the null mailbox, and must still publish the pending work so a
+// polling server makes progress.
+TEST(LlvmLibcRPCDoorbell, NotifyWithoutMailbox) {
+ ProcType Proc(port_count, buffer);
+
+ pending = 0;
+ Proc.doorbell->value = &pending;
+ Proc.doorbell->mailbox = nullptr;
+ Proc.doorbell->event_id = 0;
+
+ Proc.notify(/*lane_mask=*/1);
+
+ // notify() has no effect when built with MSVC.
+#ifndef _MSC_VER
+ EXPECT_EQ(pending, static_cast<uint64_t>(1));
+#endif
+}
+
+// A doorbell that was never configured at all is ignored entirely.
+TEST(LlvmLibcRPCDoorbell, NotifyWithoutDoorbell) {
+ ProcType Proc(port_count, buffer);
+
+ Proc.doorbell->value = nullptr;
+ Proc.doorbell->mailbox = nullptr;
+ Proc.doorbell->event_id = 0;
+
+ Proc.notify(/*lane_mask=*/1);
+}
>From b9463f41042a828899afdda14be538b9d6525d6d Mon Sep 17 00:00:00 2001
From: Minseob Shin <minseob.shin11 at gmail.com>
Date: Tue, 4 Aug 2026 15:47:54 -0500
Subject: [PATCH 2/2] address review
---
libc/shared/rpc.h | 19 ++++++++-----------
.../src/__support/RPC/rpc_doorbell_test.cpp | 12 +++---------
2 files changed, 11 insertions(+), 20 deletions(-)
diff --git a/libc/shared/rpc.h b/libc/shared/rpc.h
index eb6051f329e4a..2e899354fe592 100644
--- a/libc/shared/rpc.h
+++ b/libc/shared/rpc.h
@@ -156,18 +156,15 @@ template <bool Invert> struct Process {
uint32_t event_id = rpc::broadcast_value(lane_mask, doorbell->event_id);
if (rpc::is_first_lane(lane_mask)) {
+ // The interrupt is optional and is skipped if there is no mailbox.
if (!__scoped_atomic_fetch_add(doorbell->value, 1UL, __ATOMIC_RELAXED,
- __MEMORY_SCOPE_SYSTEM)) {
- // A doorbell that is not interrupt-backed has no mailbox. The counter
- // incremented above already publishes the work, so skipping the
- // interrupt is safe.
- if (doorbell->mailbox) {
- __scoped_atomic_store_n(doorbell->mailbox,
- static_cast<uint64_t>(doorbell->event_id),
- __ATOMIC_RELAXED, __MEMORY_SCOPE_SYSTEM);
- __scoped_atomic_thread_fence(__ATOMIC_RELEASE, __MEMORY_SCOPE_SYSTEM);
- signal_interrupt(event_id);
- }
+ __MEMORY_SCOPE_SYSTEM) &&
+ doorbell->mailbox) {
+ __scoped_atomic_store_n(doorbell->mailbox,
+ static_cast<uint64_t>(doorbell->event_id),
+ __ATOMIC_RELAXED, __MEMORY_SCOPE_SYSTEM);
+ __scoped_atomic_thread_fence(__ATOMIC_RELEASE, __MEMORY_SCOPE_SYSTEM);
+ signal_interrupt(event_id);
}
}
#endif
diff --git a/libc/test/src/__support/RPC/rpc_doorbell_test.cpp b/libc/test/src/__support/RPC/rpc_doorbell_test.cpp
index f836b29f6bcce..385b2c0472fa7 100644
--- a/libc/test/src/__support/RPC/rpc_doorbell_test.cpp
+++ b/libc/test/src/__support/RPC/rpc_doorbell_test.cpp
@@ -19,16 +19,10 @@ enum { alloc_size = ProcType::allocation_size(port_count, 1) };
alignas(64) char buffer[alloc_size] = {0};
-// At namespace scope so the doorbell never refers to a dead local.
uint64_t pending = 0;
} // namespace
-// A doorbell can be only partially initialized. The AMDGPU offload plugin sets
-// 'value' to the address of a field inside its HSA signal, so it is never null,
-// while 'mailbox' is that signal's event mailbox pointer, which is null unless
-// the signal is backed by an interrupt. Ringing such a doorbell must not
-// dereference the null mailbox, and must still publish the pending work so a
-// polling server makes progress.
+// A null mailbox is ignored, but the work is still published.
TEST(LlvmLibcRPCDoorbell, NotifyWithoutMailbox) {
ProcType Proc(port_count, buffer);
@@ -39,13 +33,13 @@ TEST(LlvmLibcRPCDoorbell, NotifyWithoutMailbox) {
Proc.notify(/*lane_mask=*/1);
- // notify() has no effect when built with MSVC.
+ // notify() is a no-op on MSVC.
#ifndef _MSC_VER
EXPECT_EQ(pending, static_cast<uint64_t>(1));
#endif
}
-// A doorbell that was never configured at all is ignored entirely.
+// An unconfigured doorbell is ignored entirely.
TEST(LlvmLibcRPCDoorbell, NotifyWithoutDoorbell) {
ProcType Proc(port_count, buffer);
More information about the libc-commits
mailing list