From 6474ae30e5c9e1efa161a0d8c306a24ef1a55ed6 Mon Sep 17 00:00:00 2001
From: Drew DeVault <sir@cmpwn.com>
Date: Wed, 30 Nov 2022 13:04:59 +0100
Subject: [PATCH] ev::read, ev::write: initial support

---
 ev/+linux/file.ha  | 31 +++++++++++++++++++++++++++++++
 ev/+linux/io.ha    | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 ev/+linux/types.ha | 16 ++--------------

diff --git a/ev/+linux/file.ha b/ev/+linux/file.ha
index 1844a2af8c903ad594472f769b3d25ea3c2b5dda..a4ef4e35ff5dae63e340ba4ceb9678b8c790ec9f 100644
--- a/ev/+linux/file.ha
+++ b/ev/+linux/file.ha
@@ -2,8 +2,24 @@ use errors;
 use io;
 use rt;
 
+export type op = enum {
+	NONE,
+	READV,
+	WRITEV,
+};
+
 export type file = struct {
 	fd: io::file,
+	ev: *loop,
+	// Pending operation on this file object
+	op: op,
+	callback: nullable *void,
+
+	// Operation-specific data
+	vbuf: rt::iovec,
+	union {
+		vec: []rt::iovec,
+	},
 };
 
 // Registers a file descriptor with an event loop.
@@ -13,6 +29,9 @@ 	fd: io::file,
 ) (*file | errors::error) = {
 	const file = alloc(file {
 		fd = fd,
+		ev = loop,
+		op = op::NONE,
+		...
 	});
 
 	let ev = rt::epoll_event {
@@ -39,3 +58,15 @@ 	// was never registered, so assert on error.
 	rt::epoll_ctl(loop.fd, rt::EPOLL_CTL_DEL, file.fd, null)!;
 	free(file);
 };
+
+// Modifies the epoll events for a given file. For internal use.
+fn filemod(file: *file, events: u32) void = {
+	let ev = rt::epoll_event {
+		events = events,
+		...
+	};
+	ev.data.ptr = file;
+	// This can only fail under conditions associated with EPOLLEXCLUSIVE,
+	// which we do not support.
+	rt::epoll_ctl(file.ev.fd, rt::EPOLL_CTL_MOD, file.fd, &ev)!;
+};
diff --git a/ev/+linux/io.ha b/ev/+linux/io.ha
new file mode 100644
index 0000000000000000000000000000000000000000..3c72ee857910c88ac9259a4884899e761131e924
--- /dev/null
+++ b/ev/+linux/io.ha
@@ -0,0 +1,62 @@
+use io;
+use rt;
+
+// A callback for a [[read]] or [[readv]] operation.
+export type readcb = *fn(file: *file, result: (size | io::EOF | io::error)) void;
+
+// Schedules a read operation on a file object.
+export fn read(
+	file: *file,
+	cb: *readcb,
+	buf: []u8,
+) req = {
+	file.vbuf = io::mkvector(buf);
+	// XXX: Bit of a hack to avoid allocating a slice
+	const vec = (&file.vbuf: *[*]io::vector)[..1];
+	return readv(file, cb, vec...);
+};
+
+// Schedules a vectored read operation on a file object.
+export fn readv(
+	file: *file,
+	cb: *readcb,
+	vec: io::vector...
+) req = {
+	assert(file.op == op::NONE);
+	file.op = op::READV;
+	file.callback = cb;
+	file.vec = vec;
+	filemod(file, rt::EPOLLOUT);
+	return req { ... };
+};
+
+// A callback for a [[write]] or [[writev]] operation.
+export type writecb = *fn(file: *file, result: (size | io::error)) void;
+
+// Schedules a write operation on a file object.
+export fn write(
+	file: *file,
+	cb: *writecb,
+	buf: []u8,
+) req = {
+	file.vbuf = io::mkvector(buf);
+	// XXX: Bit of a hack to avoid allocating a slice
+	const vec = (&file.vbuf: *[*]io::vector)[..1];
+	return writev(file, cb, vec...);
+};
+
+// Schedules a vectored read operation on a file object.
+export fn writev(
+	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::NONE);
+	file.op = op::WRITEV;
+	file.callback = cb;
+	file.vec = vec;
+	filemod(file, rt::EPOLLOUT);
+	return req { ... };
+};
diff --git a/ev/+linux/types.ha b/ev/+linux/types.ha
index c6713c6a7227677a13cd1423693724b31626b39e..067ce7bf41b5de538d0cf6c18c48ac6ab12641ab 100644
--- a/ev/+linux/types.ha
+++ b/ev/+linux/types.ha
@@ -1,15 +1,3 @@
-// Defines events which are available for monitoring.
-export type event = enum u32 {
-	// Read operations are available
-	IN = 0x00000001,
-	// Write operations are available
-	OUT = 0x00000004,
-	// An error condition has been reported
-	ERR = 0x00000008,
-	// There is exceptional data available in the file descriptor;
-	// equivalent to [[unix::poll::event::POLLPRI]]
-	PRI = 0x00000002,
-	// A hang-up event has occured
-	HUP = 0x00000010,
+export type req = struct {
+	placeholder: uint,
 };
-// TODO: Add less portable flags like EPOLLWAKEUP

-- 
2.48.1