cosmopolitan/test/libc/calls/shm_open_test.c

218 lines
4.8 KiB
C
Raw Normal View History

2023-11-01 06:57:52 +00:00
/* pshm_ucase_bounce.c
Adapted from SHM_OPEN(3) in Linux Programmer's Manual
Licensed under GNU General Public License v2 or later */
#include "libc/atomic.h"
#include "libc/calls/calls.h"
#include "libc/calls/struct/sigaction.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/runtime/runtime.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/map.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/prot.h"
#include "libc/sysv/consts/s.h"
#include "libc/sysv/consts/sig.h"
#include "libc/thread/semaphore.h"
#define SHM_PATH "/fc7261622dd420d8"
#define STRING_SEND "hello"
#define STRING_RECV "HELLO"
struct shmbuf {
sem_t sem1; /* POSIX unnamed semaphore */
sem_t sem2; /* POSIX unnamed semaphore */
size_t cnt; /* Number of bytes used in 'buf' */
char buf[256]; /* Data being transferred */
};
atomic_bool *ready;
wontreturn void Bouncer(void) {
/* Create shared memory object and set its size to the size
of our structure. */
int fd = shm_open(SHM_PATH, O_CREAT | O_EXCL | O_RDWR, S_IRUSR | S_IWUSR);
if (fd == -1) {
perror("shm_open(bouncer)");
exit(1);
}
if (ftruncate(fd, sizeof(struct shmbuf)) == -1) {
perror("ftruncate");
exit(1);
}
/* Map the object into the caller's address space. */
struct shmbuf *shmp =
mmap(NULL, sizeof(*shmp), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (shmp == MAP_FAILED) {
perror("mmap");
exit(1);
}
/* Initialize semaphores as process-shared, with value 0. */
if (sem_init(&shmp->sem1, 1, 0) == -1) {
perror("sem_init-sem1");
exit(1);
}
if (sem_init(&shmp->sem2, 1, 0) == -1) {
perror("sem_init-sem2");
exit(1);
}
/* Let other process know it's ok to open. */
*ready = true;
/* Wait for 'sem1' to be posted by peer before touching
shared memory. */
if (sem_wait(&shmp->sem1) == -1) {
perror("sem_wait");
exit(1);
}
/* Convert data in shared memory into upper case. */
for (int j = 0; j < shmp->cnt; j++) {
shmp->buf[j] = toupper((unsigned char)shmp->buf[j]);
}
/* Post 'sem2' to tell the peer that it can now
access the modified data in shared memory. */
if (sem_post(&shmp->sem2) == -1) {
perror("sem_post");
exit(1);
}
exit(0);
}
wontreturn void Sender(void) {
/* Wait for file to exist. */
while (!*ready) donothing;
/* Open the existing shared memory object and map it
into the caller's address space. */
int fd = shm_open(SHM_PATH, O_RDWR, 0);
if (fd == -1) {
perror("shm_open(sender)");
exit(1);
}
struct shmbuf *shmp =
mmap(NULL, sizeof(*shmp), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (shmp == MAP_FAILED) {
perror("mmap");
exit(1);
}
/* Copy data into the shared memory object. */
shmp->cnt = strlen(STRING_SEND);
strcpy(shmp->buf, STRING_SEND);
/* Tell peer that it can now access shared memory. */
if (sem_post(&shmp->sem1) == -1) {
perror("sem_post");
exit(1);
}
/* Wait until peer says that it has finished accessing
the shared memory. */
if (sem_wait(&shmp->sem2) == -1) {
perror("sem_wait");
exit(1);
}
/* Write modified data in shared memory to standard output. */
if (strcmp(shmp->buf, STRING_RECV)) {
tinyprint(2, program_invocation_name,
": received wrong string: ", shmp->buf, "\n", NULL);
exit(1);
}
/* Unlink the shared memory object. Even if the peer process
is still using the object, this is okay. The object will
be removed only after all open references are closed. */
if (shm_unlink(SHM_PATH)) {
if (IsWindows() && errno == EACCES) {
// TODO(jart): Make unlink() work better on Windows.
} else {
perror("shm_unlink");
exit(1);
}
}
exit(0);
}
int pid1;
int pid2;
void OnExit(void) {
kill(pid1, SIGKILL);
kill(pid2, SIGKILL);
shm_unlink(SHM_PATH);
}
void OnTimeout(int sig) {
tinyprint(2, program_invocation_name, ": test timed out!\n", NULL);
exit(1);
}
int main(int argc, char *argv[]) {
// create synchronization object
ready = _mapshared(1);
// create bouncer
pid1 = fork();
if (pid1 == -1) {
perror("fork");
exit(1);
}
if (!pid1) {
Bouncer();
}
// create sender
pid2 = fork();
if (pid1 == -1) {
perror("fork");
kill(pid1, SIGKILL);
exit(1);
}
if (!pid2) {
Sender();
}
// set limit
atexit(OnExit);
signal(SIGALRM, OnTimeout);
alarm(1);
// wait for children
for (;;) {
int child, status;
child = wait(&status);
if (child == -1) {
if (errno == ECHILD) {
break;
}
perror("wait");
exit(1);
}
if (status) {
if (WIFEXITED(status)) {
exit(WEXITSTATUS(status));
} else {
raise(WTERMSIG(status));
exit(128 + WTERMSIG(status));
}
}
}
// report success
exit(0);
}