[libc-commits] [libc] affe132 - Add named posix semaphore lifetime operations on linux (#192278)

via libc-commits libc-commits at lists.llvm.org
Mon May 4 14:50:23 PDT 2026


Author: Pengxiang Huang
Date: 2026-05-04T14:50:18-07:00
New Revision: affe1324278fc91dd44f8bdfc46e281089cf579f

URL: https://github.com/llvm/llvm-project/commit/affe1324278fc91dd44f8bdfc46e281089cf579f
DIFF: https://github.com/llvm/llvm-project/commit/affe1324278fc91dd44f8bdfc46e281089cf579f.diff

LOG: Add named posix semaphore lifetime operations on linux (#192278)

This implements the second part of #190847 

Specifically, this pr adds `sem_open`, `sem_close`, and `sem_unlink` for
posix semaphore on linux.
https://pubs.opengroup.org/onlinepubs/9799919799/functions/sem_open.html

https://pubs.opengroup.org/onlinepubs/9799919799/functions/sem_close.html

https://pubs.opengroup.org/onlinepubs/9799919799/functions/sem_unlink.html

Since it targets on linux implementation, two extra things are added:
1. add system call wrappers for `mmap`, `munmap`, `link`, `unlink`, and
`ftruncate`. Those are necessary for the implementation of semaphore on
linux. Wrappers is added based on the refactor proposal:
https://libc.llvm.org/dev/syscall_wrapper_refactor.html.
2. refactor the previous semaphore implementation, put it under `linux/`
since its based on linux.

Added: 
    libc/src/__support/OSUtil/linux/syscall_wrappers/ftruncate.h
    libc/src/__support/OSUtil/linux/syscall_wrappers/link.h
    libc/src/__support/OSUtil/linux/syscall_wrappers/unlink.h
    libc/src/semaphore/linux/CMakeLists.txt
    libc/src/semaphore/linux/named_semaphore.cpp
    libc/src/semaphore/linux/semaphore.h
    libc/test/src/semaphore/linux/CMakeLists.txt
    libc/test/src/semaphore/linux/semaphore_test.cpp

Modified: 
    libc/src/__support/OSUtil/linux/syscall_wrappers/CMakeLists.txt
    libc/src/semaphore/CMakeLists.txt
    libc/src/sys/mman/linux/shm_common.h
    libc/test/src/semaphore/CMakeLists.txt

Removed: 
    libc/src/semaphore/posix_semaphore.h
    libc/test/src/semaphore/semaphore_test.cpp


################################################################################
diff  --git a/libc/src/__support/OSUtil/linux/syscall_wrappers/CMakeLists.txt b/libc/src/__support/OSUtil/linux/syscall_wrappers/CMakeLists.txt
index 224ad1680ea9f..6418d6f83dbfa 100644
--- a/libc/src/__support/OSUtil/linux/syscall_wrappers/CMakeLists.txt
+++ b/libc/src/__support/OSUtil/linux/syscall_wrappers/CMakeLists.txt
@@ -327,3 +327,41 @@ add_header_library(
     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/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 45da6f1af7966..cbe2c334fb571 100644
--- a/libc/src/semaphore/CMakeLists.txt
+++ b/libc/src/semaphore/CMakeLists.txt
@@ -1,8 +1,7 @@
-add_header_library(
-  posix_semaphore
-  HDRS
-    posix_semaphore.h
-  DEPENDS
-    libc.src.__support.CPP.atomic
-    libc.src.__support.threads.futex_utils
-)
+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/src/semaphore/linux/CMakeLists.txt b/libc/src/semaphore/linux/CMakeLists.txt
new file mode 100644
index 0000000000000..a61075de9b3b9
--- /dev/null
+++ b/libc/src/semaphore/linux/CMakeLists.txt
@@ -0,0 +1,38 @@
+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.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.ctype_utils
+    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
+    libc.src.sys.mman.linux.shm_common
+)

diff  --git a/libc/src/semaphore/linux/named_semaphore.cpp b/libc/src/semaphore/linux/named_semaphore.cpp
new file mode 100644
index 0000000000000..1c162aec4ccc1
--- /dev/null
+++ b/libc/src/semaphore/linux/named_semaphore.cpp
@@ -0,0 +1,195 @@
+//===-- 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/ctype_utils.h"
+#include "src/__support/error_or.h"
+#include "src/__support/macros/config.h"
+#include "src/string/memory_utils/inline_memcpy.h"
+#include "src/sys/mman/linux/shm_common.h"
+
+#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 
diff erent 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 buffer for the temp path.
+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<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.
+  char *dst = path.data() + SEM_TMP_PREFIX.size();
+  for (size_t i = 0; i < RANDOM_SUFFIX_BYTES; ++i) {
+    *dst++ = internal::int_to_b36_char(rand_bytes[i] >> 4);
+    *dst++ = internal::int_to_b36_char(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 = shm_common::translate_name<SEM_PREFIX>(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 = shm_common::translate_name<SEM_PREFIX>(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 11adfb5a209c4..b1f7da42901e5 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/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/src/sys/mman/linux/shm_common.h b/libc/src/sys/mman/linux/shm_common.h
index 9ba8fd1ea100c..feec0ecaca6df 100644
--- a/libc/src/sys/mman/linux/shm_common.h
+++ b/libc/src/sys/mman/linux/shm_common.h
@@ -21,9 +21,19 @@ namespace LIBC_NAMESPACE_DECL {
 namespace shm_common {
 
 LIBC_INLINE_VAR constexpr cpp::string_view SHM_PREFIX = "/dev/shm/";
-using SHMPath = cpp::array<char, NAME_MAX + SHM_PREFIX.size() + 1>;
 
-LIBC_INLINE ErrorOr<SHMPath> translate_name(cpp::string_view name) {
+// Fixed-size buffer for a path of the form: "<Prefix><name>", name is at
+// most NAME_MAX bytes.
+template <const cpp::string_view &Prefix>
+using TranslatedPath = cpp::array<char, NAME_MAX + Prefix.size() + 1>;
+
+using SHMPath = TranslatedPath<SHM_PREFIX>;
+
+// validate a shared-object name and translate it to a path for a
+// giving Prefix.
+template <const cpp::string_view &Prefix = SHM_PREFIX>
+LIBC_INLINE ErrorOr<TranslatedPath<Prefix>>
+translate_name(cpp::string_view name) {
   // trim leading slashes
   size_t offset = name.find_first_not_of('/');
   if (offset == cpp::string_view::npos)
@@ -37,10 +47,10 @@ LIBC_INLINE ErrorOr<SHMPath> translate_name(cpp::string_view name) {
     return Error(EINVAL);
 
   // prepend the prefix
-  SHMPath buffer;
-  inline_memcpy(buffer.data(), SHM_PREFIX.data(), SHM_PREFIX.size());
-  inline_memcpy(buffer.data() + SHM_PREFIX.size(), name.data(), name.size());
-  buffer[SHM_PREFIX.size() + name.size()] = '\0';
+  TranslatedPath<Prefix> buffer;
+  inline_memcpy(buffer.data(), Prefix.data(), Prefix.size());
+  inline_memcpy(buffer.data() + Prefix.size(), name.data(), name.size());
+  buffer[Prefix.size() + name.size()] = '\0';
   return buffer;
 }
 } // namespace shm_common

diff  --git a/libc/test/src/semaphore/CMakeLists.txt b/libc/test/src/semaphore/CMakeLists.txt
index 0c458db5d3669..07095686cac2c 100644
--- a/libc/test/src/semaphore/CMakeLists.txt
+++ b/libc/test/src/semaphore/CMakeLists.txt
@@ -1,11 +1,3 @@
-add_custom_target(libc_semaphore_unittests)
-
-add_libc_unittest(
-  semaphore_test
-  SUITE
-    libc_semaphore_unittests
-  SRCS
-    semaphore_test.cpp
-  DEPENDS
-    libc.src.semaphore.posix_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/linux/semaphore_test.cpp b/libc/test/src/semaphore/linux/semaphore_test.cpp
new file mode 100644
index 0000000000000..ccda4fd7d4fb4
--- /dev/null
+++ b/libc/test/src/semaphore/linux/semaphore_test.cpp
@@ -0,0 +1,114 @@
+//===-- Unittests for the internal Semaphore class ------------------------===//
+//
+// 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 "hdr/errno_macros.h"
+#include "hdr/fcntl_macros.h"
+#include "src/semaphore/linux/semaphore.h"
+#include "test/UnitTest/Test.h"
+
+using LIBC_NAMESPACE::Semaphore;
+
+TEST(LlvmLibcSemaphoreTest, InitAndGetValue) {
+  Semaphore sem(3);
+  ASSERT_TRUE(sem.is_valid());
+  ASSERT_EQ(sem.getvalue(), 3);
+}
+
+TEST(LlvmLibcSemaphoreTest, Destroy) {
+  Semaphore sem(5);
+  ASSERT_TRUE(sem.is_valid());
+  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 with embedded slash.
+  auto r2 = Semaphore::open("/has/slash", O_CREAT, 0644, 0);
+  ASSERT_FALSE(r2.has_value());
+  ASSERT_EQ(r2.error(), EINVAL);
+
+  // just a slash.
+  auto r3 = Semaphore::open("/", O_CREAT, 0644, 0);
+  ASSERT_FALSE(r3.has_value());
+  ASSERT_EQ(r3.error(), EINVAL);
+}

diff  --git a/libc/test/src/semaphore/semaphore_test.cpp b/libc/test/src/semaphore/semaphore_test.cpp
deleted file mode 100644
index 241078ef5a09c..0000000000000
--- a/libc/test/src/semaphore/semaphore_test.cpp
+++ /dev/null
@@ -1,25 +0,0 @@
-//===-- Unittests for the internal Semaphore class ------------------------===//
-//
-// 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/posix_semaphore.h"
-#include "test/UnitTest/Test.h"
-
-using LIBC_NAMESPACE::Semaphore;
-
-TEST(LlvmLibcSemaphoreTest, InitAndGetValue) {
-  Semaphore sem(3);
-  ASSERT_TRUE(sem.is_valid());
-  ASSERT_EQ(sem.getvalue(), 3);
-}
-
-TEST(LlvmLibcSemaphoreTest, Destroy) {
-  Semaphore sem(5);
-  ASSERT_TRUE(sem.is_valid());
-  sem.destroy();
-  ASSERT_FALSE(sem.is_valid());
-}


        


More information about the libc-commits mailing list