1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238
|
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include "noeintr.h"
#include "warnp.h"
#include "ipc_sync.h"
/* Convention for read/write ends of a pipe. */
#define R 0
#define W 1
struct ipc_sync {
int fd[2];
};
/* Read and discard one byte from ${fd}, looping upon EINTR. */
static inline int
readbyte(int fd)
{
char dummy;
char done = 0;
/* Loop until done. */
do {
switch (read(fd, &dummy, 1)) {
case -1:
/* Anything other than EINTR is bad. */
if (errno != EINTR) {
warnp("read");
goto err0;
}
/* Otherwise, loop and read again. */
break;
case 0:
warn0("Unexpected EOF in pipe");
goto err0;
case 1:
/* Expected value; quit the loop. */
done = 1;
}
} while (!done);
/* Success! */
return (0);
err0:
/* Failure! */
return (-1);
}
/* Write one byte from ${fd}, looping upon EINTR. */
static inline int
writebyte(int fd)
{
char dummy = 0;
/* Do the write. */
if (noeintr_write(fd, &dummy, 1) == -1) {
warnp("write");
goto err0;
}
/* Success! */
return (0);
err0:
/* Failure! */
return (-1);
}
/* close(), but looping upon EINTR. */
static inline int
ipc_sync_close(int fd)
{
/* Loop until closed. */
while (close(fd)) {
if (errno == EINTR)
continue;
warnp("close");
goto err0;
}
/* Success! */
return (0);
err0:
/* Failure! */
return (-1);
}
/* Close the unneeded read or write end of the pipe. */
static int
ipc_sync_prep(struct ipc_sync * IS, int num)
{
/* Bail if there's nothing to do. */
if (IS->fd[num] == -1)
return (0);
/*
* Close the indicated end of pipe so that if the other process dies,
* we will notice the pipe being reset.
*/
if (ipc_sync_close(IS->fd[num]))
goto err0;
IS->fd[num] = -1;
/* Success! */
return (0);
err0:
/* Failure! */
return (-1);
}
/**
* ipc_sync_init(void):
* Initialize an inter-process synchronization barrier. This must be called
* before forking.
*/
struct ipc_sync *
ipc_sync_init(void)
{
struct ipc_sync * IS;
/* Allocate the cookie. */
if ((IS = malloc(sizeof(struct ipc_sync))) == NULL) {
warnp("malloc");
goto err0;
}
/* Set up synchronization. */
if (pipe(IS->fd)) {
warnp("pipe");
goto err1;
}
/* Success! */
return (IS);
err1:
free(IS);
err0:
/* Failure! */
return (NULL);
}
/**
* ipc_sync_wait(IS):
* Block until ipc_sync_signal() has been called with ${IS}.
*/
int
ipc_sync_wait(struct ipc_sync * IS)
{
/* Ensure that we've closed the write end. */
if (ipc_sync_prep(IS, W))
return (-1);
/* Read and discard a byte from the read end of the pipe. */
return (readbyte(IS->fd[R]));
}
/**
* ipc_sync_signal(IS):
* Indicate that ${IS} should no longer block.
*/
int
ipc_sync_signal(struct ipc_sync * IS)
{
/* Ensure that we've closed the read end. */
if (ipc_sync_prep(IS, R))
return (-1);
/* Write a byte to the write end of the pipe. */
return (writebyte(IS->fd[W]));
}
/**
* ipc_sync_wait_prep(IS):
* Perform any operation(s) that would speed up an upcoming call to
* ipc_sync_wait(). Calling this function is optional.
*/
int
ipc_sync_wait_prep(struct ipc_sync * IS)
{
/* Ensure that we've closed the write end. */
return (ipc_sync_prep(IS, W));
}
/**
* ipc_sync_signal_prep(IS):
* Perform any operation(s) that would speed up an upcoming call to
* ipc_sync_signal(). Calling this function is optional.
*/
int
ipc_sync_signal_prep(struct ipc_sync * IS)
{
/* Ensure that we've closed the read end. */
return (ipc_sync_prep(IS, R));
}
/**
* ipc_sync_done(IS):
* Free resources associated with ${IS}.
*/
int
ipc_sync_done(struct ipc_sync * IS)
{
/* Close any open file descriptors. */
if ((IS->fd[W] != -1) && ipc_sync_close(IS->fd[W]))
goto err1;
if ((IS->fd[R] != -1) && ipc_sync_close(IS->fd[R]))
goto err2;
/* Clean up. */
free(IS);
/* Success! */
return (0);
err2:
if (IS->fd[R] != -1)
ipc_sync_close(IS->fd[R]);
err1:
free(IS);
/* Failure! */
return (-1);
}
|