[libc][sys/sem] Add sys v sem headers and syscall wrapper implementation (#185914)

Fix #182161
Based on the last PR #182700 implementing sys/ipc.
This commit is contained in:
Pengxiang Huang
2026-03-19 10:12:06 -04:00
committed by GitHub
parent 0e7262407c
commit bed9fa2de5
37 changed files with 993 additions and 0 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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

22
libc/hdr/sys_ipc_macros.h Normal file
View File

@@ -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 <sys/ipc.h>
#endif // LLVM_LIBC_FULL_BUILD
#endif // LLVM_LIBC_HDR_SYS_IPC_MACROS_H

22
libc/hdr/sys_sem_macros.h Normal file
View File

@@ -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 <sys/sem.h>
#endif // LLVM_LIBC_FULL_BUILD
#endif // LLVM_LIBC_HDR_SYS_SEM_MACROS_H

View File

@@ -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

View File

@@ -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 <sys/sem.h>
#endif // LLVM_LIBC_FULL_BUILD
#endif // LLVM_LIBC_HDR_TYPES_STRUCT_SEMBUF_H

View File

@@ -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 <sys/sem.h>
#endif // LLVM_LIBC_FULL_BUILD
#endif // LLVM_LIBC_HDR_TYPES_STRUCT_SEMID_DS_H

View File

@@ -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 <sys/sem.h>
#endif // LLVM_LIBC_FULL_BUILD
#endif // LLVM_LIBC_HDR_TYPES_STRUCT_SEMINFO_H

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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

84
libc/include/sys/sem.yaml Normal file
View File

@@ -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

View File

@@ -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)

View File

@@ -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
)

View File

@@ -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
)

View File

@@ -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 <stdarg.h>
#include <sys/syscall.h>
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<unsigned long>(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<unsigned long>(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<unsigned long>(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<unsigned long>(va_arg(vargs, struct seminfo *));
va_end(vargs);
break;
}
// unrecognized flags
default:
libc_errno = EINVAL;
return -1;
}
int ret = LIBC_NAMESPACE::syscall_impl<int>(SYS_semctl, semid, semnum, cmd,
cmd_arg);
if (ret < 0) {
libc_errno = -ret;
return -1;
}
return ret;
}
} // namespace LIBC_NAMESPACE_DECL

View File

@@ -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 <sys/syscall.h>
namespace LIBC_NAMESPACE_DECL {
LLVM_LIBC_FUNCTION(int, semget, (key_t key, int nsems, int semflg)) {
int ret = LIBC_NAMESPACE::syscall_impl<int>(SYS_semget, key, nsems, semflg);
if (ret < 0) {
libc_errno = -ret;
return -1;
}
return ret;
}
} // namespace LIBC_NAMESPACE_DECL

View File

@@ -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 <sys/syscall.h>
namespace LIBC_NAMESPACE_DECL {
LLVM_LIBC_FUNCTION(int, semop, (int semid, struct sembuf *sops, size_t nsops)) {
int ret = LIBC_NAMESPACE::syscall_impl<int>(SYS_semop, semid, sops, nsops);
if (ret < 0) {
libc_errno = -ret;
return -1;
}
return ret;
}
} // namespace LIBC_NAMESPACE_DECL

20
libc/src/sys/sem/semctl.h Normal file
View File

@@ -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

21
libc/src/sys/sem/semget.h Normal file
View File

@@ -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

22
libc/src/sys/sem/semop.h Normal file
View File

@@ -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

View File

@@ -4,4 +4,5 @@ add_subdirectory(spawn)
add_subdirectory(stdio)
add_subdirectory(stdlib)
add_subdirectory(threads)
add_subdirectory(sys)
add_subdirectory(unistd)

View File

@@ -0,0 +1 @@
add_subdirectory(sem)

View File

@@ -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
)

View File

@@ -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 <errno.h>
#include <signal.h>
#include <sys/wait.h>
// 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;
}

View File

@@ -15,3 +15,4 @@ add_subdirectory(ipc)
add_subdirectory(uio)
add_subdirectory(time)
add_subdirectory(ioctl)
add_subdirectory(sem)

View File

@@ -0,0 +1,3 @@
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS})
add_subdirectory(${LIBC_TARGET_OS})
endif()

View File

@@ -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
)

View File

@@ -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));
}