hare

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

commit 34cdb4d7e0f6785d9925fa3efb4d6441a53196ac
parent 04a6bcacde40968ae0d967d876602a74c1c5c9cb
Author: Drew DeVault <sir@cmpwn.com>
Date:   Wed, 19 May 2021 15:37:56 -0400

linux::io_uring: timeout operations

Signed-off-by: Drew DeVault <sir@cmpwn.com>

Diffstat:
Merrors/common.ha | 8++++++++
Merrors/string.ha | 2++
Mlinux/io_uring/cqe.ha | 12++++++++++--
Mlinux/io_uring/sqe.ha | 46+++++++++++++++++++++++++++++++++++++++++++++-
Mlinux/io_uring/uring.ha | 8+++++++-
Mtime/+linux/functions.ha | 12+++++++++---
6 files changed, 81 insertions(+), 7 deletions(-)

diff --git a/errors/common.ha b/errors/common.ha @@ -19,6 +19,12 @@ export type overflow = !void; // The requested operation is not supported. export type unsupported = !void; +// The requested operation timed out. +export type timeout = !void; + +// The requested operation was cancelled. +export type cancelled = !void; + // A tagged union of all error types. export type error = !( busy | @@ -28,5 +34,7 @@ export type error = !( noentry | overflow | unsupported | + timeout | + cancelled | opaque ); diff --git a/errors/string.ha b/errors/string.ha @@ -16,5 +16,7 @@ export fn strerror(err: error) const str = match (err) { noentry => "An entry was requested which does not exist", overflow => "The requested operation caused a numeric overflow condition", unsupported => "The requested operation is not supported", + timeout => "The requested operation timed out", + cancelled => "The requested operation was cancelled", op: opaque => op.strerror(&op.data), }; diff --git a/linux/io_uring/cqe.ha b/linux/io_uring/cqe.ha @@ -28,8 +28,16 @@ export fn wait(ring: *io_uring) (*cqe | error) = { export fn peek(ring: *io_uring) (nullable *cqe | error) = get_cqe(ring, 0, 0); // Returns the result of a [[cqe]], or an error if unsuccessful. -export fn result(cqe: *cqe) (int | error) = - if (cqe.res < 0) rt::wrap_errno(-cqe.res) else cqe.res; +export fn result(cqe: *cqe) (int | error) = { + // TODO: Flesh out more errors + return if (cqe.res < 0) switch (-cqe.res) { + rt::ETIME => errors::timeout, + rt::ECANCELED => errors::cancelled, + * => errors::errno(rt::wrap_errno(-cqe.res)), + } else { + cqe.res; + }; +}; // Gets the user data field of a [[cqe]]. See [[set_user]] for the corresponding // SQE function. diff --git a/linux/io_uring/sqe.ha b/linux/io_uring/sqe.ha @@ -148,7 +148,8 @@ export fn poll_add( sqe.poll32_events = poll_mask: u32; }; -// Removes an existing poll request by matching the SQE's user_data field. +// Removes an existing poll request by matching the SQE's user_data field. See +// [[setuser]]. export fn poll_remove(sqe: *sqe, user_data: *void, flags: sqe_flags...) void = { preprw(sqe, op::POLL_REMOVE, -1, null, 0, 0, flags...); set_user(sqe, user_data); @@ -209,3 +210,46 @@ export fn recv( preprw(sqe, op::RECV, fd, buf, count: u32, 0, flags...); sqe.msg_flags = recv_flags; }; + +// Prepares a timeout operation for an [[sqe]]. "ts" should be a timespec +// describing the desired timeout, and "events" may optionally be used to define +// a number of completion events to wake after (or zero to wake only after the +// timeout expires). The caller must call [[setuser]] to provide a user data +// field in order to use [[timeout_remove]] to cancel this timeout later. +export fn timeout( + sqe: *sqe, + ts: *rt::timespec, + events: uint, + to_flags: timeout_flags, + flags: sqe_flags... +) void = { + preprw(sqe, op::TIMEOUT, 0, ts, 1, events, flags...); + sqe.timeout_flags = to_flags; +}; + +// Removes an existing timeout request by matching the SQE's user_data field. +// See [[setuser]]. +export fn timeout_remove( + sqe: *sqe, + user_data: *void, + to_flags: timeout_flags, + flags: sqe_flags... +) void = { + preprw(sqe, op::TIMEOUT_REMOVE, 0, user_data, 0, 0, flags...); + sqe.timeout_flags = to_flags; +}; + +// Updates an existing timeout request by matching the SQE's user_data field. +// See [[setuser]]. +export fn timeout_update( + sqe: *sqe, + user_data: *void, + ts: *rt::timespec, + events: uint, + to_flags: timeout_flags, + flags: sqe_flags... +) void = { + preprw(sqe, op::TIMEOUT_REMOVE, 0, user_data, 0, events, flags...); + sqe.timeout_flags = to_flags | timeout_flags::UPDATE; + sqe.addr2 = ts; +}; diff --git a/linux/io_uring/uring.ha b/linux/io_uring/uring.ha @@ -1,7 +1,7 @@ use errors; // All errors which may be returned by this module. -export type error = errors::opaque; +export type error = (errors::timeout | errors::cancelled | errors::opaque); // Converts an [[error]] into a human-readable string. export fn strerror(err: error) const str = { @@ -77,7 +77,13 @@ export type fsync_flags = enum u32 { // Flags for a timeout operation. export type timeout_flags = enum u32 { + // If set, the timeout will be "absolute", waiting until CLOCK_MONOTONIC + // reaches the time defined by the timespec. If unset, it will be + // interpted as a duration relative to the I/O submission. ABS = 1 << 0, + // When combined with [[op::TIMEOUT_REMOVE]], causes the submission to + // update the timer rather than remove it. + UPDATE = 1 << 1, }; // Flags for a splice operation. diff --git a/time/+linux/functions.ha b/time/+linux/functions.ha @@ -1,17 +1,23 @@ use rt; use linux::vdso; -fn duration_to_timespec(n: duration, ts: *rt::timespec) void = { +// Converts a [[duration]] to an [[rt::timespec]]. This function is +// non-portable. +export fn duration_to_timespec(n: duration, ts: *rt::timespec) void = { ts.tv_sec = n / SECOND; ts.tv_nsec = n % SECOND; }; -fn instant_to_timespec(t: instant, ts: *rt::timespec) void = { +// Converts an [[instant]] to an [[rt::timespec]]. This function is +// non-portable. +export fn instant_to_timespec(t: instant, ts: *rt::timespec) void = { ts.tv_sec = t.sec; ts.tv_nsec = t.nsec; }; -fn timespec_to_instant(ts: rt::timespec) instant = instant { +// Converts a [[rt::timespec]] to an [[rt::instant]]. This function is +// non-portable. +export fn timespec_to_instant(ts: rt::timespec) instant = instant { sec = ts.tv_sec, nsec = ts.tv_nsec, };