commit 35b47a26b3b1f9c98fbfffaa8cfe12a3facbca65
parent 67aeb09b77b99130ffcbe178fe5f9c7c1bf1816b
Author: Drew DeVault <sir@cmpwn.com>
Date: Wed, 20 Oct 2021 11:54:21 +0200
iobus: rig up eventfd support
Signed-off-by: Drew DeVault <sir@cmpwn.com>
Diffstat:
4 files changed, 51 insertions(+), 25 deletions(-)
diff --git a/cmd/iobus/main.ha b/cmd/iobus/main.ha
@@ -7,36 +7,41 @@ use strings;
use unix::poll;
export fn main() void = {
- // TODO: It would be nice to have two iobus instances here, one for us
- // (i.e. polling stdin) and one for the user. However, it does not seem
- // to be possible to poll an io_uring fd on Linux 5.10. I have written
- // to the io_uring mailing list for more information.
- let bus = iobus::new()!;
- defer iobus::destroy(bus);
-
- const in = iobus::register_file(bus, os::stdin_file);
- let poll = iobus::poll(bus, in, poll::event::POLLIN)!;
- iobus::enqueue(bus, &poll);
+ let mainbus = iobus::new()!;
+ defer iobus::destroy(mainbus);
+ let userbus = iobus::new()!;
+ defer iobus::destroy(userbus);
+
+ const userfd = iobus::busfile(userbus);
+ defer io::close(userfd);
+
+ const in = iobus::register_file(mainbus, os::stdin_file);
+ //const userfd = iobus::register_file(mainbus, userfd);
+
+ let pollin = iobus::poll(mainbus, in, poll::event::POLLIN)!;
+ let polluser = iobus::poll(mainbus, userfd, poll::event::POLLIN)!;
+
+ iobus::enqueue(mainbus, &pollin);
fmt::error("> ")!;
for (true) {
- const res = match (iobus::dispatch(bus)) {
- case err: iobus::error =>
- fmt::fatal("Error dispatching bus: {}",
- iobus::strerror(err));
- case res: iobus::result =>
- yield res;
- };
- defer iobus::done(bus, res);
+ const res = iobus::dispatch(mainbus)!;
+ defer iobus::done(mainbus, res);
- if (iobus::handleof(res) == &poll) {
- iobus::endpoll(bus, res)!;
- if (!readcmd(bus)) {
+ if (iobus::handleof(res) == &pollin) {
+ iobus::endpoll(mainbus, res)!;
+ if (!readcmd(mainbus)) {
fmt::errorln("exit")!;
break;
};
- poll = iobus::poll(bus, in, poll::event::POLLIN)!;
- iobus::enqueue(bus, &poll);
+ pollin = iobus::poll(mainbus, in, poll::event::POLLIN)!;
+ iobus::enqueue(mainbus, &pollin);
+ };
+ if (iobus::handleof(res) == &polluser) {
+ iobus::endpoll(mainbus, res)!;
+ iobus::busfile_drain(userfd);
+ polluser = iobus::poll(mainbus, userfd, poll::event::POLLIN)!;
+ iobus::enqueue(mainbus, &polluser);
};
};
};
diff --git a/iobus/io_uring/bus.ha b/iobus/io_uring/bus.ha
@@ -1,6 +1,7 @@
use errors;
use io;
use linux::io_uring;
+use rt;
def DEFAULT_RING_SIZE: u32 = 256;
@@ -22,9 +23,20 @@ export fn destroy(bus: *bus) void = {
io_uring::finish(&bus.uring);
};
-// Returns an [[io::file]] for the bus itself.
+// Returns an [[io::file]] for the bus itself. This file descriptor may be used
+// with [[poll]], [[unix::poll]], etc, to determine when [[dispatch]] will
+// return new I/O results. When this file is readable, the user must call
+// [[busfile_drain]] to acknowledge this condition.
export fn busfile(bus: *bus) io::file = {
- return bus.uring.fd;
+ let fd = rt::eventfd(0, 0)!;
+ io_uring::register_eventfd(&bus.uring, fd)!;
+ return fd;
+};
+
+// Drains a pending read from the [[busfile]].
+export fn busfile_drain(file: io::file) void = {
+ let count: [8]u8 = [0...];
+ io::read(file, count)!;
};
// Registers a set of I/O operations with the queue, without submitting them.
diff --git a/rt/+linux/syscalls.ha b/rt/+linux/syscalls.ha
@@ -460,6 +460,11 @@ export fn timerfd_create(clock_id: int, flags: int) (int | errno) = {
clock_id: u64, flags: u64))?: int;
};
+export fn eventfd(initval: uint, flags: int) (int | errno) = {
+ return wrap_return(syscall2(SYS_eventfd,
+ initval: u64, flags: u64))?: int;
+};
+
export fn timerfd_settime(
fd: int,
flags: int,
diff --git a/rt/+linux/types.ha b/rt/+linux/types.ha
@@ -379,6 +379,10 @@ export type epoll_event = struct {
@offset(4) data: epoll_data,
};
+export def EFD_CLOEXEC: int = O_CLOEXEC;
+export def EFD_NONBLOCK: int = O_NONBLOCK;
+export def EFD_SEMAPHORE: int = 1;
+
export def TFD_TIMER_ABSTIME: int = 1;
export def TFD_TIMER_CANCEL_ON_SET: int = 2;