Skip to content

Instantly share code, notes, and snippets.

@nickeldan
Last active June 24, 2023 17:45
Show Gist options
  • Save nickeldan/59f31837f1472ee492d69a81c595a0db to your computer and use it in GitHub Desktop.
Save nickeldan/59f31837f1472ee492d69a81c595a0db to your computer and use it in GitHub Desktop.
Parent/child synchronization
#include <errno.h>
#include <fcntl.h>
#include <poll.h>
#include <stdbool.h>
#include <unistd.h>
typedef struct syncChannel {
int fds[2];
} syncChannel;
int
syncChannelInit(syncChannel *event);
bool
syncChannelIsOpen(const syncChannel *event);
void
syncChannelShutdown(syncChannel *event);
pid_t
syncChannelFork(syncChannel *event);
void
syncChannelSignalParent(syncChannel *event);
void
syncChannelMessageParent(const syncChannel *event, const unsigned char *message, size_t message_size);
ssize_t
syncChannelWaitForChild(syncChannel *event, int timeout_ms, unsigned char *dest, size_t dest_size);
#ifndef _GNU_SOURCE
static int
setFlags(int fds[2])
{
int flags;
if ( (flags = fcntl(fds[0], F_GETFL)) < 0 || fcntl(fds[0], F_SETFL, flags | FD_NONBLOCK) < 0 ) {
goto error;
}
for (int k=0; k<2; k++) {
if ( (flags = fcntl(fds[k], F_GETFD)) < 0 || fcntl(fds[k], F_SETFD, flags | O_CLOEXEC) < 0 ) {
goto error;
}
}
return 0;
error:
for (int k; k<2; k++) {
close(fds[k]);
fds[k] = -1;
}
return -1;
}
#endif // _GNU_SOURCE
static void
closeReadEnd(syncChannel *event)
{
close(event->fds[0]);
event->fds[0] = -1;
}
int
syncChannelInit(syncChannel *event)
{
if ( !event ) {
errno = EINVAL;
return -1;
}
#ifdef _GNU_SOURCE
return pipe2(event->fds, O_CLOEXEC | O_NONBLOCK);
#else
if ( pipe(event->fds) != 0 ) {
return -1;
}
return setFlags(event->fds);
#endif
}
bool
syncChannelIsOpen(const syncChannel *event)
{
return event && event->fds[0] >= 0;
}
void
syncChannelShutdown(syncChannel *event)
{
if ( !event ) {
return;
}
for (int k=0; k<2; k++) {
close(event->fds[k]);
event->fds[k] = -1;
}
}
pid_t
syncChannelFork(syncChannel *event)
{
pid_t child;
if ( !event ) {
errno = EINVAL;
return -1;
}
child = fork();
switch ( child ) {
case -1: break;
case 0:
close(event->fds[0]);
event->fds[0] = -1;
break;
default:
close(event->fds[1]);
event->fds[1] = -1;
break;
}
return child;
}
void
syncChannelSignalParent(syncChannel *event)
{
if ( !event ) {
return;
}
close(event->fds[1]);
event->fds[1] = -1;
}
void
syncChannelMessageParent(const syncChannel *event, const unsigned char *message, size_t message_size)
{
if ( !event || !message || message_size == 0 ) {
return;
}
if ( write(event->fds[1], message, message_size) < 0 ) {}
}
ssize_t
syncChannelWaitForChild(syncChannel *event, int timeout_ms, unsigned char *dest, size_t dest_size)
{
struct pollfd poller = {.events = POLLIN};
if ( !event ) {
errno = EINVAL;
return -1;
}
poller.fd = event->fds[0];
switch ( poll(&poller, 1, timeout_ms) ) {
case -1: return -1;
case 0: return 0;
default: break;
}
if ( dest && dest_size > 0 ) {
ssize_t recvd;
recvd = read(event->fds[0], dest, dest_size);
if ( recvd == 0 ) {
closeReadEnd(event);
}
return recvd;
}
if ( poller.revents & POLLHUP )
closeReadEnd(event);
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment