From e78fbb7a8b5bebf518faced27372aa27f692395f Mon Sep 17 00:00:00 2001 From: Drew DeVault <sir@cmpwn.com> Date: Wed, 30 Nov 2022 15:28:36 +0100 Subject: [PATCH] ev: implement blocking I/O fallback --- ev/+linux/README | 6 ++++++ ev/+linux/file.ha | 24 +++++++++++++++++++----- ev/+linux/io.ha | 12 ++++++++++++ diff --git a/ev/+linux/README b/ev/+linux/README new file mode 100644 index 0000000000000000000000000000000000000000..8b6ec3d48ff3a3e523ab05e844d20ee9d4e06a55 --- /dev/null +++ b/ev/+linux/README @@ -0,0 +1,6 @@ +The ev module provides an event loop. Use [[newloop]] to create an event loop, +and [[finish]] to finalize it. You may add external I/O sources via functions +like [[register]] and [[unregister]]. + +On Linux, ev is implemented with epoll. Note that, on Linux, I/O on regular +files is always blocking. diff --git a/ev/+linux/file.ha b/ev/+linux/file.ha index 5337e503b0c498f878fcf8f33a9e7b727d27df31..95afaf8cee545ded04dcbf885ae80c09dea6dc76 100644 --- a/ev/+linux/file.ha +++ b/ev/+linux/file.ha @@ -8,10 +8,15 @@ READV, WRITEV, }; +export type fflags = enum uint { + BLOCKING = 1 << 31, +}; + export type file = struct { fd: io::file, ev: *loop, - // Pending operation on this file object + + flags: fflags, op: op, cb: nullable *void, user: nullable *void, @@ -44,6 +49,12 @@ match (rt::epoll_ctl(loop.fd, rt::EPOLL_CTL_ADD, fd, &ev)) { case void => yield; case let err: rt::errno => + if (err: int == rt::EPERM) { + // epoll(2) does not support regular files, use blocking + // I/O instead + file.flags = fflags::BLOCKING; + return file; + }; return errors::errno(err); }; @@ -54,10 +65,13 @@ // Unregisters a file object with an event loop and frees resources associated // with it. Does not close the underlying file descriptor. export fn unregister(file: *file) void = { const loop = file.ev; - // The only way that this could fail is in the event of a use-after-free - // or if the user fucks around and constructs a custom [[file]] which - // was never registered, so assert on error. - rt::epoll_ctl(loop.fd, rt::EPOLL_CTL_DEL, file.fd, null)!; + if (file.flags & fflags::BLOCKING == 0) { + // The only way that this could fail is in the event of a + // use-after-free or if the user fucks around and constructs a + // custom [[file]] which was never registered, so assert on + // error. + rt::epoll_ctl(loop.fd, rt::EPOLL_CTL_DEL, file.fd, null)!; + }; free(file); }; diff --git a/ev/+linux/io.ha b/ev/+linux/io.ha index 9572c300043d7c99c8608418c12975459d1cc18f..5b543fde7eafd25e260711d14176a6abc08ec42d 100644 --- a/ev/+linux/io.ha +++ b/ev/+linux/io.ha @@ -23,6 +23,12 @@ cb: *readcb, vec: io::vector... ) req = { assert(file.op == op::NONE); + if (file.flags & fflags::BLOCKING != 0) { + const r = io::readv(file.fd, vec...); + cb(file, r); + return req { ... }; + }; + file.op = op::READV; file.cb = cb; file.vec = vec; @@ -69,6 +75,12 @@ ) req = { // XXX: Should we support both pending reads and writes at the same // time? (yes) assert(file.op == op::NONE); + if (file.flags & fflags::BLOCKING != 0) { + const r = io::writev(file.fd, vec...); + cb(file, r); + return req { ... }; + }; + file.op = op::WRITEV; file.cb = cb; file.vec = vec; -- 2.48.1