From bed9fa2de54aafcb854dfb4cee7617db15895a99 Mon Sep 17 00:00:00 2001 From: Pengxiang Huang <71998072+Pengxiang-Huang@users.noreply.github.com> Date: Thu, 19 Mar 2026 10:12:06 -0400 Subject: [PATCH] [libc][sys/sem] Add sys v sem headers and syscall wrapper implementation (#185914) Fix #182161 Based on the last PR #182700 implementing sys/ipc. --- libc/config/linux/x86_64/entrypoints.txt | 5 + libc/config/linux/x86_64/headers.txt | 1 + libc/hdr/CMakeLists.txt | 18 ++ libc/hdr/sys_ipc_macros.h | 22 +++ libc/hdr/sys_sem_macros.h | 22 +++ libc/hdr/types/CMakeLists.txt | 27 +++ libc/hdr/types/struct_sembuf.h | 22 +++ libc/hdr/types/struct_semid_ds.h | 22 +++ libc/hdr/types/struct_seminfo.h | 22 +++ libc/include/CMakeLists.txt | 15 ++ libc/include/llvm-libc-macros/CMakeLists.txt | 6 + .../llvm-libc-macros/linux/CMakeLists.txt | 6 + .../llvm-libc-macros/linux/sys-ipc-macros.h | 1 + .../llvm-libc-macros/linux/sys-sem-macros.h | 29 +++ .../include/llvm-libc-macros/sys-sem-macros.h | 16 ++ libc/include/llvm-libc-types/CMakeLists.txt | 9 + libc/include/llvm-libc-types/struct_sembuf.h | 19 ++ .../include/llvm-libc-types/struct_semid_ds.h | 33 ++++ libc/include/llvm-libc-types/struct_seminfo.h | 27 +++ libc/include/sys/sem.yaml | 84 +++++++++ libc/src/sys/CMakeLists.txt | 1 + libc/src/sys/sem/CMakeLists.txt | 24 +++ libc/src/sys/sem/linux/CMakeLists.txt | 46 +++++ libc/src/sys/sem/linux/semctl.cpp | 101 +++++++++++ libc/src/sys/sem/linux/semget.cpp | 27 +++ libc/src/sys/sem/linux/semop.cpp | 27 +++ libc/src/sys/sem/semctl.h | 20 ++ libc/src/sys/sem/semget.h | 21 +++ libc/src/sys/sem/semop.h | 22 +++ libc/test/integration/src/CMakeLists.txt | 1 + libc/test/integration/src/sys/CMakeLists.txt | 1 + .../integration/src/sys/sem/CMakeLists.txt | 29 +++ .../test/integration/src/sys/sem/sem_test.cpp | 171 ++++++++++++++++++ libc/test/src/sys/CMakeLists.txt | 1 + libc/test/src/sys/sem/CMakeLists.txt | 3 + libc/test/src/sys/sem/linux/CMakeLists.txt | 20 ++ libc/test/src/sys/sem/linux/sem_test.cpp | 72 ++++++++ 37 files changed, 993 insertions(+) create mode 100644 libc/hdr/sys_ipc_macros.h create mode 100644 libc/hdr/sys_sem_macros.h create mode 100644 libc/hdr/types/struct_sembuf.h create mode 100644 libc/hdr/types/struct_semid_ds.h create mode 100644 libc/hdr/types/struct_seminfo.h create mode 100644 libc/include/llvm-libc-macros/linux/sys-sem-macros.h create mode 100644 libc/include/llvm-libc-macros/sys-sem-macros.h create mode 100644 libc/include/llvm-libc-types/struct_sembuf.h create mode 100644 libc/include/llvm-libc-types/struct_semid_ds.h create mode 100644 libc/include/llvm-libc-types/struct_seminfo.h create mode 100644 libc/include/sys/sem.yaml create mode 100644 libc/src/sys/sem/CMakeLists.txt create mode 100644 libc/src/sys/sem/linux/CMakeLists.txt create mode 100644 libc/src/sys/sem/linux/semctl.cpp create mode 100644 libc/src/sys/sem/linux/semget.cpp create mode 100644 libc/src/sys/sem/linux/semop.cpp create mode 100644 libc/src/sys/sem/semctl.h create mode 100644 libc/src/sys/sem/semget.h create mode 100644 libc/src/sys/sem/semop.h create mode 100644 libc/test/integration/src/sys/CMakeLists.txt create mode 100644 libc/test/integration/src/sys/sem/CMakeLists.txt create mode 100644 libc/test/integration/src/sys/sem/sem_test.cpp create mode 100644 libc/test/src/sys/sem/CMakeLists.txt create mode 100644 libc/test/src/sys/sem/linux/CMakeLists.txt create mode 100644 libc/test/src/sys/sem/linux/sem_test.cpp diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt index 1039c28eb5c6..e76f2f2ee523 100644 --- a/libc/config/linux/x86_64/entrypoints.txt +++ b/libc/config/linux/x86_64/entrypoints.txt @@ -288,6 +288,11 @@ set(TARGET_LIBC_ENTRYPOINTS libc.src.sys.resource.getrlimit libc.src.sys.resource.setrlimit + # sys/sem.h entrypoints + libc.src.sys.sem.semget + libc.src.sys.sem.semctl + libc.src.sys.sem.semop + # sys/sendfile entrypoints libc.src.sys.sendfile.sendfile diff --git a/libc/config/linux/x86_64/headers.txt b/libc/config/linux/x86_64/headers.txt index 543d15146566..157740033aab 100644 --- a/libc/config/linux/x86_64/headers.txt +++ b/libc/config/linux/x86_64/headers.txt @@ -45,6 +45,7 @@ set(TARGET_PUBLIC_HEADERS libc.include.sys_random libc.include.sys_resource libc.include.sys_select + libc.include.sys_sem libc.include.sys_socket libc.include.sys_stat libc.include.sys_statvfs diff --git a/libc/hdr/CMakeLists.txt b/libc/hdr/CMakeLists.txt index 8a1f40e64ca1..0a496206f490 100644 --- a/libc/hdr/CMakeLists.txt +++ b/libc/hdr/CMakeLists.txt @@ -149,6 +149,24 @@ add_proxy_header_library( libc.include.llvm-libc-macros.sys_ioctl_macros ) +add_proxy_header_library( + sys_ipc_macros + HDRS + sys_ipc_macros.h + FULL_BUILD_DEPENDS + libc.include.sys_ipc + libc.include.llvm-libc-macros.sys_ipc_macros +) + +add_proxy_header_library( + sys_sem_macros + HDRS + sys_sem_macros.h + FULL_BUILD_DEPENDS + libc.include.sys_sem + libc.include.llvm-libc-macros.sys_sem_macros +) + add_proxy_header_library( sys_socket_macros HDRS diff --git a/libc/hdr/sys_ipc_macros.h b/libc/hdr/sys_ipc_macros.h new file mode 100644 index 000000000000..f62ae4b6ed25 --- /dev/null +++ b/libc/hdr/sys_ipc_macros.h @@ -0,0 +1,22 @@ +//===-- Definition of macros from sys/ipc.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_SYS_IPC_MACROS_H +#define LLVM_LIBC_HDR_SYS_IPC_MACROS_H + +#ifdef LIBC_FULL_BUILD + +#include "include/llvm-libc-macros/sys-ipc-macros.h" + +#else // Overlay mode + +#include + +#endif // LLVM_LIBC_FULL_BUILD + +#endif // LLVM_LIBC_HDR_SYS_IPC_MACROS_H diff --git a/libc/hdr/sys_sem_macros.h b/libc/hdr/sys_sem_macros.h new file mode 100644 index 000000000000..1082840ea598 --- /dev/null +++ b/libc/hdr/sys_sem_macros.h @@ -0,0 +1,22 @@ +//===-- Definition of macros from sys/sem.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_SYS_SEM_MACROS_H +#define LLVM_LIBC_HDR_SYS_SEM_MACROS_H + +#ifdef LIBC_FULL_BUILD + +#include "include/llvm-libc-macros/sys-sem-macros.h" + +#else // Overlay mode + +#include + +#endif // LLVM_LIBC_FULL_BUILD + +#endif // LLVM_LIBC_HDR_SYS_SEM_MACROS_H diff --git a/libc/hdr/types/CMakeLists.txt b/libc/hdr/types/CMakeLists.txt index 84a26ecf8fb5..68b74700aef9 100644 --- a/libc/hdr/types/CMakeLists.txt +++ b/libc/hdr/types/CMakeLists.txt @@ -514,6 +514,33 @@ add_proxy_header_library( libc.include.sys_resource ) +add_proxy_header_library( + struct_semid_ds + HDRS + struct_semid_ds.h + FULL_BUILD_DEPENDS + libc.include.llvm-libc-types.struct_semid_ds + libc.include.sys_sem +) + +add_proxy_header_library( + struct_sembuf + HDRS + struct_sembuf.h + FULL_BUILD_DEPENDS + libc.include.llvm-libc-types.struct_sembuf + libc.include.sys_sem +) + +add_proxy_header_library( + struct_seminfo + HDRS + struct_seminfo.h + FULL_BUILD_DEPENDS + libc.include.llvm-libc-types.struct_seminfo + libc.include.sys_sem +) + add_proxy_header_library( gid_t HDRS diff --git a/libc/hdr/types/struct_sembuf.h b/libc/hdr/types/struct_sembuf.h new file mode 100644 index 000000000000..d5efcbaa0152 --- /dev/null +++ b/libc/hdr/types/struct_sembuf.h @@ -0,0 +1,22 @@ +//===-- Proxy for struct sembuf -------------------------------------------===// +// +// 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_STRUCT_SEMBUF_H +#define LLVM_LIBC_HDR_TYPES_STRUCT_SEMBUF_H + +#ifdef LIBC_FULL_BUILD + +#include "include/llvm-libc-types/struct_sembuf.h" + +#else // Overlay mode + +#include + +#endif // LLVM_LIBC_FULL_BUILD + +#endif // LLVM_LIBC_HDR_TYPES_STRUCT_SEMBUF_H diff --git a/libc/hdr/types/struct_semid_ds.h b/libc/hdr/types/struct_semid_ds.h new file mode 100644 index 000000000000..f4d8b36362d4 --- /dev/null +++ b/libc/hdr/types/struct_semid_ds.h @@ -0,0 +1,22 @@ +//===-- Proxy for struct semid_ds -----------------------------------------===// +// +// 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_STRUCT_SEMID_DS_H +#define LLVM_LIBC_HDR_TYPES_STRUCT_SEMID_DS_H + +#ifdef LIBC_FULL_BUILD + +#include "include/llvm-libc-types/struct_semid_ds.h" + +#else // Overlay mode + +#include + +#endif // LLVM_LIBC_FULL_BUILD + +#endif // LLVM_LIBC_HDR_TYPES_STRUCT_SEMID_DS_H diff --git a/libc/hdr/types/struct_seminfo.h b/libc/hdr/types/struct_seminfo.h new file mode 100644 index 000000000000..86259425712d --- /dev/null +++ b/libc/hdr/types/struct_seminfo.h @@ -0,0 +1,22 @@ +//===-- Proxy for struct seminfo ------------------------------------------===// +// +// 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_STRUCT_SEMINFO_H +#define LLVM_LIBC_HDR_TYPES_STRUCT_SEMINFO_H + +#ifdef LIBC_FULL_BUILD + +#include "include/llvm-libc-types/struct_seminfo.h" + +#else // Overlay mode + +#include + +#endif // LLVM_LIBC_FULL_BUILD + +#endif // LLVM_LIBC_HDR_TYPES_STRUCT_SEMINFO_H diff --git a/libc/include/CMakeLists.txt b/libc/include/CMakeLists.txt index 9eae20c820e9..e0e2e058da85 100644 --- a/libc/include/CMakeLists.txt +++ b/libc/include/CMakeLists.txt @@ -660,6 +660,21 @@ add_header_macro( .llvm-libc-types.struct_timeval ) +add_header_macro( + sys_sem + ../libc/include/sys/sem.yaml + sys/sem.h + DEPENDS + .llvm_libc_common_h + .llvm-libc-macros.sys_sem_macros + .llvm-libc-types.key_t + .llvm-libc-types.size_t + .llvm-libc-types.struct_ipc_perm + .llvm-libc-types.struct_semid_ds + .llvm-libc-types.struct_sembuf + .llvm-libc-types.struct_seminfo +) + add_header_macro( sys_sendfile ../libc/include/sys/sendfile.yaml diff --git a/libc/include/llvm-libc-macros/CMakeLists.txt b/libc/include/llvm-libc-macros/CMakeLists.txt index 553879e27d3d..6a91b394a878 100644 --- a/libc/include/llvm-libc-macros/CMakeLists.txt +++ b/libc/include/llvm-libc-macros/CMakeLists.txt @@ -232,6 +232,12 @@ add_macro_header( sys-ipc-macros.h ) +add_macro_header( + sys_sem_macros + HDR + sys-sem-macros.h +) + add_macro_header( sys_stat_macros HDR diff --git a/libc/include/llvm-libc-macros/linux/CMakeLists.txt b/libc/include/llvm-libc-macros/linux/CMakeLists.txt index 60376641b50f..e6a95457a5b9 100644 --- a/libc/include/llvm-libc-macros/linux/CMakeLists.txt +++ b/libc/include/llvm-libc-macros/linux/CMakeLists.txt @@ -46,6 +46,12 @@ add_header( sys-random-macros.h ) +add_header( + sys_sem_macros + HDR + sys-sem-macros.h +) + add_header( sys_socket_macros HDR diff --git a/libc/include/llvm-libc-macros/linux/sys-ipc-macros.h b/libc/include/llvm-libc-macros/linux/sys-ipc-macros.h index 87546ac314d8..6c8443cef5b3 100644 --- a/libc/include/llvm-libc-macros/linux/sys-ipc-macros.h +++ b/libc/include/llvm-libc-macros/linux/sys-ipc-macros.h @@ -20,5 +20,6 @@ #define IPC_RMID 0 #define IPC_SET 1 #define IPC_STAT 2 +#define IPC_INFO 3 #endif // LLVM_LIBC_MACROS_LINUX_SYS_IPC_MACROS_H diff --git a/libc/include/llvm-libc-macros/linux/sys-sem-macros.h b/libc/include/llvm-libc-macros/linux/sys-sem-macros.h new file mode 100644 index 000000000000..1ca62e6b1e29 --- /dev/null +++ b/libc/include/llvm-libc-macros/linux/sys-sem-macros.h @@ -0,0 +1,29 @@ +//===-- Definition of macros from sys/sem.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_LINUX_SYS_SEM_MACROS_H +#define LLVM_LIBC_MACROS_LINUX_SYS_SEM_MACROS_H + +// semop flags +#define SEM_UNDO 0x1000 + +// semctl command definitions +#define GETPID 11 +#define GETVAL 12 +#define GETALL 13 +#define GETNCNT 14 +#define GETZCNT 15 +#define SETVAL 16 +#define SETALL 17 + +// linux specific extensions +#define SEM_STAT 18 +#define SEM_INFO 19 +#define SEM_STAT_ANY 20 + +#endif // LLVM_LIBC_MACROS_LINUX_SYS_SEM_MACROS_H diff --git a/libc/include/llvm-libc-macros/sys-sem-macros.h b/libc/include/llvm-libc-macros/sys-sem-macros.h new file mode 100644 index 000000000000..801f82e79f5d --- /dev/null +++ b/libc/include/llvm-libc-macros/sys-sem-macros.h @@ -0,0 +1,16 @@ +//===-- Macros defined in sys/sem.h header file ---------------------------===// +// +// 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_SYS_SEM_MACROS_H +#define LLVM_LIBC_MACROS_SYS_SEM_MACROS_H + +#ifdef __linux__ +#include "linux/sys-sem-macros.h" +#endif + +#endif // LLVM_LIBC_MACROS_SYS_SEM_MACROS_H diff --git a/libc/include/llvm-libc-types/CMakeLists.txt b/libc/include/llvm-libc-types/CMakeLists.txt index b03e4fa830b0..44b3a7e82bee 100644 --- a/libc/include/llvm-libc-types/CMakeLists.txt +++ b/libc/include/llvm-libc-types/CMakeLists.txt @@ -101,6 +101,15 @@ add_header( add_header(struct_pollfd HDR struct_pollfd.h) add_header(struct_rlimit HDR struct_rlimit.h DEPENDS .rlim_t) add_header(struct_sched_param HDR struct_sched_param.h) +add_header(struct_sembuf HDR struct_sembuf.h) +add_header(struct_seminfo HDR struct_seminfo.h) +add_header( + struct_semid_ds + HDR struct_semid_ds.h + DEPENDS + .struct_ipc_perm + .time_t +) add_header(struct_timeval HDR struct_timeval.h DEPENDS .suseconds_t .time_t) add_header(struct_itimerval HDR struct_itimerval.h DEPENDS .struct_timeval) add_header(struct_rusage HDR struct_rusage.h DEPENDS .struct_timeval) diff --git a/libc/include/llvm-libc-types/struct_sembuf.h b/libc/include/llvm-libc-types/struct_sembuf.h new file mode 100644 index 000000000000..8358effdf790 --- /dev/null +++ b/libc/include/llvm-libc-types/struct_sembuf.h @@ -0,0 +1,19 @@ +//===-- Definition of struct sembuf ---------------------------------------===// +// +// 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_STRUCT_SEMBUF_H +#define LLVM_LIBC_TYPES_STRUCT_SEMBUF_H + +struct sembuf { + // changed to unsigned short from short since POSIX issue 7 + unsigned short sem_num; + short sem_op; + short sem_flg; +}; + +#endif // LLVM_LIBC_TYPES_STRUCT_SEMBUF_H diff --git a/libc/include/llvm-libc-types/struct_semid_ds.h b/libc/include/llvm-libc-types/struct_semid_ds.h new file mode 100644 index 000000000000..775f52604b0a --- /dev/null +++ b/libc/include/llvm-libc-types/struct_semid_ds.h @@ -0,0 +1,33 @@ +//===-- Definition of struct semid_ds -------------------------------------===// +// +// 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_STRUCT_SEMID_DS_H +#define LLVM_LIBC_TYPES_STRUCT_SEMID_DS_H + +#include "struct_ipc_perm.h" +#include "time_t.h" + +struct semid_ds { + struct ipc_perm sem_perm; +#ifdef __linux__ + time_t sem_otime; + unsigned long __unused1; + time_t sem_ctime; + unsigned long __unused2; +#else + time_t sem_otime; + time_t sem_ctime; +#endif + unsigned long sem_nsems; +#ifdef __linux__ + unsigned long __unused3; + unsigned long __unused4; +#endif +}; + +#endif // LLVM_LIBC_TYPES_STRUCT_SEMID_DS_H diff --git a/libc/include/llvm-libc-types/struct_seminfo.h b/libc/include/llvm-libc-types/struct_seminfo.h new file mode 100644 index 000000000000..90610ac828a7 --- /dev/null +++ b/libc/include/llvm-libc-types/struct_seminfo.h @@ -0,0 +1,27 @@ +//===-- Definition of struct seminfo --------------------------------------===// +// +// 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_STRUCT_SEMINFO_H +#define LLVM_LIBC_TYPES_STRUCT_SEMINFO_H + +#ifdef __linux__ +struct seminfo { + int semmap; + int semmni; + int semmns; + int semmnu; + int semmsl; + int semopm; + int semume; + int semusz; + int semvmx; + int semaem; +}; +#endif + +#endif // LLVM_LIBC_TYPES_STRUCT_SEMINFO_H diff --git a/libc/include/sys/sem.yaml b/libc/include/sys/sem.yaml new file mode 100644 index 000000000000..6e583d669d25 --- /dev/null +++ b/libc/include/sys/sem.yaml @@ -0,0 +1,84 @@ +header: sys/sem.h +standards: + - posix + - linux +macros: + - macro_name: SEM_UNDO + macro_header: sys-sem-macros.h + standards: + - posix + - macro_name: GETNCNT + macro_header: sys-sem-macros.h + standards: + - posix + - macro_name: GETPID + macro_header: sys-sem-macros.h + standards: + - posix + - macro_name: GETVAL + macro_header: sys-sem-macros.h + standards: + - posix + - macro_name: GETALL + macro_header: sys-sem-macros.h + standards: + - posix + - macro_name: GETZCNT + macro_header: sys-sem-macros.h + standards: + - posix + - macro_name: SETVAL + macro_header: sys-sem-macros.h + standards: + - posix + - macro_name: SETALL + macro_header: sys-sem-macros.h + standards: + - posix + - macro_name: SEM_STAT + macro_header: sys-sem-macros.h + standards: + - linux + - macro_name: SEM_INFO + macro_header: sys-sem-macros.h + standards: + - linux + - macro_name: SEM_STAT_ANY + macro_header: sys-sem-macros.h + standards: + - linux +types: + - type_name: key_t + - type_name: size_t + - type_name: struct_ipc_perm + - type_name: struct_semid_ds + - type_name: struct_sembuf + - type_name: struct_seminfo + standards: + - linux +functions: + - name: semctl + standards: + - posix + return_type: int + arguments: + - type: int + - type: int + - type: int + - type: '...' + - name: semget + standards: + - posix + return_type: int + arguments: + - type: key_t + - type: int + - type: int + - name: semop + standards: + - posix + return_type: int + arguments: + - type: int + - type: struct sembuf * + - type: size_t diff --git a/libc/src/sys/CMakeLists.txt b/libc/src/sys/CMakeLists.txt index 1c17ec31a859..41becce798dd 100644 --- a/libc/src/sys/CMakeLists.txt +++ b/libc/src/sys/CMakeLists.txt @@ -6,6 +6,7 @@ add_subdirectory(random) add_subdirectory(resource) add_subdirectory(select) add_subdirectory(socket) +add_subdirectory(sem) add_subdirectory(sendfile) add_subdirectory(stat) add_subdirectory(statvfs) diff --git a/libc/src/sys/sem/CMakeLists.txt b/libc/src/sys/sem/CMakeLists.txt new file mode 100644 index 000000000000..4b9bd851b9d2 --- /dev/null +++ b/libc/src/sys/sem/CMakeLists.txt @@ -0,0 +1,24 @@ +if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS}) + add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS}) +endif() + +add_entrypoint_object( + semget + ALIAS + DEPENDS + .${LIBC_TARGET_OS}.semget +) + +add_entrypoint_object( + semctl + ALIAS + DEPENDS + .${LIBC_TARGET_OS}.semctl +) + +add_entrypoint_object( + semop + ALIAS + DEPENDS + .${LIBC_TARGET_OS}.semop +) diff --git a/libc/src/sys/sem/linux/CMakeLists.txt b/libc/src/sys/sem/linux/CMakeLists.txt new file mode 100644 index 000000000000..0be957ab660d --- /dev/null +++ b/libc/src/sys/sem/linux/CMakeLists.txt @@ -0,0 +1,46 @@ +add_entrypoint_object( + semget + SRCS + semget.cpp + HDRS + ../semget.h + DEPENDS + libc.hdr.types.key_t + libc.include.sys_syscall + libc.src.__support.OSUtil.osutil + libc.src.__support.common + libc.src.errno.errno +) + +add_entrypoint_object( + semctl + SRCS + semctl.cpp + HDRS + ../semctl.h + DEPENDS + libc.hdr.errno_macros + libc.hdr.sys_ipc_macros + libc.hdr.sys_sem_macros + libc.hdr.types.struct_semid_ds + libc.hdr.types.struct_seminfo + libc.include.sys_syscall + libc.src.__support.OSUtil.osutil + libc.src.__support.common + libc.src.errno.errno +) + +add_entrypoint_object( + semop + SRCS + semop.cpp + HDRS + ../semop.h + DEPENDS + libc.hdr.types.size_t + libc.hdr.types.struct_sembuf + libc.include.sys_syscall + libc.src.__support.OSUtil.osutil + libc.src.__support.common + libc.src.errno.errno +) diff --git a/libc/src/sys/sem/linux/semctl.cpp b/libc/src/sys/sem/linux/semctl.cpp new file mode 100644 index 000000000000..1dd38f773004 --- /dev/null +++ b/libc/src/sys/sem/linux/semctl.cpp @@ -0,0 +1,101 @@ +//===-- Linux implementation of semctl ------------------------------------===// +// +// 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/sys/sem/semctl.h" + +#include "hdr/errno_macros.h" +#include "hdr/sys_ipc_macros.h" +#include "hdr/sys_sem_macros.h" +#include "hdr/types/struct_semid_ds.h" +#include "hdr/types/struct_seminfo.h" +#include "src/__support/OSUtil/syscall.h" +#include "src/__support/common.h" +#include "src/__support/libc_errno.h" +#include +#include + +namespace LIBC_NAMESPACE_DECL { + +LLVM_LIBC_FUNCTION(int, semctl, (int semid, int semnum, int cmd, ...)) { + // used to parse the fourth varargs argument + // its expected to be explicitly declared by application as an union type: + // union semun { + // int val; + // struct semid_ds *buf; + // unsigned short *array; + // struct seminfo *__buf; (* linux specific *) + // } arg; + unsigned long cmd_arg = 0; + + // parse cmd_arg based on the flags + switch (cmd) { + // does not use the vargs + case IPC_RMID: + case GETVAL: + case GETPID: + case GETNCNT: + case GETZCNT: + break; + + // use vargs as int, semun->val + case SETVAL: { + va_list vargs; + va_start(vargs, cmd); + cmd_arg = static_cast(va_arg(vargs, int)); + va_end(vargs); + break; + } + + // use vargs as semid_ds*, semun->buf + case IPC_SET: + case IPC_STAT: + case SEM_STAT: + case SEM_STAT_ANY: { + va_list vargs; + va_start(vargs, cmd); + cmd_arg = reinterpret_cast(va_arg(vargs, struct semid_ds *)); + va_end(vargs); + break; + } + + // use vargs as short*, semun->array + case GETALL: + case SETALL: { + va_list vargs; + va_start(vargs, cmd); + cmd_arg = reinterpret_cast(va_arg(vargs, unsigned short *)); + va_end(vargs); + break; + } + + // linux specific, use vargs as seminfo*, semun->__buf + case IPC_INFO: + case SEM_INFO: { + va_list vargs; + va_start(vargs, cmd); + cmd_arg = reinterpret_cast(va_arg(vargs, struct seminfo *)); + va_end(vargs); + break; + } + + // unrecognized flags + default: + libc_errno = EINVAL; + return -1; + } + + int ret = LIBC_NAMESPACE::syscall_impl(SYS_semctl, semid, semnum, cmd, + cmd_arg); + if (ret < 0) { + libc_errno = -ret; + return -1; + } + return ret; +} + +} // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/sys/sem/linux/semget.cpp b/libc/src/sys/sem/linux/semget.cpp new file mode 100644 index 000000000000..b5a69056ccbd --- /dev/null +++ b/libc/src/sys/sem/linux/semget.cpp @@ -0,0 +1,27 @@ +//===-- Linux implementation of semget ------------------------------------===// +// +// 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/sys/sem/semget.h" + +#include "src/__support/OSUtil/syscall.h" +#include "src/__support/common.h" +#include "src/__support/libc_errno.h" +#include + +namespace LIBC_NAMESPACE_DECL { + +LLVM_LIBC_FUNCTION(int, semget, (key_t key, int nsems, int semflg)) { + int ret = LIBC_NAMESPACE::syscall_impl(SYS_semget, key, nsems, semflg); + if (ret < 0) { + libc_errno = -ret; + return -1; + } + return ret; +} + +} // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/sys/sem/linux/semop.cpp b/libc/src/sys/sem/linux/semop.cpp new file mode 100644 index 000000000000..97d7d12daf89 --- /dev/null +++ b/libc/src/sys/sem/linux/semop.cpp @@ -0,0 +1,27 @@ +//===-- Linux implementation of semop -------------------------------------===// +// +// 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/sys/sem/semop.h" + +#include "src/__support/OSUtil/syscall.h" +#include "src/__support/common.h" +#include "src/__support/libc_errno.h" +#include + +namespace LIBC_NAMESPACE_DECL { + +LLVM_LIBC_FUNCTION(int, semop, (int semid, struct sembuf *sops, size_t nsops)) { + int ret = LIBC_NAMESPACE::syscall_impl(SYS_semop, semid, sops, nsops); + if (ret < 0) { + libc_errno = -ret; + return -1; + } + return ret; +} + +} // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/sys/sem/semctl.h b/libc/src/sys/sem/semctl.h new file mode 100644 index 000000000000..b7046edba18b --- /dev/null +++ b/libc/src/sys/sem/semctl.h @@ -0,0 +1,20 @@ +//===-- Implementation header for semctl ------------------------*- 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_SYS_SEM_SEMCTL_H +#define LLVM_LIBC_SRC_SYS_SEM_SEMCTL_H + +#include "src/__support/macros/config.h" + +namespace LIBC_NAMESPACE_DECL { + +int semctl(int semid, int semnum, int cmd, ...); + +} // namespace LIBC_NAMESPACE_DECL + +#endif // LLVM_LIBC_SRC_SYS_SEM_SEMCTL_H diff --git a/libc/src/sys/sem/semget.h b/libc/src/sys/sem/semget.h new file mode 100644 index 000000000000..7730b21eb0f7 --- /dev/null +++ b/libc/src/sys/sem/semget.h @@ -0,0 +1,21 @@ +//===-- Implementation header for semget ------------------------*- 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_SYS_SEM_SEMGET_H +#define LLVM_LIBC_SRC_SYS_SEM_SEMGET_H + +#include "hdr/types/key_t.h" +#include "src/__support/macros/config.h" + +namespace LIBC_NAMESPACE_DECL { + +int semget(key_t key, int nsems, int semflg); + +} // namespace LIBC_NAMESPACE_DECL + +#endif // LLVM_LIBC_SRC_SYS_SEM_SEMGET_H diff --git a/libc/src/sys/sem/semop.h b/libc/src/sys/sem/semop.h new file mode 100644 index 000000000000..c1af69bd9218 --- /dev/null +++ b/libc/src/sys/sem/semop.h @@ -0,0 +1,22 @@ +//===-- Implementation header for semop -------------------------*- 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_SYS_SEM_SEMOP_H +#define LLVM_LIBC_SRC_SYS_SEM_SEMOP_H + +#include "hdr/types/size_t.h" +#include "hdr/types/struct_sembuf.h" +#include "src/__support/macros/config.h" + +namespace LIBC_NAMESPACE_DECL { + +int semop(int semid, struct sembuf *sops, size_t nsops); + +} // namespace LIBC_NAMESPACE_DECL + +#endif // LLVM_LIBC_SRC_SYS_SEM_SEMOP_H diff --git a/libc/test/integration/src/CMakeLists.txt b/libc/test/integration/src/CMakeLists.txt index 1104b3de99e9..47c0716dc22b 100644 --- a/libc/test/integration/src/CMakeLists.txt +++ b/libc/test/integration/src/CMakeLists.txt @@ -4,4 +4,5 @@ add_subdirectory(spawn) add_subdirectory(stdio) add_subdirectory(stdlib) add_subdirectory(threads) +add_subdirectory(sys) add_subdirectory(unistd) diff --git a/libc/test/integration/src/sys/CMakeLists.txt b/libc/test/integration/src/sys/CMakeLists.txt new file mode 100644 index 000000000000..9a6b3cd220e8 --- /dev/null +++ b/libc/test/integration/src/sys/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(sem) diff --git a/libc/test/integration/src/sys/sem/CMakeLists.txt b/libc/test/integration/src/sys/sem/CMakeLists.txt new file mode 100644 index 000000000000..286ccb72dae9 --- /dev/null +++ b/libc/test/integration/src/sys/sem/CMakeLists.txt @@ -0,0 +1,29 @@ +add_custom_target(sys-sem-integration-tests) +add_dependencies(libc-integration-tests sys-sem-integration-tests) + +add_integration_test( + sem_test + SUITE + sys-sem-integration-tests + SRCS + sem_test.cpp + DEPENDS + libc.hdr.fcntl_macros + libc.hdr.sys_ipc_macros + libc.hdr.sys_sem_macros + libc.hdr.types.struct_sembuf + libc.src.__support.threads.sleep + libc.src.fcntl.open + libc.src.signal.kill + libc.src.stdlib.exit + libc.src.sys.ipc.ftok + libc.src.sys.sem.semctl + libc.src.sys.sem.semget + libc.src.sys.sem.semop + libc.src.sys.wait.waitpid + libc.src.unistd.close + libc.src.unistd.fork + libc.src.unistd.unlink + libc.include.signal + libc.include.sys_wait +) diff --git a/libc/test/integration/src/sys/sem/sem_test.cpp b/libc/test/integration/src/sys/sem/sem_test.cpp new file mode 100644 index 000000000000..0d8532df89be --- /dev/null +++ b/libc/test/integration/src/sys/sem/sem_test.cpp @@ -0,0 +1,171 @@ +//===-- Integration test for sys/sem --------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "src/__support/threads/sleep.h" +#include "src/fcntl/open.h" +#include "src/signal/kill.h" +#include "src/stdlib/exit.h" +#include "src/sys/ipc/ftok.h" +#include "src/sys/sem/semctl.h" +#include "src/sys/sem/semget.h" +#include "src/sys/sem/semop.h" +#include "src/sys/wait/waitpid.h" +#include "src/unistd/close.h" +#include "src/unistd/fork.h" +#include "src/unistd/unlink.h" + +#include "test/IntegrationTest/test.h" + +#include "hdr/fcntl_macros.h" +#include "hdr/sys_ipc_macros.h" +#include "hdr/sys_sem_macros.h" +#include "hdr/types/struct_sembuf.h" + +#include +#include +#include + +// child try IPC_CREAT|IPC_EXCL, +// but expect EEXIST then fall back to get the existing semaphore. +static int sem_joiner_get(key_t semkey) { + // this expect to fail and return -1 because we used the same key + // and this semaphore has been initialized. + int sid = LIBC_NAMESPACE::semget(semkey, 1, IPC_CREAT | IPC_EXCL | 0666); + if (sid == -1 && errno == EEXIST) + // get the initialized semaphore id + sid = LIBC_NAMESPACE::semget(semkey, 0, 0); + return sid; +} + +// fork a child that acquires the semaphore and hold untill killed. +// returns child pid. +static pid_t fork_sem_holder(key_t semkey) { + pid_t pid = LIBC_NAMESPACE::fork(); + if (pid == 0) { + int sid = sem_joiner_get(semkey); + if (sid < 0) + LIBC_NAMESPACE::exit(1); + // try acquire and hold + // set flag to UNDO if process terminate unexpectedly + struct sembuf acq = {0, -1, SEM_UNDO}; + if (LIBC_NAMESPACE::semop(sid, &acq, 1) != 0) + LIBC_NAMESPACE::exit(2); + + // hold the semaphore until killed by parent. + while (1) + LIBC_NAMESPACE::sleep_briefly(); + } + return pid; +} + +// fork a child that tries to acquire (IPC_NOWAIT). +// exit code: 0 = acquired, 1 = EAGAIN (full, try later again), 2+ = error. +static pid_t fork_sem_try_acquire(key_t semkey) { + pid_t pid = LIBC_NAMESPACE::fork(); + if (pid == 0) { + int sid = sem_joiner_get(semkey); + if (sid < 0) + LIBC_NAMESPACE::exit(3); + // try acquire + struct sembuf acq = {0, -1, SEM_UNDO | IPC_NOWAIT}; + + if (LIBC_NAMESPACE::semop(sid, &acq, 1) == 0) + LIBC_NAMESPACE::exit(0); // acquired + + // exit with code 1 if EAGAIN + LIBC_NAMESPACE::exit(errno == EAGAIN ? 1 : 2); + } + return pid; +} + +// wait for child to terminate. +static int wait_for_child(pid_t pid) { + int status; + LIBC_NAMESPACE::waitpid(pid, &status, 0); + return status; +} + +// semaphore gate that allows at most two processes to enter +void sem_gate_test() { + constexpr const char *TEST_FILE = "/tmp/sem_gate_test"; + int fd = LIBC_NAMESPACE::open(TEST_FILE, O_CREAT | O_WRONLY, 0666); + ASSERT_TRUE(fd >= 0); + ASSERT_ERRNO_SUCCESS(); + ASSERT_TRUE(LIBC_NAMESPACE::close(fd) == 0); + + key_t semkey = LIBC_NAMESPACE::ftok(TEST_FILE, 'a'); + ASSERT_TRUE(semkey != key_t(-1)); + ASSERT_ERRNO_SUCCESS(); + + // clean up stale semaphore from previous run because semaphore + // are kernel persistent, they can remain on the system if previous + // test fail and exit. + int old_semid = LIBC_NAMESPACE::semget(semkey, 1, 0); + if (old_semid != -1) + LIBC_NAMESPACE::semctl(old_semid, 0, IPC_RMID); + errno = 0; + + // create a semaphore + int semid = LIBC_NAMESPACE::semget(semkey, 1, IPC_CREAT | IPC_EXCL | 0666); + ASSERT_TRUE(semid >= 0); + ASSERT_ERRNO_SUCCESS(); + + // increment the first semaphore by 2 that allows two processes to access + struct sembuf sbuf = {0, 2, 0}; + ASSERT_TRUE(LIBC_NAMESPACE::semop(semid, &sbuf, 1) == 0); + + // verify the first semaphore value + ASSERT_TRUE(LIBC_NAMESPACE::semctl(semid, 0, GETVAL) == 2); + + // fork two children to hold the semaphore + pid_t holder1 = fork_sem_holder(semkey); + ASSERT_TRUE(holder1 > 0); + pid_t holder2 = fork_sem_holder(semkey); + ASSERT_TRUE(holder2 > 0); + + // wait until both children have acquired so semaphore value reaches 0. + while (LIBC_NAMESPACE::semctl(semid, 0, GETVAL) > 0) + LIBC_NAMESPACE::sleep_briefly(); + + // check my current semaphore value is 0 + ASSERT_TRUE(LIBC_NAMESPACE::semctl(semid, 0, GETVAL) == 0); + + // now the semaphore gate is full + // fork another process to try to acquire it + pid_t blocked = fork_sem_try_acquire(semkey); + ASSERT_TRUE(blocked > 0); + int blocked_status = wait_for_child(blocked); + // check the exit status + ASSERT_TRUE(WIFEXITED(blocked_status)); + ASSERT_EQ(WEXITSTATUS(blocked_status), 1); // EAGAIN (blocked) + + // kill the first holder to make one slot avalaible in semaphore + LIBC_NAMESPACE::kill(holder1, SIGKILL); + wait_for_child(holder1); + ASSERT_TRUE(LIBC_NAMESPACE::semctl(semid, 0, GETVAL) == 1); + + // then fork child again to try acquire + pid_t unblocked = fork_sem_try_acquire(semkey); + ASSERT_TRUE(unblocked > 0); + int unblocked_status = wait_for_child(unblocked); + // this child should get it since the slot is avalaible now + ASSERT_TRUE(WIFEXITED(unblocked_status)); + ASSERT_EQ(WEXITSTATUS(unblocked_status), 0); // acquired + + // cleanup + LIBC_NAMESPACE::kill(holder2, SIGKILL); + wait_for_child(holder2); + ASSERT_TRUE(LIBC_NAMESPACE::semctl(semid, 0, IPC_RMID) == 0); + ASSERT_TRUE(LIBC_NAMESPACE::unlink(TEST_FILE) == 0); +} + +TEST_MAIN([[maybe_unused]] int argc, [[maybe_unused]] char **argv, + [[maybe_unused]] char **envp) { + sem_gate_test(); + return 0; +} diff --git a/libc/test/src/sys/CMakeLists.txt b/libc/test/src/sys/CMakeLists.txt index 3b5ea64fb144..444f86f5d00a 100644 --- a/libc/test/src/sys/CMakeLists.txt +++ b/libc/test/src/sys/CMakeLists.txt @@ -15,3 +15,4 @@ add_subdirectory(ipc) add_subdirectory(uio) add_subdirectory(time) add_subdirectory(ioctl) +add_subdirectory(sem) diff --git a/libc/test/src/sys/sem/CMakeLists.txt b/libc/test/src/sys/sem/CMakeLists.txt new file mode 100644 index 000000000000..b4bbe81c92ff --- /dev/null +++ b/libc/test/src/sys/sem/CMakeLists.txt @@ -0,0 +1,3 @@ +if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS}) + add_subdirectory(${LIBC_TARGET_OS}) +endif() diff --git a/libc/test/src/sys/sem/linux/CMakeLists.txt b/libc/test/src/sys/sem/linux/CMakeLists.txt new file mode 100644 index 000000000000..7713c3c927b9 --- /dev/null +++ b/libc/test/src/sys/sem/linux/CMakeLists.txt @@ -0,0 +1,20 @@ +add_custom_target(libc_sys_sem_unittests) + +add_libc_unittest( + sem_test + SUITE + libc_sys_sem_unittests + SRCS + sem_test.cpp + DEPENDS + libc.hdr.sys_ipc_macros + libc.hdr.sys_sem_macros + libc.hdr.types.struct_sembuf + libc.hdr.types.struct_semid_ds + libc.src.errno.errno + libc.src.sys.sem.semget + libc.src.sys.sem.semctl + libc.src.sys.sem.semop + libc.test.UnitTest.ErrnoCheckingTest + libc.test.UnitTest.ErrnoSetterMatcher +) diff --git a/libc/test/src/sys/sem/linux/sem_test.cpp b/libc/test/src/sys/sem/linux/sem_test.cpp new file mode 100644 index 000000000000..0e21189f9bfb --- /dev/null +++ b/libc/test/src/sys/sem/linux/sem_test.cpp @@ -0,0 +1,72 @@ +//===-- Unittests for sys/sem.h entrypoints ------------------------------===// +// +// 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/sys_ipc_macros.h" +#include "hdr/sys_sem_macros.h" +#include "hdr/types/struct_sembuf.h" +#include "hdr/types/struct_semid_ds.h" +#include "src/sys/sem/semctl.h" +#include "src/sys/sem/semget.h" +#include "src/sys/sem/semop.h" +#include "test/UnitTest/ErrnoCheckingTest.h" +#include "test/UnitTest/ErrnoSetterMatcher.h" +#include "test/UnitTest/Test.h" + +using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Succeeds; +using LlvmLibcSysSemTest = LIBC_NAMESPACE::testing::ErrnoCheckingTest; + +union semun { + int val; + struct semid_ds *buf; + unsigned short *array; +}; + +TEST_F(LlvmLibcSysSemTest, SemgetSemctlSemopFlow) { + + // create a semaphore + int semid = + LIBC_NAMESPACE::semget(IPC_PRIVATE, 1, IPC_CREAT | IPC_EXCL | 0600); + ASSERT_ERRNO_SUCCESS(); + ASSERT_GT(semid, -1); + + union semun set_val; + set_val.val = 1; + + // set the semaphore value to 1 + ASSERT_THAT(LIBC_NAMESPACE::semctl(semid, 0, SETVAL, set_val), Succeeds(0)); + + // get the value of semaphore should be 1 + ASSERT_THAT(LIBC_NAMESPACE::semctl(semid, 0, GETVAL), Succeeds(1)); + + // decrement the semaphore value with IPC_NOWAIT flag + struct sembuf decrement_op = {0, -1, IPC_NOWAIT}; + ASSERT_THAT(LIBC_NAMESPACE::semop(semid, &decrement_op, 1), Succeeds(0)); + + // get the value of semaphore should be 0 + ASSERT_THAT(LIBC_NAMESPACE::semctl(semid, 0, GETVAL), Succeeds(0)); + + // increment the semaphore with IPC_NOWAIT flag + struct sembuf increment_op = {0, 1, IPC_NOWAIT}; + ASSERT_THAT(LIBC_NAMESPACE::semop(semid, &increment_op, 1), Succeeds(0)); + + // get the semaphore value should be 1 + ASSERT_THAT(LIBC_NAMESPACE::semctl(semid, 0, GETVAL), Succeeds(1)); + + // get the IPC stats + struct semid_ds sem_ds; + union semun stat_arg; + stat_arg.buf = &sem_ds; + ASSERT_THAT(LIBC_NAMESPACE::semctl(semid, 0, IPC_STAT, stat_arg), + Succeeds(0)); + + // the number of sem is 1 + ASSERT_EQ(sem_ds.sem_nsems, 1UL); + + // destroy the semaphore + ASSERT_THAT(LIBC_NAMESPACE::semctl(semid, 0, IPC_RMID), Succeeds(0)); +}