diff options
author | dzwdz | 2022-07-09 15:24:58 +0200 |
---|---|---|
committer | dzwdz | 2022-07-09 15:24:58 +0200 |
commit | dad8b261ac7898f4d8cf537ad288ad6a1a74d124 (patch) | |
tree | 610712b313032529195c3e5319ab45b1a7482c65 /src/init/tests/pipe.c | |
parent | 2e8e2dc1fb1aaefbe82cc4261c615428aa6250d5 (diff) |
syscalls/pipe: turn into a POSIX-style api with separate rw ends
Without separate read/write ends you can't tell when there are no more writers left if you have multiple readers. Consider this piece of code:
int fd = pipe();
fork(); // execution continues in 2 processes
while (read(fd, &some_buf, sizeof somebuf) >= 0) {
...
}
Once both processes call `read()`, it's obvious that no writes are possible - all the processes that hold a reference to the pipe are currently stuck on a `read()` call, so the kernel could just make it return an error in both. But, what then? It's still possible to write to the pipe, and you can't know if the other process will do that. Thus, if you don't want to miss any output, you have to keep reading the pipe. Forever. Both processes end up stuck.
Having separate read/write ends prevents that.
Diffstat (limited to 'src/init/tests/pipe.c')
-rw-r--r-- | src/init/tests/pipe.c | 61 |
1 files changed, 50 insertions, 11 deletions
diff --git a/src/init/tests/pipe.c b/src/init/tests/pipe.c index bbfe79a..697b9b1 100644 --- a/src/init/tests/pipe.c +++ b/src/init/tests/pipe.c @@ -6,36 +6,75 @@ static const char *pipe_msgs[2] = {"hello", "world"}; -static void test_pipe_child(handle_t pipe) { - int ret = _syscall_write(pipe, pipe_msgs[0], 5, -1); +static void test_pipe_child(handle_t ends[2]) { + int ret = _syscall_write(ends[1], pipe_msgs[0], 5, -1); assert(ret == 5); - ret = _syscall_write(pipe, pipe_msgs[1], 5, -1); + ret = _syscall_write(ends[1], pipe_msgs[1], 5, -1); assert(ret == 5); } -static void test_pipe_parent(handle_t pipe) { +static void test_pipe_parent(handle_t ends[2]) { char buf[16]; - int ret = _syscall_read(pipe, buf, 16, 0); + int ret = _syscall_read(ends[0], buf, 16, 0); assert(ret == 5); assert(!memcmp(buf, pipe_msgs[0], 5)); - _syscall_read(pipe, buf, 16, 0); + _syscall_read(ends[0], buf, 16, 0); assert(ret == 5); - assert(!memcmp(buf, pipe_msgs[1], 5)); // wrong compare for test + assert(!memcmp(buf, pipe_msgs[1], 5)); + + // TODO these calls fail for multiple reasons at once - split + assert(_syscall_read(ends[1], buf, 16, 0) < 0); + assert(_syscall_write(ends[0], buf, 16, 0) < 0); } void test_pipe(void) { - handle_t pipe = _syscall_pipe(0); - assert(pipe > 0); + handle_t ends[2]; + char buf[16]; + assert(_syscall_pipe(ends, 0) >= 0); + + if (!_syscall_fork(0, NULL)) { + test_pipe_child(ends); + _syscall_exit(0); + } else { + test_pipe_parent(ends); + _syscall_await(); + } + + _syscall_close(ends[0]); + _syscall_close(ends[1]); + + assert(_syscall_pipe(ends, 0) >= 0); + _syscall_close(ends[0]); + assert(_syscall_write(ends[1], buf, 16, 0) < 0); + _syscall_close(ends[1]); + + assert(_syscall_pipe(ends, 0) >= 0); + _syscall_close(ends[1]); + assert(_syscall_read(ends[0], buf, 16, 0) < 0); + _syscall_close(ends[0]); + + + assert(_syscall_pipe(ends, 0) >= 0); + if (!_syscall_fork(0, NULL)) { + _syscall_exit(0); + } else { + _syscall_close(ends[1]); + assert(_syscall_read(ends[0], buf, 16, 0) < 0); + _syscall_await(); + } + assert(_syscall_pipe(ends, 0) >= 0); if (!_syscall_fork(0, NULL)) { - test_pipe_child(pipe); _syscall_exit(0); } else { - test_pipe_parent(pipe); + _syscall_close(ends[1]); + assert(_syscall_write(ends[1], buf, 16, 0) < 0); _syscall_await(); } + // TODO detect when all processes that can read are stuck on writing to the pipe and vice verse // TODO kill process that's waiting on a pipe + // TODO queue } |