diff --git a/libc/src/__support/RPC/rpc.h b/libc/src/__support/RPC/rpc.h --- a/libc/src/__support/RPC/rpc.h +++ b/libc/src/__support/RPC/rpc.h @@ -116,11 +116,10 @@ /// Atomic secondary[port_count]; /// Packet buffer[port_count]; /// }; - LIBC_INLINE static uint64_t allocation_size(uint64_t port_count) { + LIBC_INLINE static constexpr uint64_t allocation_size(uint64_t port_count) { return buffer_offset(port_count) + buffer_bytes(port_count); } -protected: /// Retrieve the inbox state from memory shared between processes. LIBC_INLINE uint32_t load_inbox(uint64_t index) { return inbox[index].load(cpp::MemoryOrder::RELAXED); @@ -251,27 +250,27 @@ } /// Number of bytes to allocate for an inbox or outbox. - LIBC_INLINE static uint64_t mailbox_bytes(uint64_t port_count) { + LIBC_INLINE static constexpr uint64_t mailbox_bytes(uint64_t port_count) { return port_count * sizeof(cpp::Atomic); } /// Number of bytes to allocate for the buffer containing the packets. - LIBC_INLINE static uint64_t buffer_bytes(uint64_t port_count) { + LIBC_INLINE static constexpr uint64_t buffer_bytes(uint64_t port_count) { return port_count * sizeof(Packet); } /// Offset of the inbox in memory. This is the same as the outbox if inverted. - LIBC_INLINE static uint64_t inbox_offset(uint64_t port_count) { + LIBC_INLINE static constexpr uint64_t inbox_offset(uint64_t port_count) { return Invert ? mailbox_bytes(port_count) : 0; } /// Offset of the outbox in memory. This is the same as the inbox if inverted. - LIBC_INLINE static uint64_t outbox_offset(uint64_t port_count) { + LIBC_INLINE static constexpr uint64_t outbox_offset(uint64_t port_count) { return Invert ? 0 : mailbox_bytes(port_count); } /// Offset of the buffer containing the packets after the inbox and outbox. - LIBC_INLINE static uint64_t buffer_offset(uint64_t port_count) { + LIBC_INLINE static constexpr uint64_t buffer_offset(uint64_t port_count) { return align_up(2 * mailbox_bytes(port_count), alignof(Packet)); } }; diff --git a/libc/src/__support/RPC/rpc_util.h b/libc/src/__support/RPC/rpc_util.h --- a/libc/src/__support/RPC/rpc_util.h +++ b/libc/src/__support/RPC/rpc_util.h @@ -51,7 +51,8 @@ } /// Return \p val aligned "upwards" according to \p align. -template LIBC_INLINE V align_up(V val, A align) { +template +LIBC_INLINE constexpr V align_up(V val, A align) { return ((val + V(align) - 1) / V(align)) * V(align); } diff --git a/libc/test/src/__support/CMakeLists.txt b/libc/test/src/__support/CMakeLists.txt --- a/libc/test/src/__support/CMakeLists.txt +++ b/libc/test/src/__support/CMakeLists.txt @@ -126,5 +126,6 @@ add_subdirectory(CPP) add_subdirectory(File) +add_subdirectory(RPC) add_subdirectory(OSUtil) add_subdirectory(FPUtil) diff --git a/libc/test/src/__support/RPC/CMakeLists.txt b/libc/test/src/__support/RPC/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/libc/test/src/__support/RPC/CMakeLists.txt @@ -0,0 +1,11 @@ +add_custom_target(libc-rpc-tests) + +add_libc_test( + rpc_smoke_test + SUITE + libc-rpc-tests + SRCS + rpc_smoke_test.cpp + DEPENDS + libc.src.__support.RPC.rpc +) diff --git a/libc/test/src/__support/RPC/rpc_smoke_test.cpp b/libc/test/src/__support/RPC/rpc_smoke_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/__support/RPC/rpc_smoke_test.cpp @@ -0,0 +1,82 @@ +//===-- smoke tests for RPC -----------------------------------------------===// +// +// 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 { lane_size = 8, port_count = 4 }; + +using ProcAType = __llvm_libc::rpc::Process; +using ProcBType = __llvm_libc::rpc::Process; + +static_assert(ProcAType::inbox_offset(port_count) == + ProcBType::outbox_offset(port_count)); + +static_assert(ProcAType::outbox_offset(port_count) == + ProcBType::inbox_offset(port_count)); + +enum { alloc_size = ProcAType::allocation_size(port_count) }; + +alignas(64) char buffer[alloc_size] = {0}; +} // namespace + +TEST(LlvmLibcRPCSmoke, SanityCheck) { + + ProcAType ProcA; + ProcBType ProcB; + + ProcA.reset(port_count, buffer); + ProcB.reset(port_count, buffer); + + EXPECT_EQ(ProcA.get_buffer_start(), ProcB.get_buffer_start()); + + uint64_t index = 0; // any < port_count + uint64_t lane_mask = 1; + + // Each process has its own local lock for index + EXPECT_TRUE(ProcA.try_lock(lane_mask, index)); + EXPECT_TRUE(ProcB.try_lock(lane_mask, index)); + + // All zero to begin with + EXPECT_EQ(ProcA.load_inbox(index), 0u); + EXPECT_EQ(ProcB.load_inbox(index), 0u); + EXPECT_EQ(ProcA.load_outbox(index), 0u); + EXPECT_EQ(ProcB.load_outbox(index), 0u); + + // Available for ProcA and not for ProcB + EXPECT_FALSE(ProcA.buffer_unavailable(ProcA.load_inbox(index), + ProcA.load_outbox(index))); + EXPECT_TRUE(ProcB.buffer_unavailable(ProcB.load_inbox(index), + ProcB.load_outbox(index))); + + // ProcA write to outbox + uint32_t ProcAOutbox = ProcA.load_outbox(index); + EXPECT_EQ(ProcAOutbox, 0u); + ProcAOutbox = ProcA.invert_outbox(index, ProcAOutbox); + EXPECT_EQ(ProcAOutbox, 1u); + + // No longer available for ProcA + EXPECT_TRUE(ProcA.buffer_unavailable(ProcA.load_inbox(index), ProcAOutbox)); + + // Outbox is still zero, hasn't been written to + EXPECT_EQ(ProcB.load_outbox(index), 0u); + + // Wait for ownership will terminate because load_inbox returns 1 + EXPECT_EQ(ProcB.load_inbox(index), 1u); + ProcB.wait_for_ownership(index, 0u, 0u); + + // and B now has the buffer available + EXPECT_FALSE(ProcB.buffer_unavailable(ProcB.load_inbox(index), + ProcB.load_outbox(index))); + + // Enough checks for one test, close the locks + ProcA.unlock(lane_mask, index); + ProcB.unlock(lane_mask, index); +}