Lindenii Project Forge
Login

hare-aio

Asynchronous I/O event loops for Hare

Warning: Due to various recent migrations, viewing non-HEAD refs may be broken.

/linux/io_uring/queue.ha (raw)

// SPDX-License-Identifier: MPL-2.0
// (c) 2021 Alexey Yerin <yyp@disroot.org>
// (c) 2021 Drew DeVault <sir@cmpwn.com>
// (c) 2021 Eyal Sawady <ecs@d2evs.net>
// (c) 2025 Runxi Yu <me@runxiyu.org>
use errors;
use rt;

// TODO: Atomics

// Returns the next available [[sqe]] for this [[io_uring]], or null if the
// queue is full.
export fn ring_get_sqe(ring: *io_uring) nullable *sqe = {
	const sq = &ring.sq;
	const head = *sq.khead, next = sq.sqe_tail + 1;
	if (next - head <= *sq.kring_entries) {
		let sqe = &sq.sqes[sq.sqe_tail & *sq.kring_mask];
		sq.sqe_tail = next;
		return sqe;
	};
	return null;
};

fn needs_enter(ring: *io_uring, flags: *enter_flags) bool = {
	if (ring.flags & ring_setup_flags::IOPOLL != ring_setup_flags::IOPOLL) {
		return true;
	};

	if (*ring.sq.kflags & sq_flags::NEED_WAKEUP == sq_flags::NEED_WAKEUP) {
		*flags |= enter_flags::SQ_WAKEUP;
		return true;
	};

	return false;
};

fn needs_flush(ring: *io_uring) bool =
	*ring.sq.kflags & sq_flags::CQ_OVERFLOW == sq_flags::CQ_OVERFLOW;

// Submits queued I/O asynchronously. Returns the number of submissions accepted
// by the kernel.
export fn ring_submit(ring: *io_uring) (uint | error) =
	do_submit(ring, flush_sq(ring), 0u);

// Submits queued I/O asynchronously and blocks until at least "wait" events are
// complete. If ring_setup_flags::IOPOLL was configured for this ring, the meaning of
// the "wait" parameter is different: a non-zero value will block until at least
// one event is completed.
//
// Returns the number of submissions accepted by the kernel.
export fn ring_submit_and_wait(ring: *io_uring, wait: uint) (uint | error) =
	do_submit(ring, flush_sq(ring), wait);

fn flush_sq(ring: *io_uring) uint = {
	let sq = &ring.sq;
	let ktail = *sq.ktail;
	const mask = *sq.kring_mask;

	if (sq.sqe_head == sq.sqe_tail) {
		return ktail - *sq.khead;
	};

	for (let n = sq.sqe_tail - sq.sqe_head; n > 0; n -= 1u) {
		sq.array[ktail & mask] = sq.sqe_head & mask;
		ktail += 1u;
		sq.sqe_head += 1u;
	};

	*sq.ktail = ktail;
	return ktail - *sq.khead;
};

fn do_submit(
	ring: *io_uring,
	submitted: uint,
	wait: uint,
) (uint | error) = {
	let flags = enter_flags::GETEVENTS;
	if (needs_enter(ring, &flags) || wait != 0) {
		match (rt::io_uring_enter(ring.fd,
				submitted, wait, flags, null)) {
		case let err: rt::errno =>
			return errors::errno(err);
		case let n: uint =>
			return n;
		};
	} else {
		return submitted;
	};
};