193 lines
4.7 KiB
C++
193 lines
4.7 KiB
C++
#define VIRTUAL_PIPE_FLAG_WRITE 1
|
|
#define VIRTUAL_PIPE_FLAG_NONBLOCK 2
|
|
|
|
struct virtual_pipe {
|
|
unsigned char content[VIRTUAL_PIPE_SIZE];
|
|
unsigned used;
|
|
unsigned n_rd;
|
|
unsigned n_wr;
|
|
};
|
|
|
|
static struct virtual_pipe *pipe_desc[FD_SETSIZE] = { NULL };
|
|
static unsigned pipe_flags[FD_SETSIZE] = { 0 };
|
|
|
|
static struct virtual_pipe *get_virtual_pipe(int fd)
|
|
{
|
|
struct virtual_pipe *desc;
|
|
if (fd >= FD_SETSIZE || !pipe_desc[fd]) return NULL;
|
|
pipe_lock();
|
|
desc = pipe_desc[fd];
|
|
if (!desc) {
|
|
pipe_unlock();
|
|
return NULL;
|
|
}
|
|
return desc;
|
|
}
|
|
|
|
static int vpipe_read(int fd, void *buf, size_t size)
|
|
{
|
|
int should_wake;
|
|
struct virtual_pipe *desc;
|
|
test_again:
|
|
if (!(desc = get_virtual_pipe(fd)))
|
|
return -2;
|
|
if (pipe_flags[fd] & VIRTUAL_PIPE_FLAG_WRITE) internal_error("vpipe_read: reading from write pipe descriptor");
|
|
if (!desc->used) {
|
|
if (!desc->n_wr) {
|
|
#ifdef TRACE_PIPES
|
|
fprintf(stderr, "read(%d) -> zero.", fd);
|
|
#endif
|
|
pipe_unlock();
|
|
return 0;
|
|
}
|
|
if (pipe_flags[fd] & VIRTUAL_PIPE_FLAG_NONBLOCK) {
|
|
#ifdef TRACE_PIPES
|
|
fprintf(stderr, "read(%d) -> wouldblock.", fd);
|
|
#endif
|
|
pipe_unlock();
|
|
errno = EWOULDBLOCK;
|
|
return -1;
|
|
}
|
|
#ifdef TRACE_PIPES
|
|
fprintf(stderr, "read(%d) -> sleep.", fd);
|
|
#endif
|
|
pipe_unlock_wait();
|
|
pipe_unlock();
|
|
goto test_again;
|
|
}
|
|
should_wake = desc->used == VIRTUAL_PIPE_SIZE;
|
|
if (size > desc->used)
|
|
size = desc->used;
|
|
memcpy(buf, desc->content, size);
|
|
memmove(desc->content, desc->content + size, desc->used -= size);
|
|
#ifdef TRACE_PIPES
|
|
fprintf(stderr, "read(%d) -> %d. (%d)", fd, size, should_wake);
|
|
#endif
|
|
pipe_unlock();
|
|
if (should_wake) pipe_wake();
|
|
return size;
|
|
}
|
|
|
|
static int vpipe_write(int fd, const void *buf, size_t size)
|
|
{
|
|
int should_wake;
|
|
struct virtual_pipe *desc;
|
|
test_again:
|
|
if (!(desc = get_virtual_pipe(fd)))
|
|
return -2;
|
|
if (!(pipe_flags[fd] & VIRTUAL_PIPE_FLAG_WRITE)) internal_error("vpipe_write: writing to read pipe descriptor");
|
|
if (!desc->n_rd) {
|
|
#ifdef TRACE_PIPES
|
|
fprintf(stderr, "write(%d) -> epipe.", fd);
|
|
#endif
|
|
pipe_unlock();
|
|
errno = EPIPE;
|
|
return -1;
|
|
}
|
|
if (desc->used == VIRTUAL_PIPE_SIZE) {
|
|
if (pipe_flags[fd] & VIRTUAL_PIPE_FLAG_NONBLOCK) {
|
|
#ifdef TRACE_PIPES
|
|
fprintf(stderr, "write(%d) -> wouldblock.", fd);
|
|
#endif
|
|
pipe_unlock();
|
|
errno = EWOULDBLOCK;
|
|
return -1;
|
|
}
|
|
#ifdef TRACE_PIPES
|
|
fprintf(stderr, "write(%d) -> sleep.", fd);
|
|
#endif
|
|
pipe_unlock_wait();
|
|
pipe_unlock();
|
|
goto test_again;
|
|
}
|
|
should_wake = !desc->used;
|
|
if (size > (VIRTUAL_PIPE_SIZE - desc->used))
|
|
size = VIRTUAL_PIPE_SIZE - desc->used;
|
|
memcpy(desc->content + desc->used, buf, size);
|
|
desc->used += size;
|
|
#ifdef TRACE_PIPES
|
|
fprintf(stderr, "write(%d) -> %d.", fd, size);
|
|
#endif
|
|
pipe_unlock();
|
|
if (should_wake) pipe_wake();
|
|
return size;
|
|
}
|
|
|
|
static int vpipe_close(int fd)
|
|
{
|
|
struct virtual_pipe *desc;
|
|
if (!(desc = get_virtual_pipe(fd)))
|
|
return -2;
|
|
if (!(pipe_flags[fd] & VIRTUAL_PIPE_FLAG_WRITE)) {
|
|
if (!desc->n_rd) internal_error("vpipe_close: read counter underflow");
|
|
desc->n_rd--;
|
|
} else {
|
|
if (!desc->n_wr) internal_error("vpipe_close: write counter underflow");
|
|
desc->n_wr--;
|
|
}
|
|
pipe_desc[fd] = NULL;
|
|
pipe_flags[fd] = 0;
|
|
pipe_unlock();
|
|
if (!desc->n_rd && !desc->n_wr)
|
|
free(desc);
|
|
return close(fd);
|
|
}
|
|
|
|
static int vpipe_create(int fd[2])
|
|
{
|
|
int rs;
|
|
struct virtual_pipe *desc;
|
|
EINTRLOOP(fd[0], open("/dev/null", O_RDONLY));
|
|
if (fd[0] == -1)
|
|
goto err0;
|
|
EINTRLOOP(fd[1], open("/dev/null", O_WRONLY));
|
|
if (fd[1] == -1)
|
|
goto err1;
|
|
if (fd[0] >= FD_SETSIZE || fd[1] >= FD_SETSIZE) {
|
|
errno = EMFILE;
|
|
goto err2;
|
|
}
|
|
desc = malloc(sizeof(struct virtual_pipe));
|
|
if (!desc) goto err2;
|
|
desc->used = 0;
|
|
desc->n_rd = 1;
|
|
desc->n_wr = 1;
|
|
pipe_lock();
|
|
if (pipe_desc[fd[0]] || pipe_flags[fd[0]] || pipe_desc[fd[1]] || pipe_flags[fd[0]])
|
|
internal_error("c_pipe: pipe handles %d, %d already used: %p, %d, %p, %d", fd[0], fd[1], pipe_desc[fd[0]], pipe_flags[fd[0]], pipe_desc[fd[1]], pipe_flags[fd[0]]);
|
|
pipe_desc[fd[0]] = desc;
|
|
pipe_flags[fd[0]] = 0;
|
|
pipe_desc[fd[1]] = desc;
|
|
pipe_flags[fd[1]] = VIRTUAL_PIPE_FLAG_WRITE;
|
|
#ifdef DOS
|
|
pipe_flags[fd[0]] |= VIRTUAL_PIPE_FLAG_NONBLOCK;
|
|
pipe_flags[fd[1]] |= VIRTUAL_PIPE_FLAG_NONBLOCK;
|
|
#endif
|
|
pipe_unlock();
|
|
return 0;
|
|
err2:
|
|
EINTRLOOP(rs, close(fd[1]));
|
|
err1:
|
|
EINTRLOOP(rs, close(fd[0]));
|
|
err0:
|
|
return -1;
|
|
}
|
|
|
|
static int vpipe_may_read(int fd)
|
|
{
|
|
if (pipe_flags[fd] & VIRTUAL_PIPE_FLAG_WRITE) internal_error("vpipe_may_read: selecting write descriptor %d for read", fd);
|
|
return pipe_desc[fd]->used || !pipe_desc[fd]->n_wr;
|
|
}
|
|
|
|
static int vpipe_may_write(int fd)
|
|
{
|
|
if (!(pipe_flags[fd] & VIRTUAL_PIPE_FLAG_WRITE)) internal_error("vpipe_may_write: selecting read descriptor %d for write", fd);
|
|
return pipe_desc[fd]->used != VIRTUAL_PIPE_SIZE || !pipe_desc[fd]->n_rd;
|
|
}
|
|
|
|
static void clear_inactive(fd_set *fs, int i)
|
|
{
|
|
if (!fs) return;
|
|
while (--i >= 0) FD_CLR(i, fs);
|
|
}
|
|
|