[libc-commits] [libc] [libc][semaphore] Add unnamed semaphore implementation (PR #190851)
Pengxiang Huang via libc-commits
libc-commits at lists.llvm.org
Wed Apr 8 08:06:06 PDT 2026
https://github.com/Pengxiang-Huang updated https://github.com/llvm/llvm-project/pull/190851
>From cb8c1252535c5070c645eb6695c0a67e61d0d923 Mon Sep 17 00:00:00 2001
From: Pengxiang Huang <huangpengxiang70 at gmail.com>
Date: Tue, 7 Apr 2026 16:40:46 -0400
Subject: [PATCH 1/2] add unamed semaphore implementation
---
libc/config/linux/x86_64/entrypoints.txt | 5 ++
libc/hdr/types/CMakeLists.txt | 8 +++
libc/hdr/types/sem_t.h | 22 ++++++
libc/include/CMakeLists.txt | 9 +++
libc/include/limits.yaml | 2 +
libc/include/llvm-libc-macros/limits-macros.h | 4 ++
libc/include/llvm-libc-types/CMakeLists.txt | 1 +
libc/include/llvm-libc-types/sem_t.h | 20 ++++++
libc/include/semaphore.yaml | 21 ++++++
libc/src/CMakeLists.txt | 1 +
libc/src/semaphore/CMakeLists.txt | 49 +++++++++++++
libc/src/semaphore/posix_semaphore.h | 72 +++++++++++++++++++
libc/src/semaphore/sem_destroy.cpp | 29 ++++++++
libc/src/semaphore/sem_destroy.h | 21 ++++++
libc/src/semaphore/sem_getvalue.cpp | 33 +++++++++
libc/src/semaphore/sem_getvalue.h | 21 ++++++
libc/src/semaphore/sem_init.cpp | 32 +++++++++
libc/src/semaphore/sem_init.h | 21 ++++++
libc/test/src/CMakeLists.txt | 1 +
libc/test/src/semaphore/CMakeLists.txt | 19 +++++
libc/test/src/semaphore/semaphore_test.cpp | 51 +++++++++++++
21 files changed, 442 insertions(+)
create mode 100644 libc/hdr/types/sem_t.h
create mode 100644 libc/include/llvm-libc-types/sem_t.h
create mode 100644 libc/include/semaphore.yaml
create mode 100644 libc/src/semaphore/CMakeLists.txt
create mode 100644 libc/src/semaphore/posix_semaphore.h
create mode 100644 libc/src/semaphore/sem_destroy.cpp
create mode 100644 libc/src/semaphore/sem_destroy.h
create mode 100644 libc/src/semaphore/sem_getvalue.cpp
create mode 100644 libc/src/semaphore/sem_getvalue.h
create mode 100644 libc/src/semaphore/sem_init.cpp
create mode 100644 libc/src/semaphore/sem_init.h
create mode 100644 libc/test/src/semaphore/CMakeLists.txt
create mode 100644 libc/test/src/semaphore/semaphore_test.cpp
diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index 90a0dade5435a..72fc463aac362 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -1233,6 +1233,11 @@ if(LLVM_LIBC_FULL_BUILD)
libc.src.pthread.pthread_spin_lock
libc.src.pthread.pthread_spin_trylock
libc.src.pthread.pthread_spin_unlock
+
+ # semaphore.h entrypoints
+ libc.src.semaphore.sem_destroy
+ libc.src.semaphore.sem_getvalue
+ libc.src.semaphore.sem_init
libc.src.pthread.pthread_self
libc.src.pthread.pthread_setname_np
libc.src.pthread.pthread_setspecific
diff --git a/libc/hdr/types/CMakeLists.txt b/libc/hdr/types/CMakeLists.txt
index 7e1a8974fa486..0862f0f9efecf 100644
--- a/libc/hdr/types/CMakeLists.txt
+++ b/libc/hdr/types/CMakeLists.txt
@@ -282,6 +282,14 @@ add_proxy_header_library(
libc.include.llvm-libc-types.pid_t
)
+add_proxy_header_library(
+ sem_t
+ HDRS
+ sem_t.h
+ FULL_BUILD_DEPENDS
+ libc.include.llvm-libc-types.sem_t
+)
+
add_proxy_header_library(
pthread_barrier_t
HDRS
diff --git a/libc/hdr/types/sem_t.h b/libc/hdr/types/sem_t.h
new file mode 100644
index 0000000000000..e057bca6b98df
--- /dev/null
+++ b/libc/hdr/types/sem_t.h
@@ -0,0 +1,22 @@
+//===-- Definition of sem_t type -----------------------------------------===//
+//
+// 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_HDR_TYPES_SEM_T_H
+#define LLVM_LIBC_HDR_TYPES_SEM_T_H
+
+#ifdef LIBC_FULL_BUILD
+
+#include "include/llvm-libc-types/sem_t.h"
+
+#else // Overlay mode
+
+#error "Cannot overlay sem_t"
+
+#endif // LLVM_LIBC_FULL_BUILD
+
+#endif // LLVM_LIBC_HDR_TYPES_SEM_T_H
diff --git a/libc/include/CMakeLists.txt b/libc/include/CMakeLists.txt
index 3375e4e21e338..aa289a4f6fa88 100644
--- a/libc/include/CMakeLists.txt
+++ b/libc/include/CMakeLists.txt
@@ -471,6 +471,15 @@ add_header_macro(
.llvm-libc-types.struct_timespec
)
+add_header_macro(
+ semaphore
+ ../libc/include/semaphore.yaml
+ semaphore.h
+ DEPENDS
+ .llvm-libc-types.sem_t
+ .llvm_libc_common_h
+)
+
add_header_macro(
spawn
../libc/include/spawn.yaml
diff --git a/libc/include/limits.yaml b/libc/include/limits.yaml
index 1f0956128fb00..f7576d89a5d80 100644
--- a/libc/include/limits.yaml
+++ b/libc/include/limits.yaml
@@ -52,6 +52,8 @@ macros:
macro_header: limits-macros.h
- macro_name: ULLONG_MAX
macro_header: limits-macros.h
+ - macro_name: SEM_VALUE_MAX
+ macro_header: limits-macros.h
- macro_name: SCHAR_MIN
macro_header: limits-macros.h
- macro_name: UCHAR_MIN
diff --git a/libc/include/llvm-libc-macros/limits-macros.h b/libc/include/llvm-libc-macros/limits-macros.h
index 79bbbe401eb02..0f999b2bedd19 100644
--- a/libc/include/llvm-libc-macros/limits-macros.h
+++ b/libc/include/llvm-libc-macros/limits-macros.h
@@ -173,6 +173,10 @@
#define ULLONG_MAX (LLONG_MAX * 2ULL + 1ULL)
#endif // ULLONG_MAX
+#ifndef SEM_VALUE_MAX
+#define SEM_VALUE_MAX INT_MAX
+#endif // SEM_VALUE_MAX
+
// *_MIN macros
#ifndef SCHAR_MIN
diff --git a/libc/include/llvm-libc-types/CMakeLists.txt b/libc/include/llvm-libc-types/CMakeLists.txt
index 0d6bc0982b847..ec4719a5e57a1 100644
--- a/libc/include/llvm-libc-types/CMakeLists.txt
+++ b/libc/include/llvm-libc-types/CMakeLists.txt
@@ -84,6 +84,7 @@ add_header(pthread_rwlockattr_t HDR pthread_rwlockattr_t.h)
add_header(pthread_spinlock_t HDR pthread_spinlock_t.h DEPENDS .pid_t)
add_header(pthread_t HDR pthread_t.h DEPENDS .__thread_type)
add_header(rlim_t HDR rlim_t.h)
+add_header(sem_t HDR sem_t.h DEPENDS .__futex_word)
if(LIBC_TYPES_TIME_T_IS_32_BIT)
add_header(time_t HDR time_t_32.h DEST_HDR time_t.h)
else()
diff --git a/libc/include/llvm-libc-types/sem_t.h b/libc/include/llvm-libc-types/sem_t.h
new file mode 100644
index 0000000000000..b9596f4e65707
--- /dev/null
+++ b/libc/include/llvm-libc-types/sem_t.h
@@ -0,0 +1,20 @@
+//===-- Definition of sem_t type -----------------------------------------===//
+//
+// 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_TYPES_SEM_T_H
+#define LLVM_LIBC_TYPES_SEM_T_H
+
+#include "__futex_word.h"
+
+typedef struct {
+ __futex_word __value; // current semaphore count
+ unsigned int __canary; // used for sanity check
+ unsigned char __reserved[8]; // for future usage, total fixed size 16 bytes
+} sem_t;
+
+#endif // LLVM_LIBC_TYPES_SEM_T_H
diff --git a/libc/include/semaphore.yaml b/libc/include/semaphore.yaml
new file mode 100644
index 0000000000000..a56e2eca1c390
--- /dev/null
+++ b/libc/include/semaphore.yaml
@@ -0,0 +1,21 @@
+header: semaphore.h
+standards:
+ - posix
+types:
+ - type_name: sem_t
+functions:
+ - name: sem_destroy
+ return_type: int
+ arguments:
+ - type: sem_t *
+ - name: sem_getvalue
+ return_type: int
+ arguments:
+ - type: sem_t *__restrict
+ - type: int *__restrict
+ - name: sem_init
+ return_type: int
+ arguments:
+ - type: sem_t *
+ - type: int
+ - type: unsigned int
diff --git a/libc/src/CMakeLists.txt b/libc/src/CMakeLists.txt
index 8a0acccaed708..e4b0de3d5c75d 100644
--- a/libc/src/CMakeLists.txt
+++ b/libc/src/CMakeLists.txt
@@ -25,6 +25,7 @@ if(${LIBC_TARGET_OS} STREQUAL "linux")
add_subdirectory(poll)
add_subdirectory(pthread)
add_subdirectory(sched)
+ add_subdirectory(semaphore)
add_subdirectory(sys)
add_subdirectory(termios)
endif()
diff --git a/libc/src/semaphore/CMakeLists.txt b/libc/src/semaphore/CMakeLists.txt
new file mode 100644
index 0000000000000..83572bc0464c3
--- /dev/null
+++ b/libc/src/semaphore/CMakeLists.txt
@@ -0,0 +1,49 @@
+add_header_library(
+ posix_semaphore
+ HDRS
+ posix_semaphore.h
+ DEPENDS
+ libc.hdr.types.sem_t
+ libc.src.__support.CPP.atomic
+ libc.src.__support.threads.linux.futex_word_type
+)
+
+add_entrypoint_object(
+ sem_init
+ SRCS
+ sem_init.cpp
+ HDRS
+ sem_init.h
+ DEPENDS
+ .posix_semaphore
+ libc.hdr.errno_macros
+ libc.hdr.limits_macros
+ libc.include.semaphore
+ libc.src.errno.errno
+)
+
+add_entrypoint_object(
+ sem_destroy
+ SRCS
+ sem_destroy.cpp
+ HDRS
+ sem_destroy.h
+ DEPENDS
+ .posix_semaphore
+ libc.hdr.errno_macros
+ libc.include.semaphore
+ libc.src.errno.errno
+)
+
+add_entrypoint_object(
+ sem_getvalue
+ SRCS
+ sem_getvalue.cpp
+ HDRS
+ sem_getvalue.h
+ DEPENDS
+ .posix_semaphore
+ libc.hdr.errno_macros
+ libc.include.semaphore
+ libc.src.errno.errno
+)
diff --git a/libc/src/semaphore/posix_semaphore.h b/libc/src/semaphore/posix_semaphore.h
new file mode 100644
index 0000000000000..50bcba63f5a7d
--- /dev/null
+++ b/libc/src/semaphore/posix_semaphore.h
@@ -0,0 +1,72 @@
+//===-- Shared helpers 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_SEMAPHORE_POSIX_SEMAPHORE_H
+#define LLVM_LIBC_SRC_SEMAPHORE_POSIX_SEMAPHORE_H
+
+#include "hdr/types/sem_t.h"
+#include "src/__support/CPP/atomic.h"
+#include "src/__support/common.h"
+#include "src/__support/threads/linux/futex_word.h"
+
+namespace LIBC_NAMESPACE_DECL {
+namespace sem_utils {
+
+// 0x53 = S, 0x45 = E, 0x4D = M, 0x31 = 1
+// canary value: SEM1 in hex
+LIBC_INLINE_VAR constexpr unsigned int SEM_CANARY = 0x53454D31U;
+
+static_assert(sizeof(__futex_word) == sizeof(FutexWordType));
+static_assert(alignof(__futex_word) >= alignof(FutexWordType));
+
+// TODO:
+// 1. Add named semaphore support: sem_open, sem_close, sem_unlink
+// 2. Add the posting and waiting operations: sem_post, sem_wait,
+// sem_trywait, sem_timedwait, sem_clockwait.
+
+LIBC_INLINE FutexWordType *value_ptr(sem_t *sem) {
+ return &sem->__value.__word;
+}
+
+// get the atomic reference for sem->__value
+LIBC_INLINE cpp::AtomicRef<FutexWordType> value(sem_t *sem) {
+ return cpp::AtomicRef<FutexWordType>(*value_ptr(sem));
+}
+
+LIBC_INLINE void initialize(sem_t *sem, unsigned int initial_value) {
+ // used in sem_init
+ // init happens before the semaphore is published to any threads
+ // initialize a initialized semaphore is undefined
+ // so RELAXED ordering is enough
+ value(sem).store(initial_value, cpp::MemoryOrder::RELAXED);
+ sem->__canary = SEM_CANARY;
+ for (unsigned char &byte : sem->__reserved)
+ byte = 0;
+}
+
+LIBC_INLINE bool is_valid(const sem_t *sem) {
+ // sanity check for a given semaphore pointer
+ return sem != nullptr && sem->__canary == SEM_CANARY;
+}
+
+LIBC_INLINE void invalidate(sem_t *sem) {
+ // used in sem_destroy
+ // invalidate is safe only when no threads is using
+ // blocked by destroyed semaphore is undefined
+ // use a destroyed semaphore is undefined
+ // RELAXED ordering is enough
+ value(sem).store(0, cpp::MemoryOrder::RELAXED);
+ sem->__canary = 0;
+ for (unsigned char &byte : sem->__reserved)
+ byte = 0;
+}
+
+} // namespace sem_utils
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_SEMAPHORE_POSIX_SEMAPHORE_H
diff --git a/libc/src/semaphore/sem_destroy.cpp b/libc/src/semaphore/sem_destroy.cpp
new file mode 100644
index 0000000000000..c64e18a88e639
--- /dev/null
+++ b/libc/src/semaphore/sem_destroy.cpp
@@ -0,0 +1,29 @@
+//===-- Implementation of sem_destroy ------------------------------------===//
+//
+// 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/sem_destroy.h"
+
+#include "src/semaphore/posix_semaphore.h"
+
+#include "hdr/errno_macros.h"
+#include "src/__support/common.h"
+#include "src/__support/libc_errno.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(int, sem_destroy, (sem_t * sem)) {
+ if (!sem_utils::is_valid(sem)) {
+ libc_errno = EINVAL;
+ return -1;
+ }
+
+ sem_utils::invalidate(sem);
+ return 0;
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/semaphore/sem_destroy.h b/libc/src/semaphore/sem_destroy.h
new file mode 100644
index 0000000000000..b664cf97eecf0
--- /dev/null
+++ b/libc/src/semaphore/sem_destroy.h
@@ -0,0 +1,21 @@
+//===-- Implementation header for sem_destroy ------------------*- 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_SEMAPHORE_SEM_DESTROY_H
+#define LLVM_LIBC_SRC_SEMAPHORE_SEM_DESTROY_H
+
+#include "hdr/types/sem_t.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+int sem_destroy(sem_t *sem);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_SEMAPHORE_SEM_DESTROY_H
diff --git a/libc/src/semaphore/sem_getvalue.cpp b/libc/src/semaphore/sem_getvalue.cpp
new file mode 100644
index 0000000000000..ea84c1e8a6241
--- /dev/null
+++ b/libc/src/semaphore/sem_getvalue.cpp
@@ -0,0 +1,33 @@
+//===-- Implementation of sem_getvalue -----------------------------------===//
+//
+// 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/sem_getvalue.h"
+
+#include "src/semaphore/posix_semaphore.h"
+
+#include "hdr/errno_macros.h"
+#include "src/__support/common.h"
+#include "src/__support/libc_errno.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(int, sem_getvalue,
+ (sem_t *__restrict sem, int *__restrict sval)) {
+ if (!sem_utils::is_valid(sem) || sval == nullptr) {
+ libc_errno = EINVAL;
+ return -1;
+ }
+
+ // get value is informational but not a synchronization op
+ // RELAXED ordering is enough
+ *sval =
+ static_cast<int>(sem_utils::value(sem).load(cpp::MemoryOrder::RELAXED));
+ return 0;
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/semaphore/sem_getvalue.h b/libc/src/semaphore/sem_getvalue.h
new file mode 100644
index 0000000000000..4ba49491746b1
--- /dev/null
+++ b/libc/src/semaphore/sem_getvalue.h
@@ -0,0 +1,21 @@
+//===-- Implementation header for sem_getvalue -----------------*- 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_SEMAPHORE_SEM_GETVALUE_H
+#define LLVM_LIBC_SRC_SEMAPHORE_SEM_GETVALUE_H
+
+#include "hdr/types/sem_t.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+int sem_getvalue(sem_t *__restrict sem, int *__restrict sval);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_SEMAPHORE_SEM_GETVALUE_H
diff --git a/libc/src/semaphore/sem_init.cpp b/libc/src/semaphore/sem_init.cpp
new file mode 100644
index 0000000000000..a3f6e452b6e6c
--- /dev/null
+++ b/libc/src/semaphore/sem_init.cpp
@@ -0,0 +1,32 @@
+//===-- Implementation of sem_init ---------------------------------------===//
+//
+// 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/sem_init.h"
+
+#include "src/semaphore/posix_semaphore.h"
+
+#include "hdr/errno_macros.h"
+#include "hdr/limits_macros.h"
+#include "src/__support/common.h"
+#include "src/__support/libc_errno.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(int, sem_init,
+ (sem_t * sem, int pshared, unsigned int value)) {
+ if (sem == nullptr || value > SEM_VALUE_MAX) {
+ libc_errno = EINVAL;
+ return -1;
+ }
+
+ (void)pshared;
+ sem_utils::initialize(sem, value);
+ return 0;
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/semaphore/sem_init.h b/libc/src/semaphore/sem_init.h
new file mode 100644
index 0000000000000..8a3114ddfa10f
--- /dev/null
+++ b/libc/src/semaphore/sem_init.h
@@ -0,0 +1,21 @@
+//===-- Implementation header for sem_init ----------------------*- 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_SEMAPHORE_SEM_INIT_H
+#define LLVM_LIBC_SRC_SEMAPHORE_SEM_INIT_H
+
+#include "hdr/types/sem_t.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+int sem_init(sem_t *sem, int pshared, unsigned int value);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_SEMAPHORE_SEM_INIT_H
diff --git a/libc/test/src/CMakeLists.txt b/libc/test/src/CMakeLists.txt
index 5e61c739c066a..236bee337d525 100644
--- a/libc/test/src/CMakeLists.txt
+++ b/libc/test/src/CMakeLists.txt
@@ -106,4 +106,5 @@ add_subdirectory(spawn)
if(${LIBC_TARGET_OS} STREQUAL "linux")
add_subdirectory(pthread)
+ add_subdirectory(semaphore)
endif()
diff --git a/libc/test/src/semaphore/CMakeLists.txt b/libc/test/src/semaphore/CMakeLists.txt
new file mode 100644
index 0000000000000..259c11fe55f07
--- /dev/null
+++ b/libc/test/src/semaphore/CMakeLists.txt
@@ -0,0 +1,19 @@
+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.include.limits
+ libc.include.semaphore
+ libc.src.errno.errno
+ libc.src.semaphore.sem_destroy
+ libc.src.semaphore.sem_getvalue
+ libc.src.semaphore.sem_init
+ libc.test.UnitTest.ErrnoCheckingTest
+ libc.test.UnitTest.ErrnoSetterMatcher
+)
diff --git a/libc/test/src/semaphore/semaphore_test.cpp b/libc/test/src/semaphore/semaphore_test.cpp
new file mode 100644
index 0000000000000..1e67bb046ad5f
--- /dev/null
+++ b/libc/test/src/semaphore/semaphore_test.cpp
@@ -0,0 +1,51 @@
+//===-- Unittests 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "hdr/errno_macros.h"
+#include "src/semaphore/sem_destroy.h"
+#include "src/semaphore/sem_getvalue.h"
+#include "src/semaphore/sem_init.h"
+#include "test/UnitTest/ErrnoCheckingTest.h"
+#include "test/UnitTest/ErrnoSetterMatcher.h"
+#include "test/UnitTest/Test.h"
+
+#include <limits.h>
+#include <semaphore.h>
+
+using namespace LIBC_NAMESPACE::testing::ErrnoSetterMatcher;
+
+namespace {
+
+using LlvmLibcSemaphoreTest = LIBC_NAMESPACE::testing::ErrnoCheckingTest;
+
+} // namespace
+
+TEST_F(LlvmLibcSemaphoreTest, PublicSurface) {
+ sem_t sem = {};
+ (void)sem;
+ ASSERT_GE(SEM_VALUE_MAX, 32767);
+}
+
+TEST_F(LlvmLibcSemaphoreTest, UnnamedLifecycle) {
+ sem_t sem;
+ int value = -1;
+
+ ASSERT_THAT(LIBC_NAMESPACE::sem_init(&sem, 0, 3), Succeeds());
+ ASSERT_THAT(LIBC_NAMESPACE::sem_getvalue(&sem, &value), Succeeds());
+ ASSERT_EQ(value, 3);
+
+ ASSERT_THAT(LIBC_NAMESPACE::sem_destroy(&sem), Succeeds());
+ ASSERT_THAT(LIBC_NAMESPACE::sem_getvalue(&sem, &value), Fails(EINVAL));
+}
+
+TEST_F(LlvmLibcSemaphoreTest, UnnamedInitRejectsTooLargeValue) {
+ sem_t sem;
+ ASSERT_THAT(LIBC_NAMESPACE::sem_init(
+ &sem, 0, static_cast<unsigned int>(SEM_VALUE_MAX) + 1U),
+ Fails(EINVAL));
+}
>From f06596f1b4af73fc41f7f51d96e9f4d38dd828fd Mon Sep 17 00:00:00 2001
From: Pengxiang Huang <huangpengxiang70 at gmail.com>
Date: Wed, 8 Apr 2026 11:05:05 -0400
Subject: [PATCH 2/2] overlay the sem_t with internal Semaphore type
---
libc/src/semaphore/CMakeLists.txt | 2 +-
libc/src/semaphore/posix_semaphore.h | 85 +++++++++++++---------------
libc/src/semaphore/sem_destroy.cpp | 5 +-
libc/src/semaphore/sem_getvalue.cpp | 8 +--
libc/src/semaphore/sem_init.cpp | 2 +-
5 files changed, 48 insertions(+), 54 deletions(-)
diff --git a/libc/src/semaphore/CMakeLists.txt b/libc/src/semaphore/CMakeLists.txt
index 83572bc0464c3..4a805059ca20f 100644
--- a/libc/src/semaphore/CMakeLists.txt
+++ b/libc/src/semaphore/CMakeLists.txt
@@ -5,7 +5,7 @@ add_header_library(
DEPENDS
libc.hdr.types.sem_t
libc.src.__support.CPP.atomic
- libc.src.__support.threads.linux.futex_word_type
+ libc.src.__support.threads.linux.futex_utils
)
add_entrypoint_object(
diff --git a/libc/src/semaphore/posix_semaphore.h b/libc/src/semaphore/posix_semaphore.h
index 50bcba63f5a7d..f42dbbb720420 100644
--- a/libc/src/semaphore/posix_semaphore.h
+++ b/libc/src/semaphore/posix_semaphore.h
@@ -1,4 +1,4 @@
-//===-- Shared helpers for POSIX semaphores -------------------------------===//
+//===-- Internal Semaphore types and helpers 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.
@@ -12,61 +12,56 @@
#include "hdr/types/sem_t.h"
#include "src/__support/CPP/atomic.h"
#include "src/__support/common.h"
-#include "src/__support/threads/linux/futex_word.h"
+#include "src/__support/threads/linux/futex_utils.h"
namespace LIBC_NAMESPACE_DECL {
-namespace sem_utils {
-// 0x53 = S, 0x45 = E, 0x4D = M, 0x31 = 1
-// canary value: SEM1 in hex
-LIBC_INLINE_VAR constexpr unsigned int SEM_CANARY = 0x53454D31U;
+class Semaphore {
+ Futex value;
+ unsigned int canary;
-static_assert(sizeof(__futex_word) == sizeof(FutexWordType));
-static_assert(alignof(__futex_word) >= alignof(FutexWordType));
+ // 0x53 = S, 0x45 = E, 0x4D = M, 0x31 = 1
+ static constexpr unsigned int SEM_CANARY = 0x53454D31U;
-// TODO:
-// 1. Add named semaphore support: sem_open, sem_close, sem_unlink
-// 2. Add the posting and waiting operations: sem_post, sem_wait,
-// sem_trywait, sem_timedwait, sem_clockwait.
+public:
+ // TODO:
+ // 1. Add named semaphore support: sem_open, sem_close, sem_unlink
+ // 2. Add the posting and waiting operations: sem_post, sem_wait,
+ // sem_trywait, sem_timedwait, sem_clockwait.
-LIBC_INLINE FutexWordType *value_ptr(sem_t *sem) {
- return &sem->__value.__word;
-}
+ LIBC_INLINE void init(unsigned int initial_value) {
+ // init happens before the semaphore is published to any threads
+ // initialize a initialized semaphore is undefined
+ // so RELAXED ordering is enough
+ value.store(initial_value, cpp::MemoryOrder::RELAXED);
+ canary = SEM_CANARY;
+ }
-// get the atomic reference for sem->__value
-LIBC_INLINE cpp::AtomicRef<FutexWordType> value(sem_t *sem) {
- return cpp::AtomicRef<FutexWordType>(*value_ptr(sem));
-}
+ // sanity check for a given semaphore pointer
+ LIBC_INLINE bool is_valid() const { return canary == SEM_CANARY; }
-LIBC_INLINE void initialize(sem_t *sem, unsigned int initial_value) {
- // used in sem_init
- // init happens before the semaphore is published to any threads
- // initialize a initialized semaphore is undefined
- // so RELAXED ordering is enough
- value(sem).store(initial_value, cpp::MemoryOrder::RELAXED);
- sem->__canary = SEM_CANARY;
- for (unsigned char &byte : sem->__reserved)
- byte = 0;
-}
+ LIBC_INLINE void destroy() {
+ // invalidate is safe only when no threads is using
+ // blocked by destroyed semaphore is undefined
+ // use a destroyed semaphore is undefined
+ // RELAXED ordering is enough
+ value.store(0, cpp::MemoryOrder::RELAXED);
+ canary = 0;
+ }
-LIBC_INLINE bool is_valid(const sem_t *sem) {
- // sanity check for a given semaphore pointer
- return sem != nullptr && sem->__canary == SEM_CANARY;
-}
+ LIBC_INLINE int getvalue() const {
+ // get value is informational but not a synchronization op
+ // RELAXED ordering is enough
+ return static_cast<int>(
+ const_cast<Futex &>(value).load(cpp::MemoryOrder::RELAXED));
+ }
+};
-LIBC_INLINE void invalidate(sem_t *sem) {
- // used in sem_destroy
- // invalidate is safe only when no threads is using
- // blocked by destroyed semaphore is undefined
- // use a destroyed semaphore is undefined
- // RELAXED ordering is enough
- value(sem).store(0, cpp::MemoryOrder::RELAXED);
- sem->__canary = 0;
- for (unsigned char &byte : sem->__reserved)
- byte = 0;
-}
+static_assert(sizeof(Semaphore) <= sizeof(sem_t),
+ "Semaphore must fit within sem_t.");
+static_assert(alignof(Semaphore) <= alignof(sem_t),
+ "Semaphore alignment must be compatible with sem_t.");
-} // namespace sem_utils
} // namespace LIBC_NAMESPACE_DECL
#endif // LLVM_LIBC_SRC_SEMAPHORE_POSIX_SEMAPHORE_H
diff --git a/libc/src/semaphore/sem_destroy.cpp b/libc/src/semaphore/sem_destroy.cpp
index c64e18a88e639..67d003697910e 100644
--- a/libc/src/semaphore/sem_destroy.cpp
+++ b/libc/src/semaphore/sem_destroy.cpp
@@ -17,12 +17,13 @@
namespace LIBC_NAMESPACE_DECL {
LLVM_LIBC_FUNCTION(int, sem_destroy, (sem_t * sem)) {
- if (!sem_utils::is_valid(sem)) {
+ auto *s = reinterpret_cast<Semaphore *>(sem);
+ if (sem == nullptr || !s->is_valid()) {
libc_errno = EINVAL;
return -1;
}
- sem_utils::invalidate(sem);
+ s->destroy();
return 0;
}
diff --git a/libc/src/semaphore/sem_getvalue.cpp b/libc/src/semaphore/sem_getvalue.cpp
index ea84c1e8a6241..574a91fe0229b 100644
--- a/libc/src/semaphore/sem_getvalue.cpp
+++ b/libc/src/semaphore/sem_getvalue.cpp
@@ -18,15 +18,13 @@ namespace LIBC_NAMESPACE_DECL {
LLVM_LIBC_FUNCTION(int, sem_getvalue,
(sem_t *__restrict sem, int *__restrict sval)) {
- if (!sem_utils::is_valid(sem) || sval == nullptr) {
+ auto *s = reinterpret_cast<Semaphore *>(sem);
+ if (sem == nullptr || !s->is_valid() || sval == nullptr) {
libc_errno = EINVAL;
return -1;
}
- // get value is informational but not a synchronization op
- // RELAXED ordering is enough
- *sval =
- static_cast<int>(sem_utils::value(sem).load(cpp::MemoryOrder::RELAXED));
+ *sval = s->getvalue();
return 0;
}
diff --git a/libc/src/semaphore/sem_init.cpp b/libc/src/semaphore/sem_init.cpp
index a3f6e452b6e6c..68bcca1041bf7 100644
--- a/libc/src/semaphore/sem_init.cpp
+++ b/libc/src/semaphore/sem_init.cpp
@@ -25,7 +25,7 @@ LLVM_LIBC_FUNCTION(int, sem_init,
}
(void)pshared;
- sem_utils::initialize(sem, value);
+ reinterpret_cast<Semaphore *>(sem)->init(value);
return 0;
}
More information about the libc-commits
mailing list