[libc-commits] [libc] [libc][Semaphore] Add public sem_t type and entry (PR #213489)

Pengxiang Huang via libc-commits libc-commits at lists.llvm.org
Sat Aug 1 13:29:41 PDT 2026


https://github.com/Pengxiang-Huang created https://github.com/llvm/llvm-project/pull/213489

Implements #190847 

Add the public `sem_t` class that operates on the internal `Semaphore` class. Assisted-by: Claude Code Fable 5. 

>From 3832586f1987bb3086dd57de84619412630e0c2f Mon Sep 17 00:00:00 2001
From: Pengxiang Huang <huangpengxiang70 at gmail.com>
Date: Sat, 1 Aug 2026 16:24:22 -0400
Subject: [PATCH] add public sem_t header and proxy that operates on internal
 Semaphore class

---
 libc/config/linux/x86_64/entrypoints.txt      |  13 ++
 libc/config/linux/x86_64/headers.txt          |   1 +
 libc/hdr/CMakeLists.txt                       |   9 +
 libc/hdr/semaphore_macros.h                   |  22 ++
 libc/hdr/types/CMakeLists.txt                 |   8 +
 libc/hdr/types/sem_t.h                        |  22 ++
 libc/include/CMakeLists.txt                   |  13 ++
 libc/include/limits.yaml                      |   2 +
 libc/include/llvm-libc-macros/CMakeLists.txt  |   6 +
 libc/include/llvm-libc-macros/limits-macros.h |   5 +
 .../llvm-libc-macros/semaphore-macros.h       |  15 ++
 libc/include/llvm-libc-types/CMakeLists.txt   |   1 +
 libc/include/llvm-libc-types/sem_t.h          |  24 +++
 libc/include/semaphore.yaml                   |  86 ++++++++
 libc/src/semaphore/CMakeLists.txt             |  77 +++++++
 libc/src/semaphore/linux/CMakeLists.txt       | 154 +++++++++++++-
 libc/src/semaphore/linux/sem_clockwait.cpp    |  38 ++++
 libc/src/semaphore/linux/sem_close.cpp        |  34 +++
 libc/src/semaphore/linux/sem_destroy.cpp      |  31 +++
 libc/src/semaphore/linux/sem_getvalue.cpp     |  32 +++
 libc/src/semaphore/linux/sem_init.cpp         |  33 +++
 libc/src/semaphore/linux/sem_open.cpp         |  45 ++++
 libc/src/semaphore/linux/sem_post.cpp         |  34 +++
 libc/src/semaphore/linux/sem_timedwait.cpp    |  37 ++++
 libc/src/semaphore/linux/sem_trywait.cpp      |  35 ++++
 libc/src/semaphore/linux/sem_unlink.cpp       |  26 +++
 libc/src/semaphore/linux/sem_wait.cpp         |  34 +++
 libc/src/semaphore/linux/semaphore.h          |  15 +-
 libc/src/semaphore/sem_clockwait.h            |  24 +++
 libc/src/semaphore/sem_close.h                |  21 ++
 libc/src/semaphore/sem_destroy.h              |  21 ++
 libc/src/semaphore/sem_getvalue.h             |  21 ++
 libc/src/semaphore/sem_init.h                 |  21 ++
 libc/src/semaphore/sem_open.h                 |  21 ++
 libc/src/semaphore/sem_post.h                 |  21 ++
 libc/src/semaphore/sem_timedwait.h            |  23 +++
 libc/src/semaphore/sem_trywait.h              |  21 ++
 libc/src/semaphore/sem_unlink.h               |  20 ++
 libc/src/semaphore/sem_wait.h                 |  21 ++
 libc/test/src/semaphore/CMakeLists.txt        |  50 +++++
 libc/test/src/semaphore/linux/CMakeLists.txt  |   3 +-
 .../src/semaphore/linux/semaphore_test.cpp    |   5 +-
 libc/test/src/semaphore/sem_open_test.cpp     | 102 +++++++++
 libc/test/src/semaphore/sem_test.cpp          | 195 ++++++++++++++++++
 44 files changed, 1432 insertions(+), 10 deletions(-)
 create mode 100644 libc/hdr/semaphore_macros.h
 create mode 100644 libc/hdr/types/sem_t.h
 create mode 100644 libc/include/llvm-libc-macros/semaphore-macros.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/linux/sem_clockwait.cpp
 create mode 100644 libc/src/semaphore/linux/sem_close.cpp
 create mode 100644 libc/src/semaphore/linux/sem_destroy.cpp
 create mode 100644 libc/src/semaphore/linux/sem_getvalue.cpp
 create mode 100644 libc/src/semaphore/linux/sem_init.cpp
 create mode 100644 libc/src/semaphore/linux/sem_open.cpp
 create mode 100644 libc/src/semaphore/linux/sem_post.cpp
 create mode 100644 libc/src/semaphore/linux/sem_timedwait.cpp
 create mode 100644 libc/src/semaphore/linux/sem_trywait.cpp
 create mode 100644 libc/src/semaphore/linux/sem_unlink.cpp
 create mode 100644 libc/src/semaphore/linux/sem_wait.cpp
 create mode 100644 libc/src/semaphore/sem_clockwait.h
 create mode 100644 libc/src/semaphore/sem_close.h
 create mode 100644 libc/src/semaphore/sem_destroy.h
 create mode 100644 libc/src/semaphore/sem_getvalue.h
 create mode 100644 libc/src/semaphore/sem_init.h
 create mode 100644 libc/src/semaphore/sem_open.h
 create mode 100644 libc/src/semaphore/sem_post.h
 create mode 100644 libc/src/semaphore/sem_timedwait.h
 create mode 100644 libc/src/semaphore/sem_trywait.h
 create mode 100644 libc/src/semaphore/sem_unlink.h
 create mode 100644 libc/src/semaphore/sem_wait.h
 create mode 100644 libc/test/src/semaphore/sem_open_test.cpp
 create mode 100644 libc/test/src/semaphore/sem_test.cpp

diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index bf809e268f585..b39cc972200bf 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -1352,6 +1352,19 @@ if(LLVM_LIBC_FULL_BUILD)
     libc.src.sched.__sched_setcpuzero
     libc.src.sched.__sched_xorcpuset
 
+    # semaphore.h entrypoints
+    libc.src.semaphore.sem_clockwait
+    libc.src.semaphore.sem_close
+    libc.src.semaphore.sem_destroy
+    libc.src.semaphore.sem_getvalue
+    libc.src.semaphore.sem_init
+    libc.src.semaphore.sem_open
+    libc.src.semaphore.sem_post
+    libc.src.semaphore.sem_timedwait
+    libc.src.semaphore.sem_trywait
+    libc.src.semaphore.sem_unlink
+    libc.src.semaphore.sem_wait
+
     # setjmp.h entrypoints
     libc.src.setjmp.longjmp
     libc.src.setjmp.setjmp
diff --git a/libc/config/linux/x86_64/headers.txt b/libc/config/linux/x86_64/headers.txt
index b8751f7810b30..3aea6394c98cb 100644
--- a/libc/config/linux/x86_64/headers.txt
+++ b/libc/config/linux/x86_64/headers.txt
@@ -35,6 +35,7 @@ set(TARGET_PUBLIC_HEADERS
     libc.include.pwd
     libc.include.sched
     libc.include.search
+    libc.include.semaphore
     libc.include.setjmp
     libc.include.signal
     libc.include.spawn
diff --git a/libc/hdr/CMakeLists.txt b/libc/hdr/CMakeLists.txt
index a3eb7769e3c91..2eb684a7ee181 100644
--- a/libc/hdr/CMakeLists.txt
+++ b/libc/hdr/CMakeLists.txt
@@ -112,6 +112,15 @@ add_proxy_header_library(
     libc.include.llvm-libc-macros.sched_macros
 )
 
+add_proxy_header_library(
+  semaphore_macros
+  HDRS
+    semaphore_macros.h
+  FULL_BUILD_DEPENDS
+    libc.include.llvm-libc-macros.semaphore_macros
+    libc.include.semaphore
+)
+
 add_proxy_header_library(
   signal_macros
   HDRS
diff --git a/libc/hdr/semaphore_macros.h b/libc/hdr/semaphore_macros.h
new file mode 100644
index 0000000000000..88647dd971f11
--- /dev/null
+++ b/libc/hdr/semaphore_macros.h
@@ -0,0 +1,22 @@
+//===-- Definition of macros from semaphore.h -----------------------------===//
+//
+// 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_SEMAPHORE_MACROS_H
+#define LLVM_LIBC_HDR_SEMAPHORE_MACROS_H
+
+#ifdef LIBC_FULL_BUILD
+
+#include "include/llvm-libc-macros/semaphore-macros.h"
+
+#else // Overlay mode
+
+#include <semaphore.h>
+
+#endif // LIBC_FULL_BUILD
+
+#endif // LLVM_LIBC_HDR_SEMAPHORE_MACROS_H
diff --git a/libc/hdr/types/CMakeLists.txt b/libc/hdr/types/CMakeLists.txt
index 8e4b2175cb7cd..e7f2aa21de4ed 100644
--- a/libc/hdr/types/CMakeLists.txt
+++ b/libc/hdr/types/CMakeLists.txt
@@ -341,6 +341,14 @@ add_proxy_header_library(
     libc.include.llvm-libc-types.pthread_barrier_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_barrierattr_t
   HDRS
diff --git a/libc/hdr/types/sem_t.h b/libc/hdr/types/sem_t.h
new file mode 100644
index 0000000000000..f7071a0836d32
--- /dev/null
+++ b/libc/hdr/types/sem_t.h
@@ -0,0 +1,22 @@
+//===-- Proxy for sem_t ---------------------------------------------------===//
+//
+// 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 // LIBC_FULL_BUILD
+
+#endif // LLVM_LIBC_HDR_TYPES_SEM_T_H
diff --git a/libc/include/CMakeLists.txt b/libc/include/CMakeLists.txt
index 196490dcfdcc1..1a25df0990750 100644
--- a/libc/include/CMakeLists.txt
+++ b/libc/include/CMakeLists.txt
@@ -579,6 +579,19 @@ add_header_macro(
     .llvm-libc-types.struct_timespec
 )
 
+add_header_macro(
+  semaphore
+  ../libc/include/semaphore.yaml
+  semaphore.h
+  DEPENDS
+    .llvm_libc_common_h
+    .llvm-libc-macros.semaphore_macros
+    .llvm-libc-types.clockid_t
+    .llvm-libc-types.mode_t
+    .llvm-libc-types.sem_t
+    .llvm-libc-types.struct_timespec
+)
+
 add_header_macro(
   spawn
   ../libc/include/spawn.yaml
diff --git a/libc/include/limits.yaml b/libc/include/limits.yaml
index 1c951318fb14c..f14aa9abed9be 100644
--- a/libc/include/limits.yaml
+++ b/libc/include/limits.yaml
@@ -86,6 +86,8 @@ macros:
     macro_header: limits-macros.h
   - macro_name: PTHREAD_DESTRUCTOR_ITERATIONS
     macro_header: limits-macros.h
+  - macro_name: SEM_VALUE_MAX
+    macro_header: limits-macros.h
   - macro_name: SSIZE_MAX
     macro_header: limits-macros.h
 types: []
diff --git a/libc/include/llvm-libc-macros/CMakeLists.txt b/libc/include/llvm-libc-macros/CMakeLists.txt
index 45a1dbc851d2c..59272a603671f 100644
--- a/libc/include/llvm-libc-macros/CMakeLists.txt
+++ b/libc/include/llvm-libc-macros/CMakeLists.txt
@@ -201,6 +201,12 @@ add_macro_header(
     sched-macros.h
 )
 
+add_macro_header(
+  semaphore_macros
+  HDR
+    semaphore-macros.h
+)
+
 add_macro_header(
   signal_macros
   HDR
diff --git a/libc/include/llvm-libc-macros/limits-macros.h b/libc/include/llvm-libc-macros/limits-macros.h
index 024591eb5b40d..7a6b58e2d598c 100644
--- a/libc/include/llvm-libc-macros/limits-macros.h
+++ b/libc/include/llvm-libc-macros/limits-macros.h
@@ -256,6 +256,11 @@
 #define _POSIX_HOST_NAME_MAX 255
 #endif
 
+#ifndef SEM_VALUE_MAX
+/// The maximum value a semaphore may hold.
+#define SEM_VALUE_MAX INT_MAX
+#endif
+
 #ifdef __linux__
 
 #ifndef PATH_MAX
diff --git a/libc/include/llvm-libc-macros/semaphore-macros.h b/libc/include/llvm-libc-macros/semaphore-macros.h
new file mode 100644
index 0000000000000..d3a88209f8cde
--- /dev/null
+++ b/libc/include/llvm-libc-macros/semaphore-macros.h
@@ -0,0 +1,15 @@
+//===-- Definition of macros from semaphore.h -----------------------------===//
+//
+// 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_MACROS_SEMAPHORE_MACROS_H
+#define LLVM_LIBC_MACROS_SEMAPHORE_MACROS_H
+
+// The value returned by sem_open on failure.
+#define SEM_FAILED ((sem_t *)0)
+
+#endif // LLVM_LIBC_MACROS_SEMAPHORE_MACROS_H
diff --git a/libc/include/llvm-libc-types/CMakeLists.txt b/libc/include/llvm-libc-types/CMakeLists.txt
index 3286ebe54d9a2..7111a7b5ed18c 100644
--- a/libc/include/llvm-libc-types/CMakeLists.txt
+++ b/libc/include/llvm-libc-types/CMakeLists.txt
@@ -119,6 +119,7 @@ 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(pthread_id_np_t HDR pthread_id_np_t.h DEPENDS libc.include.llvm-libc-macros.stdint_macros)
 add_header(rlim_t HDR rlim_t.h)
+add_header(sem_t HDR sem_t.h DEPENDS .__futex_word)
 add_header(time_t HDR time_t_64.h DEST_HDR time_t.h)
 add_header(sighandler_t HDR sighandler_t.h)
 add_header(stack_t HDR stack_t.h DEPENDS .size_t)
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..a32a5bf6fc289
--- /dev/null
+++ b/libc/include/llvm-libc-types/sem_t.h
@@ -0,0 +1,24 @@
+//===-- 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 {
+  // The semaphore count. Waiters block on this word.
+  __futex_word __value;
+  // Set to a fixed value by sem_init/sem_open and cleared by sem_destroy, so
+  // that operations on an uninitialized or destroyed semaphore can be detected.
+  unsigned int __canary;
+  // Whether the semaphore is shared between processes.
+  unsigned int __is_shared : 1;
+} 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..4c51174359f1e
--- /dev/null
+++ b/libc/include/semaphore.yaml
@@ -0,0 +1,86 @@
+header: semaphore.h
+standards:
+  - posix
+macros:
+  - macro_name: SEM_FAILED
+    macro_header: semaphore-macros.h
+types:
+  - type_name: clockid_t
+  - type_name: mode_t
+  - type_name: sem_t
+  - type_name: struct_timespec
+functions:
+  - name: sem_clockwait
+    standards:
+      - posix
+    return_type: int
+    arguments:
+      - type: sem_t *__restrict
+      - type: clockid_t
+      - type: const struct timespec *__restrict
+  - name: sem_close
+    standards:
+      - posix
+    return_type: int
+    arguments:
+      - type: sem_t *
+  - name: sem_destroy
+    standards:
+      - posix
+    return_type: int
+    arguments:
+      - type: sem_t *
+  - name: sem_getvalue
+    standards:
+      - posix
+    return_type: int
+    arguments:
+      - type: sem_t *__restrict
+      - type: int *__restrict
+  - name: sem_init
+    standards:
+      - posix
+    return_type: int
+    arguments:
+      - type: sem_t *
+      - type: int
+      - type: unsigned
+  - name: sem_open
+    standards:
+      - posix
+    return_type: sem_t *
+    arguments:
+      - type: const char *
+      - type: int
+      - type: '...'
+  - name: sem_post
+    standards:
+      - posix
+    return_type: int
+    arguments:
+      - type: sem_t *
+  - name: sem_timedwait
+    standards:
+      - posix
+    return_type: int
+    arguments:
+      - type: sem_t *__restrict
+      - type: const struct timespec *__restrict
+  - name: sem_trywait
+    standards:
+      - posix
+    return_type: int
+    arguments:
+      - type: sem_t *
+  - name: sem_unlink
+    standards:
+      - posix
+    return_type: int
+    arguments:
+      - type: const char *
+  - name: sem_wait
+    standards:
+      - posix
+    return_type: int
+    arguments:
+      - type: sem_t *
diff --git a/libc/src/semaphore/CMakeLists.txt b/libc/src/semaphore/CMakeLists.txt
index cbe2c334fb571..9d3f6e4e96513 100644
--- a/libc/src/semaphore/CMakeLists.txt
+++ b/libc/src/semaphore/CMakeLists.txt
@@ -5,3 +5,80 @@ endif()
 if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS})
   add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS})
 endif()
+
+add_entrypoint_object(
+  sem_init
+  ALIAS
+  DEPENDS
+    .${LIBC_TARGET_OS}.sem_init
+)
+
+add_entrypoint_object(
+  sem_destroy
+  ALIAS
+  DEPENDS
+    .${LIBC_TARGET_OS}.sem_destroy
+)
+
+add_entrypoint_object(
+  sem_getvalue
+  ALIAS
+  DEPENDS
+    .${LIBC_TARGET_OS}.sem_getvalue
+)
+
+add_entrypoint_object(
+  sem_open
+  ALIAS
+  DEPENDS
+    .${LIBC_TARGET_OS}.sem_open
+)
+
+add_entrypoint_object(
+  sem_close
+  ALIAS
+  DEPENDS
+    .${LIBC_TARGET_OS}.sem_close
+)
+
+add_entrypoint_object(
+  sem_unlink
+  ALIAS
+  DEPENDS
+    .${LIBC_TARGET_OS}.sem_unlink
+)
+
+add_entrypoint_object(
+  sem_post
+  ALIAS
+  DEPENDS
+    .${LIBC_TARGET_OS}.sem_post
+)
+
+add_entrypoint_object(
+  sem_wait
+  ALIAS
+  DEPENDS
+    .${LIBC_TARGET_OS}.sem_wait
+)
+
+add_entrypoint_object(
+  sem_trywait
+  ALIAS
+  DEPENDS
+    .${LIBC_TARGET_OS}.sem_trywait
+)
+
+add_entrypoint_object(
+  sem_timedwait
+  ALIAS
+  DEPENDS
+    .${LIBC_TARGET_OS}.sem_timedwait
+)
+
+add_entrypoint_object(
+  sem_clockwait
+  ALIAS
+  DEPENDS
+    .${LIBC_TARGET_OS}.sem_clockwait
+)
diff --git a/libc/src/semaphore/linux/CMakeLists.txt b/libc/src/semaphore/linux/CMakeLists.txt
index bd57b9ee5fc5d..cbc49d07fa0e9 100644
--- a/libc/src/semaphore/linux/CMakeLists.txt
+++ b/libc/src/semaphore/linux/CMakeLists.txt
@@ -4,12 +4,13 @@ add_header_library(
     semaphore.h
   DEPENDS
     libc.hdr.errno_macros
+    libc.hdr.limits_macros
     libc.hdr.time_macros
     libc.hdr.types.clockid_t
     libc.hdr.types.mode_t
     libc.hdr.types.struct_timespec
+    libc.include.llvm-libc-types.sem_t
     libc.src.__support.CPP.atomic
-    libc.src.__support.CPP.limits
     libc.src.__support.error_or
     libc.src.__support.libc_assert
     libc.src.__support.threads.futex_utils
@@ -42,3 +43,154 @@ add_object_library(
     libc.src.string.memory_utils.inline_memcpy
     libc.src.sys.mman.linux.shm_common
 )
+
+add_entrypoint_object(
+  sem_init
+  SRCS
+    sem_init.cpp
+  HDRS
+    ../sem_init.h
+  DEPENDS
+    .semaphore
+    libc.hdr.errno_macros
+    libc.hdr.limits_macros
+    libc.hdr.types.sem_t
+    libc.src.__support.CPP.new
+    libc.src.errno.errno
+)
+
+add_entrypoint_object(
+  sem_destroy
+  SRCS
+    sem_destroy.cpp
+  HDRS
+    ../sem_destroy.h
+  DEPENDS
+    .semaphore
+    libc.hdr.errno_macros
+    libc.hdr.types.sem_t
+    libc.src.errno.errno
+)
+
+add_entrypoint_object(
+  sem_getvalue
+  SRCS
+    sem_getvalue.cpp
+  HDRS
+    ../sem_getvalue.h
+  DEPENDS
+    .semaphore
+    libc.hdr.errno_macros
+    libc.hdr.types.sem_t
+    libc.src.errno.errno
+)
+
+add_entrypoint_object(
+  sem_open
+  SRCS
+    sem_open.cpp
+  HDRS
+    ../sem_open.h
+  DEPENDS
+    .named_semaphore
+    .semaphore
+    libc.hdr.fcntl_macros
+    libc.hdr.semaphore_macros
+    libc.hdr.types.mode_t
+    libc.hdr.types.sem_t
+    libc.src.errno.errno
+)
+
+add_entrypoint_object(
+  sem_close
+  SRCS
+    sem_close.cpp
+  HDRS
+    ../sem_close.h
+  DEPENDS
+    .named_semaphore
+    .semaphore
+    libc.hdr.errno_macros
+    libc.hdr.types.sem_t
+    libc.src.errno.errno
+)
+
+add_entrypoint_object(
+  sem_unlink
+  SRCS
+    sem_unlink.cpp
+  HDRS
+    ../sem_unlink.h
+  DEPENDS
+    .named_semaphore
+    .semaphore
+    libc.src.errno.errno
+)
+
+add_entrypoint_object(
+  sem_post
+  SRCS
+    sem_post.cpp
+  HDRS
+    ../sem_post.h
+  DEPENDS
+    .semaphore
+    libc.hdr.errno_macros
+    libc.hdr.types.sem_t
+    libc.src.errno.errno
+)
+
+add_entrypoint_object(
+  sem_wait
+  SRCS
+    sem_wait.cpp
+  HDRS
+    ../sem_wait.h
+  DEPENDS
+    .semaphore
+    libc.hdr.errno_macros
+    libc.hdr.types.sem_t
+    libc.src.errno.errno
+)
+
+add_entrypoint_object(
+  sem_trywait
+  SRCS
+    sem_trywait.cpp
+  HDRS
+    ../sem_trywait.h
+  DEPENDS
+    .semaphore
+    libc.hdr.errno_macros
+    libc.hdr.types.sem_t
+    libc.src.errno.errno
+)
+
+add_entrypoint_object(
+  sem_timedwait
+  SRCS
+    sem_timedwait.cpp
+  HDRS
+    ../sem_timedwait.h
+  DEPENDS
+    .semaphore
+    libc.hdr.errno_macros
+    libc.hdr.types.sem_t
+    libc.hdr.types.struct_timespec
+    libc.src.errno.errno
+)
+
+add_entrypoint_object(
+  sem_clockwait
+  SRCS
+    sem_clockwait.cpp
+  HDRS
+    ../sem_clockwait.h
+  DEPENDS
+    .semaphore
+    libc.hdr.errno_macros
+    libc.hdr.types.clockid_t
+    libc.hdr.types.sem_t
+    libc.hdr.types.struct_timespec
+    libc.src.errno.errno
+)
diff --git a/libc/src/semaphore/linux/sem_clockwait.cpp b/libc/src/semaphore/linux/sem_clockwait.cpp
new file mode 100644
index 0000000000000..75def64792668
--- /dev/null
+++ b/libc/src/semaphore/linux/sem_clockwait.cpp
@@ -0,0 +1,38 @@
+//===-- Linux implementation of sem_clockwait -----------------------------===//
+//
+// 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_clockwait.h"
+
+#include "hdr/errno_macros.h"
+#include "hdr/types/clockid_t.h"
+#include "hdr/types/sem_t.h"
+#include "hdr/types/struct_timespec.h"
+#include "src/__support/common.h"
+#include "src/__support/libc_errno.h"
+#include "src/__support/macros/config.h"
+#include "src/semaphore/linux/semaphore.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(int, sem_clockwait,
+                   (sem_t *__restrict sem, clockid_t clock_id,
+                    const struct timespec *__restrict abstime)) {
+  Semaphore *semaphore = reinterpret_cast<Semaphore *>(sem);
+  if (!semaphore->is_valid()) {
+    libc_errno = EINVAL;
+    return -1;
+  }
+
+  if (int err = semaphore->clockwait(clock_id, abstime)) {
+    libc_errno = err;
+    return -1;
+  }
+  return 0;
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/semaphore/linux/sem_close.cpp b/libc/src/semaphore/linux/sem_close.cpp
new file mode 100644
index 0000000000000..0dc5a3f074cd6
--- /dev/null
+++ b/libc/src/semaphore/linux/sem_close.cpp
@@ -0,0 +1,34 @@
+//===-- Linux implementation of sem_close ---------------------------------===//
+//
+// 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_close.h"
+
+#include "hdr/errno_macros.h"
+#include "hdr/types/sem_t.h"
+#include "src/__support/common.h"
+#include "src/__support/libc_errno.h"
+#include "src/__support/macros/config.h"
+#include "src/semaphore/linux/semaphore.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(int, sem_close, (sem_t * sem)) {
+  Semaphore *semaphore = reinterpret_cast<Semaphore *>(sem);
+  if (!semaphore->is_valid()) {
+    libc_errno = EINVAL;
+    return -1;
+  }
+
+  if (int err = Semaphore::close(semaphore)) {
+    libc_errno = err;
+    return -1;
+  }
+  return 0;
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/semaphore/linux/sem_destroy.cpp b/libc/src/semaphore/linux/sem_destroy.cpp
new file mode 100644
index 0000000000000..3b299020ce336
--- /dev/null
+++ b/libc/src/semaphore/linux/sem_destroy.cpp
@@ -0,0 +1,31 @@
+//===-- Linux 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 "hdr/errno_macros.h"
+#include "hdr/types/sem_t.h"
+#include "src/__support/common.h"
+#include "src/__support/libc_errno.h"
+#include "src/__support/macros/config.h"
+#include "src/semaphore/linux/semaphore.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(int, sem_destroy, (sem_t * sem)) {
+  Semaphore *semaphore = reinterpret_cast<Semaphore *>(sem);
+  if (!semaphore->is_valid()) {
+    libc_errno = EINVAL;
+    return -1;
+  }
+
+  semaphore->destroy();
+  return 0;
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/semaphore/linux/sem_getvalue.cpp b/libc/src/semaphore/linux/sem_getvalue.cpp
new file mode 100644
index 0000000000000..0ac5aa7d7696d
--- /dev/null
+++ b/libc/src/semaphore/linux/sem_getvalue.cpp
@@ -0,0 +1,32 @@
+//===-- Linux 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 "hdr/errno_macros.h"
+#include "hdr/types/sem_t.h"
+#include "src/__support/common.h"
+#include "src/__support/libc_errno.h"
+#include "src/__support/macros/config.h"
+#include "src/semaphore/linux/semaphore.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(int, sem_getvalue,
+                   (sem_t *__restrict sem, int *__restrict sval)) {
+  Semaphore *semaphore = reinterpret_cast<Semaphore *>(sem);
+  if (!semaphore->is_valid()) {
+    libc_errno = EINVAL;
+    return -1;
+  }
+
+  *sval = semaphore->getvalue();
+  return 0;
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/semaphore/linux/sem_init.cpp b/libc/src/semaphore/linux/sem_init.cpp
new file mode 100644
index 0000000000000..602567299e577
--- /dev/null
+++ b/libc/src/semaphore/linux/sem_init.cpp
@@ -0,0 +1,33 @@
+//===-- Linux 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 "hdr/errno_macros.h"
+#include "hdr/limits_macros.h"
+#include "hdr/types/sem_t.h"
+#include "src/__support/CPP/new.h"
+#include "src/__support/common.h"
+#include "src/__support/libc_errno.h"
+#include "src/__support/macros/config.h"
+#include "src/semaphore/linux/semaphore.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(int, sem_init,
+                   (sem_t * sem, int pshared, unsigned int value)) {
+  if (value > SEM_VALUE_MAX) {
+    libc_errno = EINVAL;
+    return -1;
+  }
+
+  new (sem) Semaphore(value, /*shared=*/pshared != 0);
+  return 0;
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/semaphore/linux/sem_open.cpp b/libc/src/semaphore/linux/sem_open.cpp
new file mode 100644
index 0000000000000..2ee7f3786278b
--- /dev/null
+++ b/libc/src/semaphore/linux/sem_open.cpp
@@ -0,0 +1,45 @@
+//===-- Linux implementation of sem_open ----------------------------------===//
+//
+// 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_open.h"
+
+#include "hdr/fcntl_macros.h"
+#include "hdr/semaphore_macros.h"
+#include "hdr/types/mode_t.h"
+#include "hdr/types/sem_t.h"
+#include "src/__support/common.h"
+#include "src/__support/libc_errno.h"
+#include "src/__support/macros/config.h"
+#include "src/semaphore/linux/semaphore.h"
+
+#include <stdarg.h>
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(sem_t *, sem_open, (const char *name, int oflag, ...)) {
+  mode_t mode = 0;
+  unsigned int value = 0;
+
+  if (oflag & O_CREAT) {
+    va_list varargs;
+    va_start(varargs, oflag);
+    mode = va_arg(varargs, mode_t);
+    value = va_arg(varargs, unsigned int);
+    va_end(varargs);
+  }
+
+  auto sem_or = Semaphore::open(name, oflag, mode, value);
+  if (!sem_or.has_value()) {
+    libc_errno = sem_or.error();
+    return SEM_FAILED;
+  }
+
+  return reinterpret_cast<sem_t *>(sem_or.value());
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/semaphore/linux/sem_post.cpp b/libc/src/semaphore/linux/sem_post.cpp
new file mode 100644
index 0000000000000..90d8b6627b5ee
--- /dev/null
+++ b/libc/src/semaphore/linux/sem_post.cpp
@@ -0,0 +1,34 @@
+//===-- Linux implementation of sem_post ----------------------------------===//
+//
+// 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_post.h"
+
+#include "hdr/errno_macros.h"
+#include "hdr/types/sem_t.h"
+#include "src/__support/common.h"
+#include "src/__support/libc_errno.h"
+#include "src/__support/macros/config.h"
+#include "src/semaphore/linux/semaphore.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(int, sem_post, (sem_t * sem)) {
+  Semaphore *semaphore = reinterpret_cast<Semaphore *>(sem);
+  if (!semaphore->is_valid()) {
+    libc_errno = EINVAL;
+    return -1;
+  }
+
+  if (int err = semaphore->post()) {
+    libc_errno = err;
+    return -1;
+  }
+  return 0;
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/semaphore/linux/sem_timedwait.cpp b/libc/src/semaphore/linux/sem_timedwait.cpp
new file mode 100644
index 0000000000000..3ce182fa1f63b
--- /dev/null
+++ b/libc/src/semaphore/linux/sem_timedwait.cpp
@@ -0,0 +1,37 @@
+//===-- Linux implementation of sem_timedwait -----------------------------===//
+//
+// 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_timedwait.h"
+
+#include "hdr/errno_macros.h"
+#include "hdr/types/sem_t.h"
+#include "hdr/types/struct_timespec.h"
+#include "src/__support/common.h"
+#include "src/__support/libc_errno.h"
+#include "src/__support/macros/config.h"
+#include "src/semaphore/linux/semaphore.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(int, sem_timedwait,
+                   (sem_t *__restrict sem,
+                    const struct timespec *__restrict abstime)) {
+  Semaphore *semaphore = reinterpret_cast<Semaphore *>(sem);
+  if (!semaphore->is_valid()) {
+    libc_errno = EINVAL;
+    return -1;
+  }
+
+  if (int err = semaphore->timedwait(abstime)) {
+    libc_errno = err;
+    return -1;
+  }
+  return 0;
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/semaphore/linux/sem_trywait.cpp b/libc/src/semaphore/linux/sem_trywait.cpp
new file mode 100644
index 0000000000000..9d40f0b3f5d33
--- /dev/null
+++ b/libc/src/semaphore/linux/sem_trywait.cpp
@@ -0,0 +1,35 @@
+//===-- Linux implementation of sem_trywait -------------------------------===//
+//
+// 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_trywait.h"
+
+#include "hdr/errno_macros.h"
+#include "hdr/types/sem_t.h"
+#include "src/__support/common.h"
+#include "src/__support/libc_errno.h"
+#include "src/__support/macros/config.h"
+#include "src/semaphore/linux/semaphore.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(int, sem_trywait, (sem_t * sem)) {
+  Semaphore *semaphore = reinterpret_cast<Semaphore *>(sem);
+  if (!semaphore->is_valid()) {
+    libc_errno = EINVAL;
+    return -1;
+  }
+
+  // An already locked semaphore reports EAGAIN rather than blocking.
+  if (int err = semaphore->trywait()) {
+    libc_errno = err;
+    return -1;
+  }
+  return 0;
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/semaphore/linux/sem_unlink.cpp b/libc/src/semaphore/linux/sem_unlink.cpp
new file mode 100644
index 0000000000000..1c1b2adb34af4
--- /dev/null
+++ b/libc/src/semaphore/linux/sem_unlink.cpp
@@ -0,0 +1,26 @@
+//===-- Linux implementation of sem_unlink --------------------------------===//
+//
+// 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_unlink.h"
+
+#include "src/__support/common.h"
+#include "src/__support/libc_errno.h"
+#include "src/__support/macros/config.h"
+#include "src/semaphore/linux/semaphore.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(int, sem_unlink, (const char *name)) {
+  if (int err = Semaphore::unlink(name)) {
+    libc_errno = err;
+    return -1;
+  }
+  return 0;
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/semaphore/linux/sem_wait.cpp b/libc/src/semaphore/linux/sem_wait.cpp
new file mode 100644
index 0000000000000..481e0406c5c83
--- /dev/null
+++ b/libc/src/semaphore/linux/sem_wait.cpp
@@ -0,0 +1,34 @@
+//===-- Linux implementation of sem_wait ----------------------------------===//
+//
+// 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_wait.h"
+
+#include "hdr/errno_macros.h"
+#include "hdr/types/sem_t.h"
+#include "src/__support/common.h"
+#include "src/__support/libc_errno.h"
+#include "src/__support/macros/config.h"
+#include "src/semaphore/linux/semaphore.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(int, sem_wait, (sem_t * sem)) {
+  Semaphore *semaphore = reinterpret_cast<Semaphore *>(sem);
+  if (!semaphore->is_valid()) {
+    libc_errno = EINVAL;
+    return -1;
+  }
+
+  if (int err = semaphore->wait()) {
+    libc_errno = err;
+    return -1;
+  }
+  return 0;
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/semaphore/linux/semaphore.h b/libc/src/semaphore/linux/semaphore.h
index 8069336b5d53f..2bde8d6bfe084 100644
--- a/libc/src/semaphore/linux/semaphore.h
+++ b/libc/src/semaphore/linux/semaphore.h
@@ -10,12 +10,13 @@
 #define LLVM_LIBC_SRC_SEMAPHORE_LINUX_SEMAPHORE_H
 
 #include "hdr/errno_macros.h"
+#include "hdr/limits_macros.h"
 #include "hdr/time_macros.h"
 #include "hdr/types/clockid_t.h"
 #include "hdr/types/mode_t.h"
 #include "hdr/types/struct_timespec.h"
+#include "include/llvm-libc-types/sem_t.h"
 #include "src/__support/CPP/atomic.h"
-#include "src/__support/CPP/limits.h"
 #include "src/__support/common.h"
 #include "src/__support/error_or.h"
 #include "src/__support/libc_assert.h"
@@ -24,10 +25,6 @@
 
 namespace LIBC_NAMESPACE_DECL {
 
-// Define SEM_VALUE_MAX as INT_MAX
-constexpr unsigned int SEM_VALUE_MAX =
-    static_cast<unsigned int>(cpp::numeric_limits<int>::max());
-
 class Semaphore {
   Futex value;
   unsigned int canary;
@@ -186,6 +183,14 @@ class Semaphore {
   static int unlink(const char *name);
 };
 
+// The public sem_t mirrors the layout of the internal Semaphore class.
+// At entrypoints sem_t object operate through a reinterpret_cast,
+// so they must be the same layout.
+static_assert(sizeof(Semaphore) == sizeof(sem_t) &&
+                  alignof(Semaphore) == alignof(sem_t),
+              "The public sem_t type must be of the same size and alignment as "
+              "the internal semaphore type.");
+
 } // namespace LIBC_NAMESPACE_DECL
 
 #endif // LLVM_LIBC_SRC_SEMAPHORE_LINUX_SEMAPHORE_H
diff --git a/libc/src/semaphore/sem_clockwait.h b/libc/src/semaphore/sem_clockwait.h
new file mode 100644
index 0000000000000..552b2fee50db4
--- /dev/null
+++ b/libc/src/semaphore/sem_clockwait.h
@@ -0,0 +1,24 @@
+//===-- Implementation header for sem_clockwait function --------*- 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_CLOCKWAIT_H
+#define LLVM_LIBC_SRC_SEMAPHORE_SEM_CLOCKWAIT_H
+
+#include "hdr/types/clockid_t.h"
+#include "hdr/types/sem_t.h"
+#include "hdr/types/struct_timespec.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+int sem_clockwait(sem_t *__restrict sem, clockid_t clock_id,
+                  const struct timespec *__restrict abstime);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_SEMAPHORE_SEM_CLOCKWAIT_H
diff --git a/libc/src/semaphore/sem_close.h b/libc/src/semaphore/sem_close.h
new file mode 100644
index 0000000000000..31048e15d49a0
--- /dev/null
+++ b/libc/src/semaphore/sem_close.h
@@ -0,0 +1,21 @@
+//===-- Implementation header for sem_close function ------------*- 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_CLOSE_H
+#define LLVM_LIBC_SRC_SEMAPHORE_SEM_CLOSE_H
+
+#include "hdr/types/sem_t.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+int sem_close(sem_t *sem);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_SEMAPHORE_SEM_CLOSE_H
diff --git a/libc/src/semaphore/sem_destroy.h b/libc/src/semaphore/sem_destroy.h
new file mode 100644
index 0000000000000..3e088981108cf
--- /dev/null
+++ b/libc/src/semaphore/sem_destroy.h
@@ -0,0 +1,21 @@
+//===-- Implementation header for sem_destroy function ----------*- 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.h b/libc/src/semaphore/sem_getvalue.h
new file mode 100644
index 0000000000000..3f21966826969
--- /dev/null
+++ b/libc/src/semaphore/sem_getvalue.h
@@ -0,0 +1,21 @@
+//===-- Implementation header for sem_getvalue function ---------*- 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.h b/libc/src/semaphore/sem_init.h
new file mode 100644
index 0000000000000..609df125d0722
--- /dev/null
+++ b/libc/src/semaphore/sem_init.h
@@ -0,0 +1,21 @@
+//===-- Implementation header for sem_init function -------------*- 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/src/semaphore/sem_open.h b/libc/src/semaphore/sem_open.h
new file mode 100644
index 0000000000000..853bdb9bd8a0a
--- /dev/null
+++ b/libc/src/semaphore/sem_open.h
@@ -0,0 +1,21 @@
+//===-- Implementation header for sem_open function -------------*- 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_OPEN_H
+#define LLVM_LIBC_SRC_SEMAPHORE_SEM_OPEN_H
+
+#include "hdr/types/sem_t.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+sem_t *sem_open(const char *name, int oflag, ...);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_SEMAPHORE_SEM_OPEN_H
diff --git a/libc/src/semaphore/sem_post.h b/libc/src/semaphore/sem_post.h
new file mode 100644
index 0000000000000..a0b572dbcf965
--- /dev/null
+++ b/libc/src/semaphore/sem_post.h
@@ -0,0 +1,21 @@
+//===-- Implementation header for sem_post function -------------*- 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_POST_H
+#define LLVM_LIBC_SRC_SEMAPHORE_SEM_POST_H
+
+#include "hdr/types/sem_t.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+int sem_post(sem_t *sem);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_SEMAPHORE_SEM_POST_H
diff --git a/libc/src/semaphore/sem_timedwait.h b/libc/src/semaphore/sem_timedwait.h
new file mode 100644
index 0000000000000..a62c79331901b
--- /dev/null
+++ b/libc/src/semaphore/sem_timedwait.h
@@ -0,0 +1,23 @@
+//===-- Implementation header for sem_timedwait function --------*- 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_TIMEDWAIT_H
+#define LLVM_LIBC_SRC_SEMAPHORE_SEM_TIMEDWAIT_H
+
+#include "hdr/types/sem_t.h"
+#include "hdr/types/struct_timespec.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+int sem_timedwait(sem_t *__restrict sem,
+                  const struct timespec *__restrict abstime);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_SEMAPHORE_SEM_TIMEDWAIT_H
diff --git a/libc/src/semaphore/sem_trywait.h b/libc/src/semaphore/sem_trywait.h
new file mode 100644
index 0000000000000..e6c9a11900abf
--- /dev/null
+++ b/libc/src/semaphore/sem_trywait.h
@@ -0,0 +1,21 @@
+//===-- Implementation header for sem_trywait function ----------*- 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_TRYWAIT_H
+#define LLVM_LIBC_SRC_SEMAPHORE_SEM_TRYWAIT_H
+
+#include "hdr/types/sem_t.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+int sem_trywait(sem_t *sem);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_SEMAPHORE_SEM_TRYWAIT_H
diff --git a/libc/src/semaphore/sem_unlink.h b/libc/src/semaphore/sem_unlink.h
new file mode 100644
index 0000000000000..664e321e58877
--- /dev/null
+++ b/libc/src/semaphore/sem_unlink.h
@@ -0,0 +1,20 @@
+//===-- Implementation header for sem_unlink function -----------*- 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_UNLINK_H
+#define LLVM_LIBC_SRC_SEMAPHORE_SEM_UNLINK_H
+
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+int sem_unlink(const char *name);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_SEMAPHORE_SEM_UNLINK_H
diff --git a/libc/src/semaphore/sem_wait.h b/libc/src/semaphore/sem_wait.h
new file mode 100644
index 0000000000000..de2de47d3a6f2
--- /dev/null
+++ b/libc/src/semaphore/sem_wait.h
@@ -0,0 +1,21 @@
+//===-- Implementation header for sem_wait function -------------*- 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_WAIT_H
+#define LLVM_LIBC_SRC_SEMAPHORE_SEM_WAIT_H
+
+#include "hdr/types/sem_t.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+int sem_wait(sem_t *sem);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_SEMAPHORE_SEM_WAIT_H
diff --git a/libc/test/src/semaphore/CMakeLists.txt b/libc/test/src/semaphore/CMakeLists.txt
index 07095686cac2c..25da2332a263a 100644
--- a/libc/test/src/semaphore/CMakeLists.txt
+++ b/libc/test/src/semaphore/CMakeLists.txt
@@ -1,3 +1,53 @@
+add_custom_target(libc_semaphore_unittests)
+
 if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS})
   add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS})
 endif()
+
+add_libc_test(
+  sem_test
+  SUITE
+    libc_semaphore_unittests
+  SRCS
+    sem_test.cpp
+  DEPENDS
+    libc.hdr.errno_macros
+    libc.hdr.limits_macros
+    libc.hdr.time_macros
+    libc.hdr.types.sem_t
+    libc.hdr.types.struct_timespec
+    libc.src.__support.time.clock_gettime
+    libc.src.errno.errno
+    libc.src.semaphore.sem_clockwait
+    libc.src.semaphore.sem_destroy
+    libc.src.semaphore.sem_getvalue
+    libc.src.semaphore.sem_init
+    libc.src.semaphore.sem_post
+    libc.src.semaphore.sem_timedwait
+    libc.src.semaphore.sem_trywait
+    libc.src.semaphore.sem_wait
+    libc.test.UnitTest.ErrnoCheckingTest
+    libc.test.UnitTest.ErrnoSetterMatcher
+)
+
+add_libc_test(
+  sem_open_test
+  SUITE
+    libc_semaphore_unittests
+  SRCS
+    sem_open_test.cpp
+  DEPENDS
+    libc.hdr.errno_macros
+    libc.hdr.fcntl_macros
+    libc.hdr.semaphore_macros
+    libc.hdr.types.sem_t
+    libc.src.errno.errno
+    libc.src.semaphore.sem_close
+    libc.src.semaphore.sem_getvalue
+    libc.src.semaphore.sem_open
+    libc.src.semaphore.sem_post
+    libc.src.semaphore.sem_unlink
+    libc.src.semaphore.sem_wait
+    libc.test.UnitTest.ErrnoCheckingTest
+    libc.test.UnitTest.ErrnoSetterMatcher
+)
diff --git a/libc/test/src/semaphore/linux/CMakeLists.txt b/libc/test/src/semaphore/linux/CMakeLists.txt
index c28b823d3c743..53fce2315c056 100644
--- a/libc/test/src/semaphore/linux/CMakeLists.txt
+++ b/libc/test/src/semaphore/linux/CMakeLists.txt
@@ -1,5 +1,3 @@
-add_custom_target(libc_semaphore_unittests)
-
 add_libc_test(
   semaphore_test
   SUITE
@@ -9,6 +7,7 @@ add_libc_test(
   DEPENDS
     libc.hdr.errno_macros
     libc.hdr.fcntl_macros
+    libc.hdr.limits_macros
     libc.hdr.time_macros
     libc.hdr.types.struct_timespec
     libc.src.__support.time.clock_gettime
diff --git a/libc/test/src/semaphore/linux/semaphore_test.cpp b/libc/test/src/semaphore/linux/semaphore_test.cpp
index 2ce28ce3c0a54..87f166e2aff8b 100644
--- a/libc/test/src/semaphore/linux/semaphore_test.cpp
+++ b/libc/test/src/semaphore/linux/semaphore_test.cpp
@@ -8,6 +8,7 @@
 
 #include "hdr/errno_macros.h"
 #include "hdr/fcntl_macros.h"
+#include "hdr/limits_macros.h"
 #include "hdr/time_macros.h"
 #include "hdr/types/struct_timespec.h"
 #include "src/__support/time/clock_gettime.h"
@@ -90,9 +91,9 @@ TEST(LlvmLibcSemaphoreTest, TimedWaitNullTime) {
 
 TEST(LlvmLibcSemaphoreTest, PostOverflow) {
   // The value exceeds maximum, post() must fail with EOVERFLOW
-  Semaphore sem(LIBC_NAMESPACE::SEM_VALUE_MAX, /*is_shared=*/false);
+  Semaphore sem(SEM_VALUE_MAX, /*is_shared=*/false);
   ASSERT_EQ(sem.post(), EOVERFLOW);
-  ASSERT_EQ(sem.getvalue(), static_cast<int>(LIBC_NAMESPACE::SEM_VALUE_MAX));
+  ASSERT_EQ(sem.getvalue(), SEM_VALUE_MAX);
 }
 
 TEST(LlvmLibcSemaphoreTest, TimedWaitTimeout) {
diff --git a/libc/test/src/semaphore/sem_open_test.cpp b/libc/test/src/semaphore/sem_open_test.cpp
new file mode 100644
index 0000000000000..31d4d5a359358
--- /dev/null
+++ b/libc/test/src/semaphore/sem_open_test.cpp
@@ -0,0 +1,102 @@
+//===-- Unittests for named 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 "hdr/fcntl_macros.h"
+#include "hdr/semaphore_macros.h"
+#include "hdr/types/sem_t.h"
+#include "src/semaphore/sem_close.h"
+#include "src/semaphore/sem_getvalue.h"
+#include "src/semaphore/sem_open.h"
+#include "src/semaphore/sem_post.h"
+#include "src/semaphore/sem_unlink.h"
+#include "src/semaphore/sem_wait.h"
+#include "test/UnitTest/ErrnoCheckingTest.h"
+#include "test/UnitTest/ErrnoSetterMatcher.h"
+#include "test/UnitTest/Test.h"
+
+using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Fails;
+using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Succeeds;
+using LlvmLibcSemOpenTest = LIBC_NAMESPACE::testing::ErrnoCheckingTest;
+
+TEST_F(LlvmLibcSemOpenTest, OpenCloseUnlink) {
+  const char *name = APPEND_LIBC_TEST("/llvmlibc_sem_open");
+
+  // Clear any leftover from a previous run
+  LIBC_NAMESPACE::sem_unlink(name);
+  LIBC_NAMESPACE::libc_errno = 0;
+
+  sem_t *sem = LIBC_NAMESPACE::sem_open(name, O_CREAT | O_EXCL, 0644, 7);
+  ASSERT_NE(sem, SEM_FAILED);
+  ASSERT_ERRNO_SUCCESS();
+
+  int value = -1;
+  ASSERT_THAT(LIBC_NAMESPACE::sem_getvalue(sem, &value), Succeeds());
+  EXPECT_EQ(value, 7);
+
+  // A named semaphore supports the same operations as an unnamed one.
+  ASSERT_THAT(LIBC_NAMESPACE::sem_wait(sem), Succeeds());
+  ASSERT_THAT(LIBC_NAMESPACE::sem_post(sem), Succeeds());
+
+  ASSERT_THAT(LIBC_NAMESPACE::sem_close(sem), Succeeds());
+  ASSERT_THAT(LIBC_NAMESPACE::sem_unlink(name), Succeeds());
+}
+
+TEST_F(LlvmLibcSemOpenTest, OpenExisting) {
+  const char *name = APPEND_LIBC_TEST("/llvmlibc_sem_open_existing");
+
+  LIBC_NAMESPACE::sem_unlink(name);
+  LIBC_NAMESPACE::libc_errno = 0;
+
+  sem_t *first = LIBC_NAMESPACE::sem_open(name, O_CREAT | O_EXCL, 0644, 10);
+  ASSERT_NE(first, SEM_FAILED);
+
+  // The name already exists, so the value argument is ignored.
+  sem_t *second = LIBC_NAMESPACE::sem_open(name, O_CREAT, 0644, 99);
+  ASSERT_NE(second, SEM_FAILED);
+
+  int value = -1;
+  ASSERT_THAT(LIBC_NAMESPACE::sem_getvalue(second, &value), Succeeds());
+  EXPECT_EQ(value, 10);
+
+  ASSERT_THAT(LIBC_NAMESPACE::sem_close(second), Succeeds());
+  ASSERT_THAT(LIBC_NAMESPACE::sem_close(first), Succeeds());
+  ASSERT_THAT(LIBC_NAMESPACE::sem_unlink(name), Succeeds());
+}
+
+TEST_F(LlvmLibcSemOpenTest, OpenExclusiveTwiceFails) {
+  const char *name = APPEND_LIBC_TEST("/llvmlibc_sem_open_excl");
+
+  LIBC_NAMESPACE::sem_unlink(name);
+  LIBC_NAMESPACE::libc_errno = 0;
+
+  sem_t *sem = LIBC_NAMESPACE::sem_open(name, O_CREAT | O_EXCL, 0644, 1);
+  ASSERT_NE(sem, SEM_FAILED);
+
+  EXPECT_EQ(LIBC_NAMESPACE::sem_open(name, O_CREAT | O_EXCL, 0644, 1),
+            SEM_FAILED);
+  ASSERT_ERRNO_EQ(EEXIST);
+  LIBC_NAMESPACE::libc_errno = 0;
+
+  ASSERT_THAT(LIBC_NAMESPACE::sem_close(sem), Succeeds());
+  ASSERT_THAT(LIBC_NAMESPACE::sem_unlink(name), Succeeds());
+}
+
+TEST_F(LlvmLibcSemOpenTest, OpenNonExistent) {
+  const char *name = APPEND_LIBC_TEST("/llvmlibc_sem_missing");
+
+  // Without O_CREAT the name must already exist.
+  EXPECT_EQ(LIBC_NAMESPACE::sem_open(name, 0), SEM_FAILED);
+  ASSERT_ERRNO_EQ(ENOENT);
+}
+
+TEST_F(LlvmLibcSemOpenTest, UnlinkNonExistent) {
+  const char *name = APPEND_LIBC_TEST("/llvmlibc_sem_missing_unlink");
+
+  EXPECT_THAT(LIBC_NAMESPACE::sem_unlink(name), Fails(ENOENT));
+}
diff --git a/libc/test/src/semaphore/sem_test.cpp b/libc/test/src/semaphore/sem_test.cpp
new file mode 100644
index 0000000000000..3749876ff1ea9
--- /dev/null
+++ b/libc/test/src/semaphore/sem_test.cpp
@@ -0,0 +1,195 @@
+//===-- Unittests for unnamed 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 "hdr/limits_macros.h"
+#include "hdr/time_macros.h"
+#include "hdr/types/sem_t.h"
+#include "hdr/types/struct_timespec.h"
+#include "src/__support/time/clock_gettime.h"
+#include "src/semaphore/sem_clockwait.h"
+#include "src/semaphore/sem_destroy.h"
+#include "src/semaphore/sem_getvalue.h"
+#include "src/semaphore/sem_init.h"
+#include "src/semaphore/sem_post.h"
+#include "src/semaphore/sem_timedwait.h"
+#include "src/semaphore/sem_trywait.h"
+#include "src/semaphore/sem_wait.h"
+#include "test/UnitTest/ErrnoCheckingTest.h"
+#include "test/UnitTest/ErrnoSetterMatcher.h"
+#include "test/UnitTest/Test.h"
+
+using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Fails;
+using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Succeeds;
+using LlvmLibcSemTest = LIBC_NAMESPACE::testing::ErrnoCheckingTest;
+
+// Returns an absolute deadline a few milliseconds past now on the given clock.
+static timespec deadline_in_10ms(clockid_t clock_id) {
+  timespec ts{};
+  LIBC_NAMESPACE::internal::clock_gettime(clock_id, &ts);
+  ts.tv_nsec += 10'000'000;
+  if (ts.tv_nsec >= 1'000'000'000) {
+    ts.tv_sec += 1;
+    ts.tv_nsec -= 1'000'000'000;
+  }
+  return ts;
+}
+
+TEST_F(LlvmLibcSemTest, InitGetValueDestroy) {
+  sem_t sem;
+  ASSERT_THAT(LIBC_NAMESPACE::sem_init(&sem, 0, 3), Succeeds());
+
+  int value = -1;
+  ASSERT_THAT(LIBC_NAMESPACE::sem_getvalue(&sem, &value), Succeeds());
+  EXPECT_EQ(value, 3);
+
+  ASSERT_THAT(LIBC_NAMESPACE::sem_destroy(&sem), Succeeds());
+}
+
+TEST_F(LlvmLibcSemTest, InitValueTooLarge) {
+  sem_t sem;
+  // A count above SEM_VALUE_MAX cannot be represented.
+  ASSERT_THAT(LIBC_NAMESPACE::sem_init(
+                  &sem, 0, static_cast<unsigned int>(SEM_VALUE_MAX) + 1),
+              Fails(EINVAL));
+}
+
+TEST_F(LlvmLibcSemTest, InitShared) {
+  sem_t sem;
+  // A non-zero pshared is accepted; the semaphore then uses the shared futex
+  // addressing mode.
+  ASSERT_THAT(LIBC_NAMESPACE::sem_init(&sem, 1, 1), Succeeds());
+  ASSERT_THAT(LIBC_NAMESPACE::sem_wait(&sem), Succeeds());
+  ASSERT_THAT(LIBC_NAMESPACE::sem_post(&sem), Succeeds());
+  ASSERT_THAT(LIBC_NAMESPACE::sem_destroy(&sem), Succeeds());
+}
+
+TEST_F(LlvmLibcSemTest, UseAfterDestroy) {
+  sem_t sem;
+  ASSERT_THAT(LIBC_NAMESPACE::sem_init(&sem, 0, 1), Succeeds());
+  ASSERT_THAT(LIBC_NAMESPACE::sem_destroy(&sem), Succeeds());
+
+  // The canary no longer matches, so every operation reports EINVAL rather
+  // than acting on a destroyed semaphore.
+  int value = -1;
+  EXPECT_THAT(LIBC_NAMESPACE::sem_getvalue(&sem, &value), Fails(EINVAL));
+  EXPECT_THAT(LIBC_NAMESPACE::sem_post(&sem), Fails(EINVAL));
+  EXPECT_THAT(LIBC_NAMESPACE::sem_wait(&sem), Fails(EINVAL));
+  EXPECT_THAT(LIBC_NAMESPACE::sem_trywait(&sem), Fails(EINVAL));
+  EXPECT_THAT(LIBC_NAMESPACE::sem_destroy(&sem), Fails(EINVAL));
+
+  timespec ts = deadline_in_10ms(CLOCK_REALTIME);
+  EXPECT_THAT(LIBC_NAMESPACE::sem_timedwait(&sem, &ts), Fails(EINVAL));
+  EXPECT_THAT(LIBC_NAMESPACE::sem_clockwait(&sem, CLOCK_MONOTONIC, &ts),
+              Fails(EINVAL));
+}
+
+TEST_F(LlvmLibcSemTest, PostAndWait) {
+  sem_t sem;
+  ASSERT_THAT(LIBC_NAMESPACE::sem_init(&sem, 0, 0), Succeeds());
+
+  ASSERT_THAT(LIBC_NAMESPACE::sem_post(&sem), Succeeds());
+  ASSERT_THAT(LIBC_NAMESPACE::sem_post(&sem), Succeeds());
+
+  int value = -1;
+  ASSERT_THAT(LIBC_NAMESPACE::sem_getvalue(&sem, &value), Succeeds());
+  EXPECT_EQ(value, 2);
+
+  // The count is positive, so neither wait blocks.
+  ASSERT_THAT(LIBC_NAMESPACE::sem_wait(&sem), Succeeds());
+  ASSERT_THAT(LIBC_NAMESPACE::sem_wait(&sem), Succeeds());
+
+  ASSERT_THAT(LIBC_NAMESPACE::sem_getvalue(&sem, &value), Succeeds());
+  EXPECT_EQ(value, 0);
+
+  ASSERT_THAT(LIBC_NAMESPACE::sem_destroy(&sem), Succeeds());
+}
+
+TEST_F(LlvmLibcSemTest, TryWait) {
+  sem_t sem;
+  ASSERT_THAT(LIBC_NAMESPACE::sem_init(&sem, 0, 1), Succeeds());
+
+  ASSERT_THAT(LIBC_NAMESPACE::sem_trywait(&sem), Succeeds());
+
+  // The count is now zero, so a non-blocking wait reports EAGAIN.
+  ASSERT_THAT(LIBC_NAMESPACE::sem_trywait(&sem), Fails(EAGAIN));
+
+  ASSERT_THAT(LIBC_NAMESPACE::sem_destroy(&sem), Succeeds());
+}
+
+TEST_F(LlvmLibcSemTest, PostOverflow) {
+  sem_t sem;
+  ASSERT_THAT(LIBC_NAMESPACE::sem_init(
+                  &sem, 0, static_cast<unsigned int>(SEM_VALUE_MAX)),
+              Succeeds());
+
+  // Incrementing past SEM_VALUE_MAX is refused and leaves the count alone.
+  ASSERT_THAT(LIBC_NAMESPACE::sem_post(&sem), Fails(EOVERFLOW));
+
+  int value = -1;
+  ASSERT_THAT(LIBC_NAMESPACE::sem_getvalue(&sem, &value), Succeeds());
+  EXPECT_EQ(value, SEM_VALUE_MAX);
+
+  ASSERT_THAT(LIBC_NAMESPACE::sem_destroy(&sem), Succeeds());
+}
+
+TEST_F(LlvmLibcSemTest, TimedWaitLocksImmediately) {
+  sem_t sem;
+  ASSERT_THAT(LIBC_NAMESPACE::sem_init(&sem, 0, 1), Succeeds());
+
+  // The semaphore can be locked right away, so the deadline is never read.
+  ASSERT_THAT(LIBC_NAMESPACE::sem_timedwait(&sem, nullptr), Succeeds());
+  ASSERT_THAT(LIBC_NAMESPACE::sem_destroy(&sem), Succeeds());
+}
+
+TEST_F(LlvmLibcSemTest, TimedWaitTimeout) {
+  sem_t sem;
+  ASSERT_THAT(LIBC_NAMESPACE::sem_init(&sem, 0, 0), Succeeds());
+
+  timespec ts = deadline_in_10ms(CLOCK_REALTIME);
+  ASSERT_THAT(LIBC_NAMESPACE::sem_timedwait(&sem, &ts), Fails(ETIMEDOUT));
+
+  ASSERT_THAT(LIBC_NAMESPACE::sem_destroy(&sem), Succeeds());
+}
+
+TEST_F(LlvmLibcSemTest, TimedWaitInvalidTimespec) {
+  sem_t sem;
+  ASSERT_THAT(LIBC_NAMESPACE::sem_init(&sem, 0, 0), Succeeds());
+
+  timespec ts{};
+  ts.tv_sec = 1;
+  ts.tv_nsec = 1'000'000'001;
+  ASSERT_THAT(LIBC_NAMESPACE::sem_timedwait(&sem, &ts), Fails(EINVAL));
+
+  ASSERT_THAT(LIBC_NAMESPACE::sem_destroy(&sem), Succeeds());
+}
+
+TEST_F(LlvmLibcSemTest, ClockWaitMonotonicTimeout) {
+  sem_t sem;
+  ASSERT_THAT(LIBC_NAMESPACE::sem_init(&sem, 0, 0), Succeeds());
+
+  timespec ts = deadline_in_10ms(CLOCK_MONOTONIC);
+  ASSERT_THAT(LIBC_NAMESPACE::sem_clockwait(&sem, CLOCK_MONOTONIC, &ts),
+              Fails(ETIMEDOUT));
+
+  ASSERT_THAT(LIBC_NAMESPACE::sem_destroy(&sem), Succeeds());
+}
+
+TEST_F(LlvmLibcSemTest, ClockWaitUnsupportedClock) {
+  sem_t sem;
+  ASSERT_THAT(LIBC_NAMESPACE::sem_init(&sem, 0, 0), Succeeds());
+
+  timespec ts{};
+  ts.tv_sec = 1;
+  ts.tv_nsec = 0;
+  ASSERT_THAT(
+      LIBC_NAMESPACE::sem_clockwait(&sem, static_cast<clockid_t>(99), &ts),
+      Fails(EINVAL));
+
+  ASSERT_THAT(LIBC_NAMESPACE::sem_destroy(&sem), Succeeds());
+}



More information about the libc-commits mailing list