~sircmpwn/hare unlisted

a9f0ded8a83626ba2c57862eafcb97e4cf9d4c6f — Drew DeVault 30 days ago b349dad
linux::io_uring: add result and get_user CQE funcs

Also moves the CQE code into its own file.

Signed-off-by: Drew DeVault <sir@cmpwn.com>
5 files changed, 95 insertions(+), 81 deletions(-)

A linux/io_uring/cqe.ha
M linux/io_uring/queue.ha
M linux/io_uring/sqe.ha
M scripts/gen-stdlib
M stdlib.mk
A linux/io_uring/cqe.ha => linux/io_uring/cqe.ha +90 -0
@@ 0,0 1,90 @@
use errors;
use rt;

// Advances the completion queue by N items.
export fn cq_advance(ring: *io_uring, n: uint) void = {
	*ring.cq.khead = *ring.cq.khead + n;
};

// Call after processing a [[cqe]]. The cqe is returned to the pool and cannot
// be used by the application again.
export fn cqe_seen(ring: *io_uring, cqe: *cqe) void = cq_advance(ring, 1);

// Waits until a CQE is available, then returns it. The caller must pass the
// returned CQE to [[cqe_seen]] to advance the queue.
export fn wait(ring: *io_uring) (*cqe | error) = {
	return match (get_cqe(ring, 0, 1)) {
		err: error => err,
		cq: nullable *cqe => {
			assert(cq != null); // XXX: Correct?
			cq: *cqe;
		},
	};
};

// Peeks the next CQE from the queue and returns it, or null if none are
// pending. The caller must pass the returned CQE to [[cqe_seen]] to advance the
// queue.
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;

// Gets the user data field of a [[cqe]]. See [[set_user]] for the corresponding
// SQE function.
export fn get_user(cqe: *cqe) nullable *void =
	cqe.user_data: uintptr: nullable *void;

fn peek_cqe(ring: *io_uring) (nullable *cqe, uint) = {
	let head = *ring.cq.khead;
	let tail = *ring.cq.ktail;
	let mask = *ring.cq.kring_mask;
	let avail = tail - head;
	if (avail == 0) {
		return (null, 0);
	};
	return (&ring.cq.cqes[head & mask], avail);
};

fn get_cqe(
	ring: *io_uring,
	submit: uint,
	wait: uint,
) (nullable *cqe | error) = {
	let cq: nullable *cqe = null;
	for (cq == null) {
		let enter = false, overflow = false;
		let flags: enter_flags = 0;

		// TODO: tuple destructuring
		let tup = peek_cqe(ring);
		let avail = tup.1;
		cq = tup.0;

		if (cq == null && wait == 0 && submit == 0) {
			if (!needs_flush(ring)) {
				return null;
			};
			overflow = true;
		};
		if (wait > avail || overflow) {
			flags |= enter_flags::GETEVENTS;
			enter = true;
		};
		if (submit > 0) {
			needs_enter(ring, &flags);
			enter = true;
		};
		if (!enter) {
			break;
		};

		match (rt::io_uring_enter(ring.fd,
				submit, wait, flags: uint, null)) {
			err: rt::errno => return errors::errno(err),
			n: uint => submit -= n,
		};
	};
	return cq;
};

M linux/io_uring/queue.ha => linux/io_uring/queue.ha +0 -79
@@ 88,82 88,3 @@ fn do_submit(
		return submitted;
	};
};

// Advances the completion queue by N items.
export fn cq_advance(ring: *io_uring, n: uint) void = {
	*ring.cq.khead = *ring.cq.khead + n;
};

// Call after processing a [[cqe]]. The cqe is returned to the pool and cannot
// be used by the application again.
export fn cqe_seen(ring: *io_uring, cqe: *cqe) void = cq_advance(ring, 1);

// Waits until a CQE is available, then returns it. The caller must pass the
// returned CQE to [[cqe_seen]] to advance the queue.
export fn wait(ring: *io_uring) (*cqe | error) = {
	return match (get_cqe(ring, 0, 1)) {
		err: error => err,
		cq: nullable *cqe => {
			assert(cq != null); // XXX: Correct?
			cq: *cqe;
		},
	};
};

// Peeks the next CQE from the queue and returns it, or null if none are
// pending. The caller must pass the returned CQE to [[cqe_seen]] to advance the
// queue.
export fn peek(ring: *io_uring) (nullable *cqe | error) = get_cqe(ring, 0, 0);

fn peek_cqe(ring: *io_uring) (nullable *cqe, uint) = {
	let head = *ring.cq.khead;
	let tail = *ring.cq.ktail;
	let mask = *ring.cq.kring_mask;
	let avail = tail - head;
	if (avail == 0) {
		return (null, 0);
	};
	return (&ring.cq.cqes[head & mask], avail);
};

fn get_cqe(
	ring: *io_uring,
	submit: uint,
	wait: uint,
) (nullable *cqe | error) = {
	let cq: nullable *cqe = null;
	for (cq == null) {
		let enter = false, overflow = false;
		let flags: enter_flags = 0;

		// TODO: tuple destructuring
		let tup = peek_cqe(ring);
		let avail = tup.1;
		cq = tup.0;

		if (cq == null && wait == 0 && submit == 0) {
			if (!needs_flush(ring)) {
				return null;
			};
			overflow = true;
		};
		if (wait > avail || overflow) {
			flags |= enter_flags::GETEVENTS;
			enter = true;
		};
		if (submit > 0) {
			needs_enter(ring, &flags);
			enter = true;
		};
		if (!enter) {
			break;
		};

		match (rt::io_uring_enter(ring.fd,
				submit, wait, flags: uint, null)) {
			err: rt::errno => return errors::errno(err),
			n: uint => submit -= n,
		};
	};
	return cq;
};

M linux/io_uring/sqe.ha => linux/io_uring/sqe.ha +2 -2
@@ 28,7 28,7 @@ fn preprw(

// Sets the user data field of an [[sqe]]. This is copied to the [[cqe]] and can
// be used to correlate a completion event with the original SQE.
export fn setuser(sqe: *sqe, user_data: *void) void = {
export fn set_user(sqe: *sqe, user_data: *void) void = {
	static assert(size(uintptr) <= size(u64));
	sqe.user_data = user_data: uintptr: u64;
};


@@ 151,5 151,5 @@ export fn poll_add(
// Removes an existing poll request by matching the SQE's user_data field.
export fn poll_remove(sqe: *sqe, user_data: *void, flags: sqe_flags...) void = {
	preprw(sqe, op::POLL_REMOVE, -1, null, 0, 0, flags...);
	setuser(sqe, user_data);
	set_user(sqe, user_data);
};

M scripts/gen-stdlib => scripts/gen-stdlib +1 -0
@@ 519,6 519,7 @@ linux() {

linux_io_uring() {
	gen_srcs linux::io_uring \
		cqe.ha \
		queue.ha \
		register.ha \
		setup.ha \

M stdlib.mk => stdlib.mk +2 -0
@@ 758,6 758,7 @@ $(HARECACHE)/linux/linux.ssa: $(stdlib_linux_srcs) $(stdlib_rt) $(stdlib_format_

# linux::io_uring
stdlib_linux_io_uring_srcs= \
	$(STDLIB)/linux/io_uring/cqe.ha \
	$(STDLIB)/linux/io_uring/queue.ha \
	$(STDLIB)/linux/io_uring/register.ha \
	$(STDLIB)/linux/io_uring/setup.ha \


@@ 1811,6 1812,7 @@ $(TESTCACHE)/linux/linux.ssa: $(testlib_linux_srcs) $(testlib_rt) $(testlib_form

# linux::io_uring
testlib_linux_io_uring_srcs= \
	$(STDLIB)/linux/io_uring/cqe.ha \
	$(STDLIB)/linux/io_uring/queue.ha \
	$(STDLIB)/linux/io_uring/register.ha \
	$(STDLIB)/linux/io_uring/setup.ha \