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:
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,
};