mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-02-01 03:53:33 +00:00
217 lines
4.8 KiB
C
217 lines
4.8 KiB
C
/* 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);
|
|
}
|