From d2263febc737ba49efd61d4a2cbfd05ba379fc62 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Fri, 16 Dec 2022 12:11:18 +0100 Subject: [PATCH] Implement ev::readable, ev::writable --- ev/+linux/file.ha | 17 ++++++++++------- ev/+linux/io.ha | 2 -- ev/+linux/loop.ha | 22 ++++++++++++++-------- ev/+linux/poll.ha | 61 +++++++++++++++++++++++++++++++++++++++++++++++++++++ diff --git a/ev/+linux/file.ha b/ev/+linux/file.ha index 65d683ff8e174d835ba1a9f2e61719eb7e1c2b41..c85eb2ee26e22b3279d72ba9fc3f27a81e923629 100644 --- a/ev/+linux/file.ha +++ b/ev/+linux/file.ha @@ -4,11 +4,14 @@ use net; use rt; export type op = enum u64 { - NONE = 0, - READV = 1 << 0, - WRITEV = 1 << 1, - ACCEPT = 1 << 2, - CONNECT = 1 << 3, + NONE = 0, + READV = 1 << 0, + WRITEV = 1 << 1, + + READABLE = 1 << 16, + WRITABLE = 2 << 16, + ACCEPT = 3 << 16, + CONNECT = 4 << 16, }; export type fflags = enum uint { @@ -118,10 +121,10 @@ // Updates epoll events for a given file. For internal use. fn file_epoll_ctl(file: *file) void = { let events = rt::EPOLLONESHOT; - if (file.op & op::READV != 0) { + if (file.op & op::READV != 0 || file.op & op::READABLE != 0) { events |= rt::EPOLLIN | rt::EPOLLHUP; }; - if (file.op & op::WRITEV != 0) { + if (file.op & op::WRITEV != 0 || file.op & op::WRITABLE != 0) { events |= rt::EPOLLOUT | rt::EPOLLHUP; }; if (file.op & op::ACCEPT != 0) { diff --git a/ev/+linux/io.ha b/ev/+linux/io.ha index 63ad4e27a5498e9851546ad900abaa4f715804a7..75c46d61ca0f9aab987c345aa7959d7f99aa4b6d 100644 --- a/ev/+linux/io.ha +++ b/ev/+linux/io.ha @@ -72,8 +72,6 @@ file: *file, cb: *writecb, vec: io::vector... ) req = { - // XXX: Should we support both pending reads and writes at the same - // time? (yes) assert(file.op & op::WRITEV == 0); if (file.flags & fflags::BLOCKING != 0) { const r = io::writev(file.fd, vec...); diff --git a/ev/+linux/loop.ha b/ev/+linux/loop.ha index 9144a59c908bd109d36db358416c6b6b4a9da0eb..425356e2882fe49060994886920ddc45780f1d39 100644 --- a/ev/+linux/loop.ha +++ b/ev/+linux/loop.ha @@ -85,22 +85,28 @@ const file = ev.data.ptr: *file; if (ev.events == 0) { continue; }; - if (file.op == op::NONE) { - abort("Invalid pending operation"); - }; + const pending = file.op; if (ev.events & (rt::EPOLLIN | rt::EPOLLHUP) != 0 - && file.op & op::READV != 0) { + && pending & op::READV != 0) { readv_ready(file, ev); }; if (ev.events & (rt::EPOLLOUT | rt::EPOLLHUP) != 0 - && file.op & op::WRITEV != 0) { + && pending & op::WRITEV != 0) { writev_ready(file, ev); }; - if (file.op & op::ACCEPT != 0) { + switch (pending) { + case op::NONE => + abort("No operation pending for ready object"); + case op::READABLE => + readable_ready(file, ev); + case op::WRITABLE => + writable_ready(file, ev); + case op::ACCEPT => accept_ready(file, ev); - }; - if (file.op & op::CONNECT != 0) { + case op::CONNECT => connect_ready(file, ev); + case => + yield; }; }; diff --git a/ev/+linux/poll.ha b/ev/+linux/poll.ha new file mode 100644 index 0000000000000000000000000000000000000000..8f4de68a76836b5581d7132aa332c21dd121dd4e --- /dev/null +++ b/ev/+linux/poll.ha @@ -0,0 +1,61 @@ +use rt; + +// A callback for a [[readable]] operation. +export type readablecb = fn(file: *file) void; + +// Executes the callback when a given file is readable. Cannot be combined with +// [[read]] or [[readv]]. +export fn readable( + file: *file, + cb: *readablecb, +) req = { + assert(file.op & op::READABLE == 0 && file.op & op::READV == 0); + if (file.flags & fflags::BLOCKING != 0) { + cb(file); + return req { ... }; + }; + + file.op |= op::READABLE; + file.cb = cb; + file_epoll_ctl(file); + return req { ... }; +}; + +fn readable_ready(file: *file, ev: *rt::epoll_event) void = { + assert(file.op & op::READABLE != 0); + assert(file.cb != null); + const cb = file.cb: *readablecb; + file.op &= ~op::READABLE; + file_epoll_ctl(file); + cb(file); +}; + +// A callback for a [[writable]] operation. +export type writablecb = fn(file: *file) void; + +// Executes the callback when a given file is writable. Cannot be combined with +// [[write]] or [[writev]]. +export fn writable( + file: *file, + cb: *writablecb, +) req = { + assert(file.op & op::WRITABLE == 0 && file.op & op::WRITEV == 0); + if (file.flags & fflags::BLOCKING != 0) { + cb(file); + return req { ... }; + }; + + file.op |= op::WRITABLE; + file.cb = cb; + file_epoll_ctl(file); + return req { ... }; +}; + +fn writable_ready(file: *file, ev: *rt::epoll_event) void = { + assert(file.op & op::WRITABLE != 0); + assert(file.cb != null); + const cb = file.cb: *writablecb; + file.op &= ~op::WRITABLE; + file_epoll_ctl(file); + cb(file); +}; -- 2.48.1