[libc-commits] [libc] Add named posix semaphore lifetime operations on linux (PR #192278)
Pengxiang Huang via libc-commits
libc-commits at lists.llvm.org
Wed Apr 15 10:01:39 PDT 2026
https://github.com/Pengxiang-Huang updated https://github.com/llvm/llvm-project/pull/192278
>From c6e784a92fcd9d9a7d3d3cd5fcfa5306eef47035 Mon Sep 17 00:00:00 2001
From: Pengxiang Huang <huangpengxiang70 at gmail.com>
Date: Wed, 15 Apr 2026 11:24:38 -0400
Subject: [PATCH 1/2] add named semaphore lifetime operations
---
.../linux/syscall_wrappers/CMakeLists.txt | 64 +++++
.../OSUtil/linux/syscall_wrappers/ftruncate.h | 41 ++++
.../OSUtil/linux/syscall_wrappers/link.h | 39 +++
.../OSUtil/linux/syscall_wrappers/mmap.h | 44 ++++
.../OSUtil/linux/syscall_wrappers/munmap.h | 31 +++
.../OSUtil/linux/syscall_wrappers/unlink.h | 38 +++
libc/src/semaphore/CMakeLists.txt | 9 +-
libc/src/semaphore/linux/CMakeLists.txt | 36 +++
libc/src/semaphore/linux/named_semaphore.cpp | 223 ++++++++++++++++++
.../{posix_semaphore.h => linux/semaphore.h} | 26 +-
libc/test/src/semaphore/CMakeLists.txt | 5 +-
libc/test/src/semaphore/semaphore_test.cpp | 96 +++++++-
12 files changed, 636 insertions(+), 16 deletions(-)
create mode 100644 libc/src/__support/OSUtil/linux/syscall_wrappers/ftruncate.h
create mode 100644 libc/src/__support/OSUtil/linux/syscall_wrappers/link.h
create mode 100644 libc/src/__support/OSUtil/linux/syscall_wrappers/mmap.h
create mode 100644 libc/src/__support/OSUtil/linux/syscall_wrappers/munmap.h
create mode 100644 libc/src/__support/OSUtil/linux/syscall_wrappers/unlink.h
create mode 100644 libc/src/semaphore/linux/CMakeLists.txt
create mode 100644 libc/src/semaphore/linux/named_semaphore.cpp
rename libc/src/semaphore/{posix_semaphore.h => linux/semaphore.h} (67%)
diff --git a/libc/src/__support/OSUtil/linux/syscall_wrappers/CMakeLists.txt b/libc/src/__support/OSUtil/linux/syscall_wrappers/CMakeLists.txt
index 3d5ef62d09e66..6242b438cad25 100644
--- a/libc/src/__support/OSUtil/linux/syscall_wrappers/CMakeLists.txt
+++ b/libc/src/__support/OSUtil/linux/syscall_wrappers/CMakeLists.txt
@@ -131,3 +131,67 @@ add_header_library(
libc.hdr.types.struct_timespec
libc.include.sys_syscall
)
+
+add_header_library(
+ mmap
+ HDRS
+ mmap.h
+ DEPENDS
+ libc.src.__support.OSUtil.osutil
+ libc.src.__support.common
+ libc.src.__support.error_or
+ libc.src.__support.macros.config
+ libc.hdr.types.off_t
+ libc.include.sys_syscall
+)
+
+add_header_library(
+ munmap
+ HDRS
+ munmap.h
+ DEPENDS
+ 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(
+ ftruncate
+ HDRS
+ ftruncate.h
+ DEPENDS
+ libc.src.__support.OSUtil.osutil
+ libc.src.__support.common
+ libc.src.__support.error_or
+ libc.src.__support.macros.config
+ libc.hdr.types.off_t
+ libc.include.sys_syscall
+)
+
+add_header_library(
+ link
+ HDRS
+ link.h
+ DEPENDS
+ libc.src.__support.OSUtil.osutil
+ libc.src.__support.common
+ libc.src.__support.error_or
+ libc.src.__support.macros.config
+ libc.hdr.fcntl_macros
+ libc.include.sys_syscall
+)
+
+add_header_library(
+ unlink
+ HDRS
+ unlink.h
+ DEPENDS
+ libc.src.__support.OSUtil.osutil
+ libc.src.__support.common
+ libc.src.__support.error_or
+ libc.src.__support.macros.config
+ libc.hdr.fcntl_macros
+ libc.include.sys_syscall
+)
diff --git a/libc/src/__support/OSUtil/linux/syscall_wrappers/ftruncate.h b/libc/src/__support/OSUtil/linux/syscall_wrappers/ftruncate.h
new file mode 100644
index 0000000000000..6835de85d3321
--- /dev/null
+++ b/libc/src/__support/OSUtil/linux/syscall_wrappers/ftruncate.h
@@ -0,0 +1,41 @@
+//===-- Implementation header for ftruncate ---------------------*- 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC___SUPPORT_OSUTIL_LINUX_SYSCALL_WRAPPERS_FTRUNCATE_H
+#define LLVM_LIBC_SRC___SUPPORT_OSUTIL_LINUX_SYSCALL_WRAPPERS_FTRUNCATE_H
+
+#include "hdr/types/off_t.h"
+#include "src/__support/OSUtil/linux/syscall.h" // syscall_impl
+#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> ftruncate(int fd, off_t len) {
+#ifdef SYS_ftruncate
+ int ret = syscall_impl<int>(SYS_ftruncate, fd, len);
+#elif defined(SYS_ftruncate64)
+ // Same as ftruncate but can handle large offsets on 32-bit systems.
+ static_assert(sizeof(off_t) == 8);
+ int ret = syscall_impl<int>(SYS_ftruncate64, fd, (long)len,
+ (long)(((uint64_t)(len)) >> 32));
+#else
+#error "ftruncate and ftruncate64 syscalls not available."
+#endif
+ if (ret < 0)
+ return Error(-static_cast<int>(ret));
+ return 0;
+}
+
+} // namespace linux_syscalls
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC___SUPPORT_OSUTIL_LINUX_SYSCALL_WRAPPERS_FTRUNCATE_H
diff --git a/libc/src/__support/OSUtil/linux/syscall_wrappers/link.h b/libc/src/__support/OSUtil/linux/syscall_wrappers/link.h
new file mode 100644
index 0000000000000..048299b995588
--- /dev/null
+++ b/libc/src/__support/OSUtil/linux/syscall_wrappers/link.h
@@ -0,0 +1,39 @@
+//===-- Implementation header for link --------------------------*- 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC___SUPPORT_OSUTIL_LINUX_SYSCALL_WRAPPERS_LINK_H
+#define LLVM_LIBC_SRC___SUPPORT_OSUTIL_LINUX_SYSCALL_WRAPPERS_LINK_H
+
+#include "hdr/fcntl_macros.h" // AT_FDCWD
+#include "src/__support/OSUtil/linux/syscall.h" // syscall_impl
+#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> link(const char *oldpath, const char *newpath) {
+#ifdef SYS_link
+ int ret = syscall_impl<int>(SYS_link, oldpath, newpath);
+#elif defined(SYS_linkat)
+ int ret =
+ syscall_impl<int>(SYS_linkat, AT_FDCWD, oldpath, AT_FDCWD, newpath, 0);
+#else
+#error "link and linkat syscalls not available."
+#endif
+ if (ret < 0)
+ return Error(-static_cast<int>(ret));
+ return 0;
+}
+
+} // namespace linux_syscalls
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC___SUPPORT_OSUTIL_LINUX_SYSCALL_WRAPPERS_LINK_H
diff --git a/libc/src/__support/OSUtil/linux/syscall_wrappers/mmap.h b/libc/src/__support/OSUtil/linux/syscall_wrappers/mmap.h
new file mode 100644
index 0000000000000..27147307e2cfb
--- /dev/null
+++ b/libc/src/__support/OSUtil/linux/syscall_wrappers/mmap.h
@@ -0,0 +1,44 @@
+//===-- Implementation header for mmap --------------------------*- 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC___SUPPORT_OSUTIL_LINUX_SYSCALL_WRAPPERS_MMAP_H
+#define LLVM_LIBC_SRC___SUPPORT_OSUTIL_LINUX_SYSCALL_WRAPPERS_MMAP_H
+
+#include "hdr/types/off_t.h"
+#include "src/__support/OSUtil/linux/syscall.h" // syscall_impl, is_valid_mmap
+#include "src/__support/common.h"
+#include "src/__support/error_or.h"
+#include "src/__support/macros/config.h"
+#include <linux/param.h> // EXEC_PAGESIZE
+#include <sys/syscall.h> // For syscall numbers
+
+namespace LIBC_NAMESPACE_DECL {
+namespace linux_syscalls {
+
+LIBC_INLINE ErrorOr<void *> mmap(void *addr, size_t size, int prot, int flags,
+ int fd, off_t offset) {
+#ifdef SYS_mmap2
+ long mmap_offset = static_cast<long>(offset / EXEC_PAGESIZE);
+ long ret = syscall_impl<long>(SYS_mmap2, reinterpret_cast<long>(addr), size,
+ prot, flags, fd, mmap_offset);
+#elif defined(SYS_mmap)
+ long mmap_offset = static_cast<long>(offset);
+ long ret = syscall_impl<long>(SYS_mmap, reinterpret_cast<long>(addr), size,
+ prot, flags, fd, mmap_offset);
+#else
+#error "mmap or mmap2 syscalls not available."
+#endif
+ if (!linux_utils::is_valid_mmap(ret))
+ return Error(static_cast<int>(-ret));
+ return reinterpret_cast<void *>(ret);
+}
+
+} // namespace linux_syscalls
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC___SUPPORT_OSUTIL_LINUX_SYSCALL_WRAPPERS_MMAP_H
diff --git a/libc/src/__support/OSUtil/linux/syscall_wrappers/munmap.h b/libc/src/__support/OSUtil/linux/syscall_wrappers/munmap.h
new file mode 100644
index 0000000000000..d6678cc59f7d8
--- /dev/null
+++ b/libc/src/__support/OSUtil/linux/syscall_wrappers/munmap.h
@@ -0,0 +1,31 @@
+//===-- Implementation header for munmap ------------------------*- 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC___SUPPORT_OSUTIL_LINUX_SYSCALL_WRAPPERS_MUNMAP_H
+#define LLVM_LIBC_SRC___SUPPORT_OSUTIL_LINUX_SYSCALL_WRAPPERS_MUNMAP_H
+
+#include "src/__support/OSUtil/linux/syscall.h" // syscall_impl
+#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> munmap(void *addr, size_t size) {
+ int ret = syscall_impl<int>(SYS_munmap, reinterpret_cast<long>(addr), size);
+ if (ret < 0)
+ return Error(-static_cast<int>(ret));
+ return 0;
+}
+
+} // namespace linux_syscalls
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC___SUPPORT_OSUTIL_LINUX_SYSCALL_WRAPPERS_MUNMAP_H
diff --git a/libc/src/__support/OSUtil/linux/syscall_wrappers/unlink.h b/libc/src/__support/OSUtil/linux/syscall_wrappers/unlink.h
new file mode 100644
index 0000000000000..5549f794a3014
--- /dev/null
+++ b/libc/src/__support/OSUtil/linux/syscall_wrappers/unlink.h
@@ -0,0 +1,38 @@
+//===-- Implementation header for unlink ------------------------*- 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC___SUPPORT_OSUTIL_LINUX_SYSCALL_WRAPPERS_UNLINK_H
+#define LLVM_LIBC_SRC___SUPPORT_OSUTIL_LINUX_SYSCALL_WRAPPERS_UNLINK_H
+
+#include "hdr/fcntl_macros.h" // AT_FDCWD
+#include "src/__support/OSUtil/linux/syscall.h" // syscall_impl
+#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> unlink(const char *path) {
+#ifdef SYS_unlink
+ int ret = syscall_impl<int>(SYS_unlink, path);
+#elif defined(SYS_unlinkat)
+ int ret = syscall_impl<int>(SYS_unlinkat, AT_FDCWD, path, 0);
+#else
+#error "unlink and unlinkat syscalls not available."
+#endif
+ if (ret < 0)
+ return Error(-static_cast<int>(ret));
+ return 0;
+}
+
+} // namespace linux_syscalls
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC___SUPPORT_OSUTIL_LINUX_SYSCALL_WRAPPERS_UNLINK_H
diff --git a/libc/src/semaphore/CMakeLists.txt b/libc/src/semaphore/CMakeLists.txt
index ac73eb8e87b56..a1034f9954740 100644
--- a/libc/src/semaphore/CMakeLists.txt
+++ b/libc/src/semaphore/CMakeLists.txt
@@ -1,8 +1 @@
-add_header_library(
- posix_semaphore
- HDRS
- posix_semaphore.h
- DEPENDS
- libc.src.__support.CPP.atomic
- libc.src.__support.threads.linux.futex_utils
-)
+add_subdirectory(linux)
diff --git a/libc/src/semaphore/linux/CMakeLists.txt b/libc/src/semaphore/linux/CMakeLists.txt
new file mode 100644
index 0000000000000..c30b50a1e5166
--- /dev/null
+++ b/libc/src/semaphore/linux/CMakeLists.txt
@@ -0,0 +1,36 @@
+add_header_library(
+ semaphore
+ HDRS
+ semaphore.h
+ DEPENDS
+ libc.hdr.types.mode_t
+ libc.src.__support.CPP.atomic
+ libc.src.__support.error_or
+ libc.src.__support.threads.linux.futex_utils
+)
+
+add_object_library(
+ named_semaphore
+ SRCS
+ named_semaphore.cpp
+ HDRS
+ semaphore.h
+ DEPENDS
+ .semaphore
+ libc.hdr.errno_macros
+ libc.hdr.fcntl_macros
+ libc.src.__support.CPP.array
+ libc.src.__support.CPP.limits
+ libc.src.__support.CPP.new
+ libc.src.__support.CPP.string_view
+ libc.src.__support.error_or
+ libc.src.__support.OSUtil.linux.syscall_wrappers.close
+ libc.src.__support.OSUtil.linux.syscall_wrappers.ftruncate
+ libc.src.__support.OSUtil.linux.syscall_wrappers.getrandom
+ libc.src.__support.OSUtil.linux.syscall_wrappers.link
+ libc.src.__support.OSUtil.linux.syscall_wrappers.mmap
+ libc.src.__support.OSUtil.linux.syscall_wrappers.munmap
+ libc.src.__support.OSUtil.linux.syscall_wrappers.open
+ libc.src.__support.OSUtil.linux.syscall_wrappers.unlink
+ libc.src.string.memory_utils.inline_memcpy
+)
diff --git a/libc/src/semaphore/linux/named_semaphore.cpp b/libc/src/semaphore/linux/named_semaphore.cpp
new file mode 100644
index 0000000000000..e97ac625df13c
--- /dev/null
+++ b/libc/src/semaphore/linux/named_semaphore.cpp
@@ -0,0 +1,223 @@
+//===-- Named semaphore implementation for Linux --------------------------===//
+//
+// 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/semaphore/linux/semaphore.h"
+
+#include "hdr/errno_macros.h"
+#include "hdr/fcntl_macros.h"
+#include "src/__support/CPP/array.h"
+#include "src/__support/CPP/limits.h"
+#include "src/__support/CPP/new.h"
+#include "src/__support/CPP/string_view.h"
+#include "src/__support/OSUtil/linux/syscall_wrappers/close.h"
+#include "src/__support/OSUtil/linux/syscall_wrappers/ftruncate.h"
+#include "src/__support/OSUtil/linux/syscall_wrappers/getrandom.h"
+#include "src/__support/OSUtil/linux/syscall_wrappers/link.h"
+#include "src/__support/OSUtil/linux/syscall_wrappers/mmap.h"
+#include "src/__support/OSUtil/linux/syscall_wrappers/munmap.h"
+#include "src/__support/OSUtil/linux/syscall_wrappers/open.h"
+#include "src/__support/OSUtil/linux/syscall_wrappers/unlink.h"
+#include "src/__support/error_or.h"
+#include "src/__support/macros/config.h"
+#include "src/string/memory_utils/inline_memcpy.h"
+
+#include <linux/limits.h> // NAME_MAX
+#include <linux/mman.h> // PROT_READ, PROT_WRITE, MAP_SHARED
+
+namespace LIBC_NAMESPACE_DECL {
+
+namespace {
+
+// define SEM_VALUE_MAX as INT_MAX
+constexpr unsigned int SEM_VALUE_MAX =
+ static_cast<unsigned int>(cpp::numeric_limits<int>::max());
+
+// Named semaphores are backed by files in /dev/shm/.
+// a prefix "sem." is added to avoid name collision.
+constexpr cpp::string_view SEM_PREFIX = "/dev/shm/sem.";
+
+// use temporary file to solve data race and guarantee atomic publish.
+// Temporary file use different prefix.
+constexpr cpp::string_view SEM_TMP_PREFIX = "/dev/shm/sem.tmp_";
+
+// 8 random bytes from getrandom() produce a 16 character hex suffix, giving
+// 2^64 possible temp names to avoid collision.
+constexpr size_t RANDOM_SUFFIX_BYTES = 8;
+constexpr size_t RANDOM_SUFFIX_HEX_LEN = RANDOM_SUFFIX_BYTES * 2;
+
+// fixed-size buffers for semaphore paths. SemPath holds the final path
+// and TmpPath holds the temp path.
+using SemPath = cpp::array<char, NAME_MAX + SEM_PREFIX.size() + 1>;
+using TmpPath =
+ cpp::array<char, SEM_TMP_PREFIX.size() + RANDOM_SUFFIX_HEX_LEN + 1>;
+
+// O_NOFOLLOW prevents symlink attacks to /dev/shm/. O_CLOEXEC ensures the
+// fd is not leaked to child processes across exec.
+constexpr int DEFAULT_OFLAGS = O_NOFOLLOW | O_CLOEXEC;
+
+ErrorOr<SemPath> translate_name(const char *name) {
+ cpp::string_view sv(name);
+
+ // name must start with '/'.
+ if (sv.empty() || sv.front() != '/')
+ return Error(EINVAL);
+
+ // remove leading '/'.
+ sv = sv.substr(1);
+
+ // name must not be empty, must not contain '/', and must not be "." or "..".
+ if (sv.empty() || sv.contains('/') || sv == "." || sv == "..")
+ return Error(EINVAL);
+
+ // name lenghth must in range.
+ if (sv.size() > NAME_MAX)
+ return Error(ENAMETOOLONG);
+
+ // copy the prefix and name into final name buffer.
+ SemPath buffer;
+ inline_memcpy(buffer.data(), SEM_PREFIX.data(), SEM_PREFIX.size());
+ inline_memcpy(buffer.data() + SEM_PREFIX.size(), sv.data(), sv.size());
+ buffer[SEM_PREFIX.size() + sv.size()] = '\0';
+ return buffer;
+}
+
+ErrorOr<TmpPath> generate_tmp_path() {
+ // fill out 8 random bytes.
+ cpp::array<uint8_t, RANDOM_SUFFIX_BYTES> rand_bytes;
+ auto ret = linux_syscalls::getrandom(rand_bytes.data(), rand_bytes.size(), 0);
+ if (!ret.has_value())
+ return Error(ret.error());
+
+ TmpPath path;
+ inline_memcpy(path.data(), SEM_TMP_PREFIX.data(), SEM_TMP_PREFIX.size());
+
+ // Encode each random byte as two hex digits, and fill out tmp path.
+ constexpr char hex_table[] = "0123456789abcdef";
+ char *dst = path.data() + SEM_TMP_PREFIX.size();
+ for (size_t i = 0; i < RANDOM_SUFFIX_BYTES; ++i) {
+ *dst++ = hex_table[rand_bytes[i] >> 4];
+ *dst++ = hex_table[rand_bytes[i] & 0xf];
+ }
+ *dst = '\0';
+ return path;
+}
+
+// map an open semaphore fd into memory.
+ErrorOr<Semaphore *> map_semaphore(int fd) {
+ auto mmap_or = linux_syscalls::mmap(
+ nullptr, sizeof(Semaphore), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ linux_syscalls::close(fd);
+
+ if (!mmap_or.has_value())
+ return Error(mmap_or.error());
+
+ return reinterpret_cast<Semaphore *>(mmap_or.value());
+}
+
+// open an existing named semaphore file and map it.
+ErrorOr<Semaphore *> open_existing(const char *path) {
+ auto fd_or = linux_syscalls::open(path, O_RDWR | DEFAULT_OFLAGS, 0);
+ if (!fd_or.has_value())
+ return Error(fd_or.error());
+ return map_semaphore(fd_or.value());
+}
+
+} // anonymous namespace
+
+ErrorOr<Semaphore *> Semaphore::open(const char *name, int oflag, mode_t mode,
+ unsigned int value) {
+ auto path_or = translate_name(name);
+ if (!path_or.has_value())
+ return Error(path_or.error());
+
+ // open an existing semaphore.
+ if (!(oflag & O_CREAT))
+ return open_existing(path_or->data());
+
+ // check semaphore value.
+ if (value > SEM_VALUE_MAX)
+ return Error(EINVAL);
+
+ // two step creation:
+ // 1. create and fully initialize a temporary file.
+ // 2. link() it and publish to the final path atomically.
+ // This ensures no other process can observe a partially-initialized
+ // semaphore through the final path. If link() fails with EEXIST and
+ // O_EXCL is not set, fall back to opening the existing semaphore.
+
+ auto tmp_or = generate_tmp_path();
+ if (!tmp_or.has_value())
+ return Error(tmp_or.error());
+
+ // if two process happen to map the same random tmp_path, though rare
+ // in 2^64 namespace, one succees and the other return EEXIST.
+ auto fd_or = linux_syscalls::open(
+ tmp_or->data(), O_RDWR | O_CREAT | O_EXCL | DEFAULT_OFLAGS, mode);
+ if (!fd_or.has_value())
+ return Error(fd_or.error());
+
+ int fd = fd_or.value();
+
+ // resizing temporary semaphore backing file.
+ auto trunc_or =
+ linux_syscalls::ftruncate(fd, static_cast<off_t>(sizeof(Semaphore)));
+ if (!trunc_or.has_value()) {
+ linux_syscalls::close(fd);
+ linux_syscalls::unlink(tmp_or->data());
+ return Error(trunc_or.error());
+ }
+
+ // map_semaphore closes the fd.
+ auto sem_or = map_semaphore(fd);
+ if (!sem_or.has_value()) {
+ linux_syscalls::unlink(tmp_or->data());
+ return Error(sem_or.error());
+ }
+
+ Semaphore *sem = sem_or.value();
+ new (sem) Semaphore(value);
+
+ // atomically publish the fully initialized semaphore.
+ auto link_or = linux_syscalls::link(tmp_or->data(), path_or->data());
+
+ // temp file is no longer needed.
+ linux_syscalls::unlink(tmp_or->data());
+
+ // link() succees
+ if (link_or.has_value())
+ return sem;
+
+ // link() fail, clean up the mapping.
+ linux_syscalls::munmap(sem, sizeof(Semaphore));
+
+ // if the name already exists and O_EXCL was not set, open existing.
+ if (link_or.error() == EEXIST && !(oflag & O_EXCL))
+ return open_existing(path_or->data());
+
+ return Error(link_or.error());
+}
+
+int Semaphore::close(Semaphore *sem) {
+ auto result = linux_syscalls::munmap(sem, sizeof(Semaphore));
+ if (!result.has_value())
+ return result.error();
+ return 0;
+}
+
+int Semaphore::unlink(const char *name) {
+ auto path_or = translate_name(name);
+ if (!path_or.has_value())
+ return path_or.error();
+
+ auto result = linux_syscalls::unlink(path_or->data());
+ if (!result.has_value())
+ return result.error();
+ return 0;
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/semaphore/posix_semaphore.h b/libc/src/semaphore/linux/semaphore.h
similarity index 67%
rename from libc/src/semaphore/posix_semaphore.h
rename to libc/src/semaphore/linux/semaphore.h
index 7b14d41165a96..77198e385301b 100644
--- a/libc/src/semaphore/posix_semaphore.h
+++ b/libc/src/semaphore/linux/semaphore.h
@@ -1,4 +1,4 @@
-//===-- Internal Semaphore implementation for POSIX semaphores ------------===//
+//===-- 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.
@@ -6,11 +6,13 @@
//
//===----------------------------------------------------------------------===//
-#ifndef LLVM_LIBC_SRC_SEMAPHORE_POSIX_SEMAPHORE_H
-#define LLVM_LIBC_SRC_SEMAPHORE_POSIX_SEMAPHORE_H
+#ifndef LLVM_LIBC_SRC_SEMAPHORE_LINUX_SEMAPHORE_H
+#define LLVM_LIBC_SRC_SEMAPHORE_LINUX_SEMAPHORE_H
+#include "hdr/types/mode_t.h"
#include "src/__support/CPP/atomic.h"
#include "src/__support/common.h"
+#include "src/__support/error_or.h"
#include "src/__support/threads/linux/futex_utils.h"
namespace LIBC_NAMESPACE_DECL {
@@ -26,8 +28,7 @@ class Semaphore {
public:
// TODO:
- // 1. Add named semaphore support: sem_open, sem_close, sem_unlink
- // 2. Add the posting and waiting operations: sem_post, sem_wait,
+ // Add the posting and waiting operations: sem_post, sem_wait,
// sem_trywait, sem_timedwait, sem_clockwait.
LIBC_INLINE constexpr Semaphore(unsigned int value)
@@ -52,8 +53,21 @@ class Semaphore {
return static_cast<int>(
const_cast<Futex &>(value).load(cpp::MemoryOrder::RELAXED));
}
+
+ // Named semaphore operations.
+ // creates or opens a named semaphore backed by a file in /dev/shm/.
+ // When O_CREAT is specified in oflag, mode and value are used for
+ // initialization.
+ static ErrorOr<Semaphore *> open(const char *name, int oflag, mode_t mode,
+ unsigned int value);
+
+ // unmaps a named semaphore.
+ static int close(Semaphore *sem);
+
+ // removes a named semaphore from the filesystem.
+ static int unlink(const char *name);
};
} // namespace LIBC_NAMESPACE_DECL
-#endif // LLVM_LIBC_SRC_SEMAPHORE_POSIX_SEMAPHORE_H
+#endif // LLVM_LIBC_SRC_SEMAPHORE_LINUX_SEMAPHORE_H
diff --git a/libc/test/src/semaphore/CMakeLists.txt b/libc/test/src/semaphore/CMakeLists.txt
index 0c458db5d3669..2c4ae4dca3e39 100644
--- a/libc/test/src/semaphore/CMakeLists.txt
+++ b/libc/test/src/semaphore/CMakeLists.txt
@@ -7,5 +7,8 @@ add_libc_unittest(
SRCS
semaphore_test.cpp
DEPENDS
- libc.src.semaphore.posix_semaphore
+ libc.hdr.errno_macros
+ libc.hdr.fcntl_macros
+ libc.src.semaphore.linux.semaphore
+ libc.src.semaphore.linux.named_semaphore
)
diff --git a/libc/test/src/semaphore/semaphore_test.cpp b/libc/test/src/semaphore/semaphore_test.cpp
index 241078ef5a09c..b275a00882438 100644
--- a/libc/test/src/semaphore/semaphore_test.cpp
+++ b/libc/test/src/semaphore/semaphore_test.cpp
@@ -6,7 +6,9 @@
//
//===----------------------------------------------------------------------===//
-#include "src/semaphore/posix_semaphore.h"
+#include "hdr/errno_macros.h"
+#include "hdr/fcntl_macros.h"
+#include "src/semaphore/linux/semaphore.h"
#include "test/UnitTest/Test.h"
using LIBC_NAMESPACE::Semaphore;
@@ -23,3 +25,95 @@ TEST(LlvmLibcSemaphoreTest, Destroy) {
sem.destroy();
ASSERT_FALSE(sem.is_valid());
}
+
+// Named semaphore tests.
+
+TEST(LlvmLibcSemaphoreTest, NamedOpenCloseUnlink) {
+ const char *name = "/llvmlibc_test_sem";
+
+ // clean up any leftover from previous test runs.
+ Semaphore::unlink(name);
+
+ // create a new named semaphore.
+ auto result = Semaphore::open(name, O_CREAT | O_EXCL, 0644, 7);
+ ASSERT_TRUE(result.has_value());
+
+ Semaphore *sem = result.value();
+ ASSERT_TRUE(sem->is_valid());
+ ASSERT_EQ(sem->getvalue(), 7);
+
+ // close and unlink.
+ ASSERT_EQ(Semaphore::close(sem), 0);
+ ASSERT_EQ(Semaphore::unlink(name), 0);
+}
+
+TEST(LlvmLibcSemaphoreTest, NamedOpenExisting) {
+ const char *name = "/llvmlibc_test_sem_exist";
+
+ Semaphore::unlink(name);
+
+ // create a named semaphore.
+ auto r1 = Semaphore::open(name, O_CREAT | O_EXCL, 0644, 10);
+ ASSERT_TRUE(r1.has_value());
+
+ Semaphore *sem1 = r1.value();
+ ASSERT_EQ(sem1->getvalue(), 10);
+
+ // open the same semaphore again without O_EXCL.
+ auto r2 = Semaphore::open(name, O_CREAT, 0644, 99);
+ ASSERT_TRUE(r2.has_value());
+
+ Semaphore *sem2 = r2.value();
+ ASSERT_EQ(sem2->getvalue(), 10);
+
+ ASSERT_EQ(Semaphore::close(sem2), 0);
+ ASSERT_EQ(Semaphore::close(sem1), 0);
+ ASSERT_EQ(Semaphore::unlink(name), 0);
+}
+
+TEST(LlvmLibcSemaphoreTest, NamedOpenExclFails) {
+ const char *name = "/llvmlibc_test_sem_excl";
+
+ Semaphore::unlink(name);
+
+ // create a named semaphore.
+ auto r1 = Semaphore::open(name, O_CREAT | O_EXCL, 0644, 1);
+ ASSERT_TRUE(r1.has_value());
+
+ // trying O_CREAT | O_EXCL again should fail with EEXIST.
+ auto r2 = Semaphore::open(name, O_CREAT | O_EXCL, 0644, 1);
+ ASSERT_FALSE(r2.has_value());
+ ASSERT_EQ(r2.error(), EEXIST);
+
+ ASSERT_EQ(Semaphore::close(r1.value()), 0);
+ ASSERT_EQ(Semaphore::unlink(name), 0);
+}
+
+TEST(LlvmLibcSemaphoreTest, NamedOpenNonExistent) {
+ // opening a non-existent semaphore without O_CREAT should fail.
+ auto result = Semaphore::open("/llvmlibc_nonexistent", 0, 0, 0);
+ ASSERT_FALSE(result.has_value());
+ ASSERT_EQ(result.error(), ENOENT);
+}
+
+TEST(LlvmLibcSemaphoreTest, NamedOpenInvalidName) {
+ // empty name.
+ auto r1 = Semaphore::open("", O_CREAT, 0644, 0);
+ ASSERT_FALSE(r1.has_value());
+ ASSERT_EQ(r1.error(), EINVAL);
+
+ // name without leading slash.
+ auto r2 = Semaphore::open("noslashtosem", O_CREAT, 0644, 0);
+ ASSERT_FALSE(r2.has_value());
+ ASSERT_EQ(r2.error(), EINVAL);
+
+ // name with embedded slash.
+ auto r3 = Semaphore::open("/has/slash", O_CREAT, 0644, 0);
+ ASSERT_FALSE(r3.has_value());
+ ASSERT_EQ(r3.error(), EINVAL);
+
+ // just a slash.
+ auto r4 = Semaphore::open("/", O_CREAT, 0644, 0);
+ ASSERT_FALSE(r4.has_value());
+ ASSERT_EQ(r4.error(), EINVAL);
+}
>From d2026375f7aed2750df5738a2a14e031deb3bf18 Mon Sep 17 00:00:00 2001
From: Pengxiang Huang <huangpengxiang70 at gmail.com>
Date: Wed, 15 Apr 2026 13:01:27 -0400
Subject: [PATCH 2/2] update cmake; add cmake guards
---
libc/src/semaphore/CMakeLists.txt | 8 +++++++-
libc/test/src/semaphore/CMakeLists.txt | 17 +++--------------
libc/test/src/semaphore/linux/CMakeLists.txt | 14 ++++++++++++++
.../semaphore/{ => linux}/semaphore_test.cpp | 0
4 files changed, 24 insertions(+), 15 deletions(-)
create mode 100644 libc/test/src/semaphore/linux/CMakeLists.txt
rename libc/test/src/semaphore/{ => linux}/semaphore_test.cpp (100%)
diff --git a/libc/src/semaphore/CMakeLists.txt b/libc/src/semaphore/CMakeLists.txt
index a1034f9954740..cbe2c334fb571 100644
--- a/libc/src/semaphore/CMakeLists.txt
+++ b/libc/src/semaphore/CMakeLists.txt
@@ -1 +1,7 @@
-add_subdirectory(linux)
+if(NOT TARGET libc.src.__support.OSUtil.osutil)
+ return()
+endif()
+
+if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS})
+ add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS})
+endif()
diff --git a/libc/test/src/semaphore/CMakeLists.txt b/libc/test/src/semaphore/CMakeLists.txt
index 2c4ae4dca3e39..07095686cac2c 100644
--- a/libc/test/src/semaphore/CMakeLists.txt
+++ b/libc/test/src/semaphore/CMakeLists.txt
@@ -1,14 +1,3 @@
-add_custom_target(libc_semaphore_unittests)
-
-add_libc_unittest(
- semaphore_test
- SUITE
- libc_semaphore_unittests
- SRCS
- semaphore_test.cpp
- DEPENDS
- libc.hdr.errno_macros
- libc.hdr.fcntl_macros
- libc.src.semaphore.linux.semaphore
- libc.src.semaphore.linux.named_semaphore
-)
+if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS})
+ add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS})
+endif()
diff --git a/libc/test/src/semaphore/linux/CMakeLists.txt b/libc/test/src/semaphore/linux/CMakeLists.txt
new file mode 100644
index 0000000000000..2c4ae4dca3e39
--- /dev/null
+++ b/libc/test/src/semaphore/linux/CMakeLists.txt
@@ -0,0 +1,14 @@
+add_custom_target(libc_semaphore_unittests)
+
+add_libc_unittest(
+ semaphore_test
+ SUITE
+ libc_semaphore_unittests
+ SRCS
+ semaphore_test.cpp
+ DEPENDS
+ libc.hdr.errno_macros
+ libc.hdr.fcntl_macros
+ libc.src.semaphore.linux.semaphore
+ libc.src.semaphore.linux.named_semaphore
+)
diff --git a/libc/test/src/semaphore/semaphore_test.cpp b/libc/test/src/semaphore/linux/semaphore_test.cpp
similarity index 100%
rename from libc/test/src/semaphore/semaphore_test.cpp
rename to libc/test/src/semaphore/linux/semaphore_test.cpp
More information about the libc-commits
mailing list