hare

The Hare programming language
git clone https://git.torresjrjr.com/hare.git
Log | Files | Refs | README | LICENSE

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:
Mcmd/iobus/main.ha | 51++++++++++++++++++++++++++++-----------------------
Miobus/io_uring/bus.ha | 16++++++++++++++--
Mrt/+linux/syscalls.ha | 5+++++
Mrt/+linux/types.ha | 4++++
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;