summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmd/sbc_harness/config/config.h18
-rw-r--r--cmd/sbc_harness/fs_harness_uptime_txt.c4
-rw-r--r--cmd/sbc_harness/usb_keyboard.c6
-rw-r--r--gdb-helpers/libcr.py142
-rw-r--r--lib9p/srv.c144
-rw-r--r--lib9p/srv_include/lib9p/srv.h58
-rw-r--r--lib9p/tests/test_server/CMakeLists.txt2
-rw-r--r--lib9p/tests/test_server/config/config.h16
-rw-r--r--lib9p/tests/test_server/fs_flush.c136
-rw-r--r--lib9p/tests/test_server/fs_flush.h27
-rw-r--r--lib9p/tests/test_server/fs_shutdown.c4
-rw-r--r--lib9p/tests/test_server/fs_slowread.c116
-rw-r--r--lib9p/tests/test_server/fs_slowread.h22
-rw-r--r--lib9p/tests/test_server/fs_whoami.c6
-rw-r--r--lib9p/tests/test_server/main.c11
-rwxr-xr-xlib9p/tests/testclient-p9p7
-rw-r--r--lib9p/tests/testclient-p9p.explog4
-rw-r--r--lib9p/tests/testclient-sess.c45
-rw-r--r--lib9p/tests/testclient-sess.explog59
-rw-r--r--libcr/tests/test_matrix.c6
-rw-r--r--libcr_ipc/chan.c151
-rw-r--r--libcr_ipc/include/libcr_ipc/chan.h239
-rw-r--r--libcr_ipc/include/libcr_ipc/mutex.h8
-rw-r--r--libcr_ipc/include/libcr_ipc/rpc.h185
-rw-r--r--libcr_ipc/include/libcr_ipc/rwmutex.h12
-rw-r--r--libcr_ipc/include/libcr_ipc/sema.h10
-rw-r--r--libcr_ipc/mutex.c13
-rw-r--r--libcr_ipc/rpc.c35
-rw-r--r--libcr_ipc/rwmutex.c46
-rw-r--r--libcr_ipc/sema.c26
-rw-r--r--libcr_ipc/tests/test_chan.c18
-rw-r--r--libcr_ipc/tests/test_mutex.c6
-rw-r--r--libcr_ipc/tests/test_rpc.c24
-rw-r--r--libcr_ipc/tests/test_select.c21
-rw-r--r--libcr_ipc/tests/test_sema.c18
-rw-r--r--libhw_cr/host_net.c3
-rw-r--r--libhw_cr/rp2040_hwspi.c6
-rw-r--r--libhw_cr/w5500.c6
-rw-r--r--libhw_cr/w5500_ll.h6
-rw-r--r--libmisc/include/libmisc/alloc.h26
-rw-r--r--libmisc/include/libmisc/linkedlist.h112
-rw-r--r--libmisc/include/libmisc/map.h86
-rw-r--r--libmisc/linkedlist.c14
-rw-r--r--libmisc/map.c72
44 files changed, 1119 insertions, 857 deletions
diff --git a/cmd/sbc_harness/config/config.h b/cmd/sbc_harness/config/config.h
index 5367dbe..ca23462 100644
--- a/cmd/sbc_harness/config/config.h
+++ b/cmd/sbc_harness/config/config.h
@@ -39,6 +39,18 @@
#define CONFIG_9P_MAX_ERR_SIZE 128 /* 128 is what Plan 9 4e uses */
+#define CONFIG_9P_ENABLE_9P2000 1 /* bool */
+#define CONFIG_9P_ENABLE_9P2000_u 1 /* bool */
+#define CONFIG_9P_ENABLE_9P2000_e 0 /* bool */
+#define CONFIG_9P_ENABLE_9P2000_L 0 /* bool */
+#define CONFIG_9P_ENABLE_9P2000_p9p 0 /* bool */
+
+/* 9P_SRV *********************************************************************/
+
+#define CONFIG_9P_SRV_DEBUG 1 /* bool */
+
+#define CONFIG_9P_SRV_MAX_MSG_SIZE ((4*1024)+24)
+
/**
* This max-msg-size is sized so that a Twrite message can return
* 8KiB of data.
@@ -64,12 +76,6 @@
*/
#define CONFIG_9P_SRV_MAX_HOSTMSG_SIZE CONFIG_9P_SRV_MAX_MSG_SIZE+16
-#define CONFIG_9P_ENABLE_9P2000 1 /* bool */
-#define CONFIG_9P_ENABLE_9P2000_u 1 /* bool */
-#define CONFIG_9P_ENABLE_9P2000_e 0 /* bool */
-#define CONFIG_9P_ENABLE_9P2000_L 0 /* bool */
-#define CONFIG_9P_ENABLE_9P2000_p9p 0 /* bool */
-
/* DHCP ***********************************************************************/
#define CONFIG_DHCP_CAN_RECV_UNICAST_IP_WITHOUT_IP 0 /* bool */
diff --git a/cmd/sbc_harness/fs_harness_uptime_txt.c b/cmd/sbc_harness/fs_harness_uptime_txt.c
index 1425bf9..dd5c681 100644
--- a/cmd/sbc_harness/fs_harness_uptime_txt.c
+++ b/cmd/sbc_harness/fs_harness_uptime_txt.c
@@ -5,10 +5,10 @@
*/
#include <stdio.h> /* for snprintf() */
-#include <stdlib.h> /* for malloc(), free() */
#include <libhw/generic/alarmclock.h>
#include <util9p/static.h>
+#include <libmisc/alloc.h> /* for heap_alloc(), free() */
#include "fs_harness_uptime_txt.h"
@@ -91,7 +91,7 @@ static lo_interface lib9p_srv_fio uptime_file_fopen(struct uptime_file *self, st
assert(self);
assert(ctx);
- struct uptime_fio *ret = malloc(sizeof(struct uptime_fio));
+ struct uptime_fio *ret = heap_alloc(1, struct uptime_fio);
ret->parent = self;
ret->buf_len = 0;
diff --git a/cmd/sbc_harness/usb_keyboard.c b/cmd/sbc_harness/usb_keyboard.c
index f3cb42d..7dd8a24 100644
--- a/cmd/sbc_harness/usb_keyboard.c
+++ b/cmd/sbc_harness/usb_keyboard.c
@@ -54,8 +54,8 @@ COROUTINE usb_keyboard_cr(void *_chan) {
while (!tud_hid_n_ready(kbd_ifc))
cr_yield();
- if (usb_keyboard_rpc_can_recv_req(chan)) {
- usb_keyboard_rpc_req_t req = usb_keyboard_rpc_recv_req(chan);
+ if (cr_rpc_can_recv_req(chan)) {
+ usb_keyboard_rpc_req_t req = cr_rpc_recv_req(chan);
uint32_t rune = req.req;
modifier = ascii2keycode[rune][0] ? KEYBOARD_MODIFIER_LEFTSHIFT : 0;
@@ -69,7 +69,7 @@ COROUTINE usb_keyboard_cr(void *_chan) {
keycodes[0] = 0;
tud_hid_n_keyboard_report(kbd_ifc, report_id, modifier, keycodes);
- usb_keyboard_rpc_send_resp(req, 1);
+ cr_rpc_send_resp(req, 1);
} else {
modifier = 0;
keycodes[0] = 0;
diff --git a/gdb-helpers/libcr.py b/gdb-helpers/libcr.py
index fcfd86e..6f95a81 100644
--- a/gdb-helpers/libcr.py
+++ b/gdb-helpers/libcr.py
@@ -89,6 +89,7 @@ def gdb_longjmp(buf: gdb_JmpBuf) -> None:
class CrGlobals:
+ main: "CrMain"
coroutines: list["CrCoroutine"]
_breakpoint: "CrBreakpoint"
_known_threads: set[gdb.InferiorThread]
@@ -98,6 +99,7 @@ class CrGlobals:
gdb.parse_and_eval("sizeof(coroutine_table)/sizeof(coroutine_table[0])")
)
+ self.main = CrMain(self)
self.coroutines = [CrCoroutine(self, i + 1) for i in range(num)]
self._breakpoint = CrBreakpoint()
@@ -126,35 +128,37 @@ class CrGlobals:
# Ignore thread creation events.
self._known_threads = cur_threads
return
- if self.coroutine_running:
- if not self.coroutine_running.is_selected():
- if gdb_bug_32428:
- print("Must return to running coroutine before continuing.")
- print("Hit ^C twice then run:")
- print(f" cr select {self.coroutine_running.id}")
- while True:
- time.sleep(1)
- assert self.coroutine_running.cont_env
- gdb_longjmp(self.coroutine_running.cont_env)
+ if not self.coroutine_running.is_selected():
+ if gdb_bug_32428:
+ print("Must return to running coroutine before continuing.")
+ print("Hit ^C twice then run:")
+ print(f" cr select {self.coroutine_running.cid}")
+ while True:
+ time.sleep(1)
+ assert self.coroutine_running.cont_env
+ gdb_longjmp(self.coroutine_running.cont_env)
+ self.main.cont_env = None
for cr in self.coroutines:
cr.cont_env = None
def is_valid_cid(self, cid: int) -> bool:
- return 0 < cid <= len(self.coroutines)
+ return (0 < cid <= len(self.coroutines)) and (
+ self.coroutines[cid - 1].state != self.CR_NONE
+ )
@property
- def coroutine_running(self) -> "CrCoroutine | None":
+ def coroutine_running(self) -> "CrMain | CrCoroutine":
cid = int(gdb.parse_and_eval("coroutine_running"))
if not self.is_valid_cid(cid):
- return None
+ return self.main
return self.coroutines[cid - 1]
@property
- def coroutine_selected(self) -> "CrCoroutine | None":
+ def coroutine_selected(self) -> "CrMain | CrCoroutine":
for cr in self.coroutines:
if cr.is_selected():
return cr
- return None
+ return self.main
@property
def CR_NONE(self) -> gdb.Value:
@@ -164,6 +168,35 @@ class CrGlobals:
def CR_RUNNING(self) -> gdb.Value:
return gdb.parse_and_eval("CR_RUNNING")
+ def select(self, cr: "CrMain | CrCoroutine", level: int = -1) -> None:
+ self.coroutine_selected.cont_env = gdb_setjmp()
+
+ if cr.cont_env:
+ gdb_longjmp(cr.cont_env)
+ else:
+ env: gdb_JmpBuf
+ if cr == self.coroutine_running:
+ assert False # cr.cont_env should have been set
+ match cr:
+ case CrMain():
+ env = self.readjmp("&coroutine_main_env")
+ case CrCoroutine():
+ if cr.state == self.CR_RUNNING:
+ env = self.readjmp("&coroutine_add_env")
+ else:
+ env = self.readjmp(f"&coroutine_table[{cr.cid-1}].env")
+ gdb_longjmp(env)
+ cr_select_top_frame()
+
+ @contextlib.contextmanager
+ def with_selected(self, cr: "CrMain | CrCoroutine") -> typing.Iterator[None]:
+ saved_env = gdb_setjmp()
+ self.select(cr)
+ try:
+ yield
+ finally:
+ gdb_longjmp(saved_env)
+
class CrBreakpointUnwinder(gdb.unwinder.Unwinder):
"""Used to temporarily disable unwinding so that
@@ -245,6 +278,22 @@ def cr_select_top_frame() -> None:
break
+class CrMain:
+ cr_globals: CrGlobals
+ cont_env: gdb_JmpBuf | None
+
+ def __init__(self, cr_globals: CrGlobals) -> None:
+ self.cr_globals = cr_globals
+ self.cont_env = None
+
+ @property
+ def cid(self) -> int:
+ return 0
+
+ def is_selected(self) -> bool:
+ return not any(cr.is_selected() for cr in self.cr_globals.coroutines)
+
+
class CrCoroutine:
cr_globals: CrGlobals
cid: int
@@ -256,10 +305,6 @@ class CrCoroutine:
self.cont_env = None
@property
- def id(self) -> int:
- return self.cid
-
- @property
def state(self) -> gdb.Value:
return gdb.parse_and_eval(f"coroutine_table[{self.cid-1}].state")
@@ -272,36 +317,10 @@ class CrCoroutine:
def is_selected(self) -> bool:
sp = int(gdb.parse_and_eval("$sp"))
- lo = int(gdb.parse_and_eval(f"coroutine_table[{self.id-1}].stack"))
- hi = lo + int(gdb.parse_and_eval(f"coroutine_table[{self.id-1}].stack_size"))
+ lo = int(gdb.parse_and_eval(f"coroutine_table[{self.cid-1}].stack"))
+ hi = lo + int(gdb.parse_and_eval(f"coroutine_table[{self.cid-1}].stack_size"))
return lo <= sp < hi
- def select(self, level: int = -1) -> None:
- if self.cr_globals.coroutine_selected:
- self.cr_globals.coroutine_selected.cont_env = gdb_setjmp()
-
- if self.cont_env:
- gdb_longjmp(self.cont_env)
- else:
- env: gdb_JmpBuf
- if self == self.cr_globals.coroutine_running:
- assert False # self.cont_env should have been set
- elif self.state == self.cr_globals.CR_RUNNING:
- env = self.cr_globals.readjmp("&coroutine_add_env")
- else:
- env = self.cr_globals.readjmp(f"&coroutine_table[{self.id-1}].env")
- gdb_longjmp(env)
- cr_select_top_frame()
-
- @contextlib.contextmanager
- def with_selected(self) -> typing.Iterator[None]:
- saved_env = gdb_setjmp()
- self.select()
- try:
- yield
- finally:
- gdb_longjmp(saved_env)
-
# User-facing commands #########################################################
@@ -342,8 +361,11 @@ class CrListCommand(gdb.Command):
rows: list[tuple[str, str, str, str, str]] = [
("", "Id", "Name", "State", "Frame")
]
- for cr in self.cr_globals.coroutines:
- if cr.state == self.cr_globals.CR_NONE:
+ for cid in range(len(self.cr_globals.coroutines) + 1):
+ cr: CrMain | CrCoroutine = (
+ self.cr_globals.coroutines[cid - 1] if cid else self.cr_globals.main
+ )
+ if isinstance(cr, CrCoroutine) and cr.state == self.cr_globals.CR_NONE:
continue
rows += [
(
@@ -353,9 +375,9 @@ class CrListCommand(gdb.Command):
"G" if cr.is_selected() else " ",
]
),
- str(cr.id),
- repr(cr.name),
- str(cr.state),
+ str(cr.cid),
+ repr(cr.name) if isinstance(cr, CrCoroutine) else "-",
+ str(cr.state) if isinstance(cr, CrCoroutine) else "-",
self._pretty_frame(cr, from_tty),
)
]
@@ -382,9 +404,9 @@ class CrListCommand(gdb.Command):
l = l[:maxline]
print(l)
- def _pretty_frame(self, cr: CrCoroutine, from_tty: bool) -> str:
+ def _pretty_frame(self, cr: CrMain | CrCoroutine, from_tty: bool) -> str:
try:
- with cr.with_selected():
+ with self.cr_globals.with_selected(cr):
saved_level = gdb.selected_frame().level()
cr_select_top_frame()
full = gdb.execute("frame", from_tty=from_tty, to_string=True)
@@ -411,16 +433,15 @@ class CrSelectCommand(gdb.Command):
if len(argv) != 1:
raise gdb.GdbError("Usage: cr select COROUTINE")
cr = self._find(argv[0])
- cr.select()
+ self.cr_globals.select(cr)
gdb.execute("frame")
- def _find(self, name: str) -> CrCoroutine:
+ def _find(self, name: str) -> CrMain | CrCoroutine:
if name.isnumeric():
cid = int(name)
- if (
- self.cr_globals.is_valid_cid(cid)
- and self.cr_globals.coroutines[cid - 1].state != self.cr_globals.CR_NONE
- ):
+ if cid == 0:
+ return self.cr_globals.main
+ if self.cr_globals.is_valid_cid(cid):
return self.cr_globals.coroutines[cid - 1]
crs: list[CrCoroutine] = []
for cr in self.cr_globals.coroutines:
@@ -445,6 +466,7 @@ def cr_initialize() -> None:
if _cr_globals:
old = _cr_globals
new = CrGlobals()
+ new.main.cont_env = old.main.cont_env
for i in range(min(len(old.coroutines), len(new.coroutines))):
new.coroutines[i].cont_env = old.coroutines[i].cont_env
old.delete()
diff --git a/lib9p/srv.c b/lib9p/srv.c
index 7e2bab0..6ab2ab2 100644
--- a/lib9p/srv.c
+++ b/lib9p/srv.c
@@ -4,7 +4,6 @@
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
-#include <alloca.h>
#include <inttypes.h> /* for PRI* */
#include <limits.h> /* for SSIZE_MAX, not set by newlib */
#include <stddef.h> /* for size_t */
@@ -17,6 +16,7 @@
#include <libcr/coroutine.h>
#include <libcr_ipc/chan.h>
#include <libcr_ipc/mutex.h>
+#include <libmisc/alloc.h>
#include <libmisc/assert.h>
#include <libmisc/endian.h>
#include <libmisc/map.h>
@@ -45,16 +45,22 @@ static_assert(CONFIG_9P_SRV_MAX_HOSTMSG_SIZE <= SSIZE_MAX);
bool lib9p_srv_flush_requested(struct lib9p_srv_ctx *ctx) {
assert(ctx);
- return _lib9p_srv_flushch_can_send(&ctx->flushch);
+ return cr_chan_can_send(&ctx->flush_ch);
}
void lib9p_srv_acknowledge_flush(struct lib9p_srv_ctx *ctx) {
assert(ctx);
- assert(_lib9p_srv_flushch_can_send(&ctx->flushch));
- lib9p_error(&ctx->basectx, LIB9P_ERRNO_L_ECANCELED, "request canceled by flush");
- _lib9p_srv_flushch_send(&ctx->flushch, true);
+ assert(cr_chan_can_send(&ctx->flush_ch));
+ ctx->flush_acknowledged = true;
}
+#define req_debugf(fmt, ...) \
+ debugf("cid=%zu: %s(tag=%"PRIu16"): " fmt, \
+ cr_getcid(), \
+ lib9p_msgtype_str(ctx->basectx.version, ctx->net_bytes[4]), \
+ ctx->tag \
+ __VA_OPT__(,) __VA_ARGS__)
+
/* structs ********************************************************************/
enum srv_filetype {
@@ -443,15 +449,13 @@ void lib9p_srv_accept_and_read_loop(struct lib9p_srv *srv, lo_interface net_stre
srv->readers--;
if (srv->readers == 0)
while (srv->writers > 0)
- _lib9p_srv_reqch_send_req(&srv->_reqch, NULL);
+ cr_rpc_send_req(&srv->_reqch, NULL);
return;
}
lib9p_srv_read(srv, conn);
}
}
-static void handle_message(struct srv_req *ctx);
-
void lib9p_srv_read(struct lib9p_srv *srv, lo_interface net_stream_conn _conn) {
assert(srv);
assert(srv->rootdir);
@@ -511,10 +515,10 @@ void lib9p_srv_read(struct lib9p_srv *srv, lo_interface net_stream_conn _conn) {
/* Handle the message... */
if (req.net_bytes[4] == LIB9P_TYP_Tversion)
/* ...in this coroutine for Tversion, */
- handle_message(&req);
+ lib9p_srv_worker(&req);
else
/* ...but usually in another coroutine. */
- _lib9p_srv_reqch_send_req(&srv->_reqch, &req);
+ cr_rpc_send_req(&srv->_reqch, &req);
}
if (map_len(&sess.reqs) == 0)
io_close(conn.fd);
@@ -560,10 +564,10 @@ void lib9p_srv_worker_loop(struct lib9p_srv *srv) {
for (;;) {
/* Receive the request from the reader coroutine. ************/
- rpc_handle = _lib9p_srv_reqch_recv_req(&srv->_reqch);
+ rpc_handle = cr_rpc_recv_req(&srv->_reqch);
if (!rpc_handle.req) {
srv->writers--;
- _lib9p_srv_reqch_send_resp(rpc_handle, 0);
+ cr_rpc_send_resp(rpc_handle, 0);
return;
}
/* Copy the request from the reader coroutine's
@@ -574,17 +578,10 @@ void lib9p_srv_worker_loop(struct lib9p_srv *srv) {
assert(reqpp && *reqpp == &req);
/* Notify the reader coroutine that we're done with
* its data. */
- _lib9p_srv_reqch_send_resp(rpc_handle, 0);
+ cr_rpc_send_resp(rpc_handle, 0);
/* Process the request. **************************************/
- handle_message(&req);
-
- /* Release resources. ****************************************/
- while (_lib9p_srv_flushch_can_send(&req.flushch))
- _lib9p_srv_flushch_send(&req.flushch, false);
- map_del(&req.parent_sess->reqs, req.tag);
- if (req.parent_sess->closing && !map_len(&req.parent_sess->reqs))
- cr_unpause(req.parent_sess->parent_conn->reader);
+ lib9p_srv_worker(&req);
}
}
@@ -616,35 +613,11 @@ _HANDLER_PROTO(swrite);
typedef void (*tmessage_handler)(struct srv_req *, void *, void *);
-static tmessage_handler tmessage_handlers[0x100] = {
- [LIB9P_TYP_Tversion] = (tmessage_handler)handle_Tversion,
- [LIB9P_TYP_Tauth] = (tmessage_handler)handle_Tauth,
- [LIB9P_TYP_Tattach] = (tmessage_handler)handle_Tattach,
- [LIB9P_TYP_Tflush] = (tmessage_handler)handle_Tflush,
- [LIB9P_TYP_Twalk] = (tmessage_handler)handle_Twalk,
- [LIB9P_TYP_Topen] = (tmessage_handler)handle_Topen,
- [LIB9P_TYP_Tcreate] = (tmessage_handler)handle_Tcreate,
- [LIB9P_TYP_Tread] = (tmessage_handler)handle_Tread,
- [LIB9P_TYP_Twrite] = (tmessage_handler)handle_Twrite,
- [LIB9P_TYP_Tclunk] = (tmessage_handler)handle_Tclunk,
- [LIB9P_TYP_Tremove] = (tmessage_handler)handle_Tremove,
- [LIB9P_TYP_Tstat] = (tmessage_handler)handle_Tstat,
- [LIB9P_TYP_Twstat] = (tmessage_handler)handle_Twstat,
-#if CONFIG_9P_ENABLE_9P2000_p9p
- [LIB9P_TYP_Topenfd] = (tmessage_handler)handle_Topenfd,
-#endif
-#if CONFIG_9P_ENABLE_9P2000_e
- [LIB9P_TYP_Tsession] = (tmessage_handler)handle_Tsession,
- [LIB9P_TYP_Tsread] = (tmessage_handler)handle_Tsread,
- [LIB9P_TYP_Tswrite] = (tmessage_handler)handle_Tswrite,
-#endif
-};
-
-static void handle_message(struct srv_req *ctx) {
+void lib9p_srv_worker(struct srv_req *ctx) {
uint8_t *host_req = NULL;
uint8_t host_resp[CONFIG_9P_SRV_MAX_HOSTMSG_SIZE];
- /* Unmarshal it. */
+ /* Unmarshal it. *****************************************************/
ssize_t host_size = lib9p_Tmsg_validate(&ctx->basectx, ctx->net_bytes);
if (host_size < 0)
goto write;
@@ -655,13 +628,45 @@ static void handle_message(struct srv_req *ctx) {
&typ, host_req);
srv_msglog(ctx, typ, host_req);
- /* Handle it. */
- tmessage_handlers[typ](ctx, (void *)host_req, (void *)host_resp);
+ /* Handle it. ********************************************************/
+ tmessage_handler handler;
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wswitch-enum"
+ switch (typ) {
+ case LIB9P_TYP_Tversion: handler = (tmessage_handler)handle_Tversion; break;
+ case LIB9P_TYP_Tauth: handler = (tmessage_handler)handle_Tauth; break;
+ case LIB9P_TYP_Tattach: handler = (tmessage_handler)handle_Tattach; break;
+ case LIB9P_TYP_Tflush: handler = (tmessage_handler)handle_Tflush; break;
+ case LIB9P_TYP_Twalk: handler = (tmessage_handler)handle_Twalk; break;
+ case LIB9P_TYP_Topen: handler = (tmessage_handler)handle_Topen; break;
+ case LIB9P_TYP_Tcreate: handler = (tmessage_handler)handle_Tcreate; break;
+ case LIB9P_TYP_Tread: handler = (tmessage_handler)handle_Tread; break;
+ case LIB9P_TYP_Twrite: handler = (tmessage_handler)handle_Twrite; break;
+ case LIB9P_TYP_Tclunk: handler = (tmessage_handler)handle_Tclunk; break;
+ case LIB9P_TYP_Tremove: handler = (tmessage_handler)handle_Tremove; break;
+ case LIB9P_TYP_Tstat: handler = (tmessage_handler)handle_Tstat; break;
+ case LIB9P_TYP_Twstat: handler = (tmessage_handler)handle_Twstat; break;
+#if CONFIG_9P_ENABLE_9P2000_p9p
+ case LIB9P_TYP_Topenfd: handler = (tmessage_handler)handle_Topenfd; break
+#endif
+#if CONFIG_9P_ENABLE_9P2000_e
+ case LIB9P_TYP_Tsession: handler = (tmessage_handler)handle_Tsession; break;
+ case LIB9P_TYP_Tsread: handler = (tmessage_handler)handle_Tsread; break;
+ case LIB9P_TYP_Tswrite: handler = (tmessage_handler)handle_Tswrite; break;
+#endif
+ default:
+ assert_notreached("lib9p_Tmsg_validate() should have rejected unknown typ");
+ }
+#pragma GCC diagnostic pop
+ handler(ctx, (void *)host_req, (void *)host_resp);
+ /* Write the response. ***********************************************/
write:
- if (lib9p_ctx_has_error(&ctx->basectx))
+ if (lib9p_ctx_has_error(&ctx->basectx)) {
srv_respond_error(ctx);
- else {
+ } else if (ctx->flush_acknowledged) {
+ /* do nothing */
+ } else {
struct lib9p_Rmsg_send_buf net_resp;
if (lib9p_Rmsg_marshal(&ctx->basectx,
typ+1, host_resp,
@@ -670,6 +675,16 @@ static void handle_message(struct srv_req *ctx) {
srv_msglog(ctx, typ+1, &host_resp);
srv_write_Rmsg(ctx, &net_resp);
}
+ /* Release resources. ************************************************/
+ map_del(&ctx->parent_sess->reqs, ctx->tag);
+ size_t nwaiters;
+ while ((nwaiters = cr_chan_num_waiters(&ctx->flush_ch))) {
+ cr_chan_send(&ctx->flush_ch, (nwaiters == 1)
+ ? _LIB9P_SRV_FLUSH_RFLUSH
+ : _LIB9P_SRV_FLUSH_SILENT);
+ }
+ if (ctx->parent_sess->closing && !map_len(&ctx->parent_sess->reqs))
+ cr_unpause(ctx->parent_sess->parent_conn->reader);
if (host_req)
free(host_req);
free(ctx->net_bytes);
@@ -736,15 +751,15 @@ static void handle_Tversion(struct srv_req *ctx,
if (map_len(&ctx->parent_sess->reqs)) {
/* Flush all in-progress requests, and wait for them
* to finish. */
- struct cr_select_arg *list = alloca(sizeof(struct cr_select_arg) * map_len(&ctx->parent_sess->reqs));
+ struct cr_select_arg *args = stack_alloc(map_len(&ctx->parent_sess->reqs), struct cr_select_arg);
while (map_len(&ctx->parent_sess->reqs)) {
size_t i = 0;
- bool flushed;
MAP_FOREACH(&ctx->parent_sess->reqs, tag, reqpp) {
- list[i] = CR_SELECT_RECV(&((*reqpp)->flushch), &flushed);
+ enum _lib9p_srv_flush_result flushed;
+ args[i++] = CR_SELECT_RECV(&((*reqpp)->flush_ch), &flushed);
}
assert(i == map_len(&ctx->parent_sess->reqs));
- cr_select_v(i, list);
+ cr_select_v(i, args);
}
}
if (map_len(&ctx->parent_sess->fids)) {
@@ -867,8 +882,21 @@ static void handle_Tflush(struct srv_req *ctx,
srv_handler_common(ctx, req, resp);
struct srv_req **oldreqp = map_load(&ctx->parent_sess->reqs, req->oldtag);
- if (oldreqp)
- _lib9p_srv_flushch_recv(&((*oldreqp)->flushch));
+ if (oldreqp) {
+ struct srv_req *oldreq = *oldreqp;
+ enum _lib9p_srv_flush_result res = _LIB9P_SRV_FLUSH_RFLUSH;
+ switch (cr_select_l(CR_SELECT_RECV(&oldreq->flush_ch, &res),
+ CR_SELECT_SEND(&ctx->flush_ch, &res))) {
+ case 0: /* original request returned */
+ req_debugf("original request (tag=%"PRIu16") returned", req->oldtag);
+ ctx->flush_acknowledged = (res == _LIB9P_SRV_FLUSH_SILENT);
+ break;
+ case 1: /* flush itself got flushed */
+ req_debugf("flush itself flushed");
+ ctx->flush_acknowledged = true;
+ break;
+ }
+ }
}
static void handle_Twalk(struct srv_req *ctx,
@@ -1146,7 +1174,7 @@ static void handle_Tread(struct srv_req *ctx,
case SRV_FILETYPE_FILE:
struct iovec iov;
LO_CALL(fidinfo->file.io, pread, ctx, req->count, req->offset, &iov);
- if (!lib9p_ctx_has_error(&ctx->basectx)) {
+ if (!lib9p_ctx_has_error(&ctx->basectx) && !ctx->flush_acknowledged) {
resp->count = iov.iov_len;
resp->data = iov.iov_base;
if (resp->count > req->count)
diff --git a/lib9p/srv_include/lib9p/srv.h b/lib9p/srv_include/lib9p/srv.h
index db5be41..9b3256b 100644
--- a/lib9p/srv_include/lib9p/srv.h
+++ b/lib9p/srv_include/lib9p/srv.h
@@ -8,8 +8,8 @@
#define _LIB9P_SRV_H_
#include <libcr/coroutine.h>
-#include <libcr_ipc/rpc.h>
#include <libcr_ipc/chan.h>
+#include <libcr_ipc/rpc.h>
#include <libhw/generic/net.h>
#include <libmisc/assert.h>
#include <libmisc/private.h>
@@ -19,8 +19,6 @@
/* context ********************************************************************/
-CR_CHAN_DECLARE(_lib9p_srv_flushch, bool);
-
struct lib9p_srv_authinfo {
lib9p_nuid_t uid;
struct lib9p_s uname;
@@ -30,20 +28,42 @@ struct lib9p_srv_authinfo {
END_PRIVATE(LIB9P_SRV_H);
};
+enum _lib9p_srv_flush_result {
+ _LIB9P_SRV_FLUSH_RFLUSH,
+ _LIB9P_SRV_FLUSH_SILENT,
+};
+
+CR_CHAN_DECLARE(_lib9p_srv_flush_ch, enum _lib9p_srv_flush_result);
+
struct lib9p_srv_ctx {
struct lib9p_ctx basectx;
struct lib9p_srv_authinfo *authinfo;
BEGIN_PRIVATE(LIB9P_SRV_H);
- struct _lib9p_srv_sess *parent_sess;
- lib9p_tag_t tag;
- uint8_t *net_bytes;
- _lib9p_srv_flushch_t flushch;
+ struct _lib9p_srv_sess *parent_sess;
+ lib9p_tag_t tag;
+ uint8_t *net_bytes;
+ _lib9p_srv_flush_ch_t flush_ch;
+ bool flush_acknowledged;
END_PRIVATE(LIB9P_SRV_H);
};
+/**
+ * Return whether there is an outstanding Tflush or Tversion
+ * cancellation of this request. After becoming true, this may go
+ * back to false if the Tflush itself is flushed.
+ */
bool lib9p_srv_flush_requested(struct lib9p_srv_ctx *ctx);
+/**
+ * Acknowledge that the handler is responding to an outstanding flush;
+ * a non-Rerror R-message will be elided in favor of Rflush/Rversion.
+ * lib9p_srv_flush_requested() must be true; so do not cr_yield()
+ * between checking lib9p_srv_flush_requested() and calling
+ * lib9p_srv_acknowledge_flush(). These are separate calls to
+ * facilitate cases where a flush merely truncates a call, instead of
+ * totally canceling it.
+ */
void lib9p_srv_acknowledge_flush(struct lib9p_srv_ctx *ctx);
/* interface definitions ******************************************************/
@@ -207,20 +227,32 @@ void lib9p_srv_accept_and_read_loop(struct lib9p_srv *srv, lo_interface net_stre
* @errno L_EDOM Tversion specified an impossibly small max_msg_size
* @errno L_EOPNOTSUPP T-message has an R-message type, or an unrecognized T-message type
* @errno L_EBADMSG T-message has wrong size[4] for its content, or has invalid UTF-8
- * @errno L_ERANGE R-message does not fit into max_msg_size
*/
void lib9p_srv_read(struct lib9p_srv *srv, lo_interface net_stream_conn conn);
-
-
/**
- * In a loop, service requests to the `struct lib9p_srv *srv` argument
- * that have been read by lib9p_srv_accept_and_read_loop() /
- * lib9p_srv_read(). A "NULL" request causes the function to return.
+ * In a loop, call lib9p_srv_worker() to service requests to the
+ * `struct lib9p_srv *srv` argument that have been read by
+ * lib9p_srv_accept_and_read_loop() / lib9p_srv_read(). A "NULL"
+ * request causes the function to return.
*
* @param srv: The server configuration and state; has an associated
* pool of lib9p_srv_accept_and_read_loop() coroutines.
*/
void lib9p_srv_worker_loop(struct lib9p_srv *srv);
+/**
+ * You should probably not call this directly; you should probably use
+ * lib9p_srv_worker_loop().
+ *
+ * Handle and send a response to a single request.
+ *
+ * @param req: The request to handle.
+ *
+ * Errors that this function itself may send to clients:
+ *
+ * @errno L_ERANGE R-message does not fit into max_msg_size
+ */
+void lib9p_srv_worker(struct lib9p_srv_ctx *req);
+
#endif /* _LIB9P_SRV_H_ */
diff --git a/lib9p/tests/test_server/CMakeLists.txt b/lib9p/tests/test_server/CMakeLists.txt
index b659373..c61d344 100644
--- a/lib9p/tests/test_server/CMakeLists.txt
+++ b/lib9p/tests/test_server/CMakeLists.txt
@@ -9,8 +9,8 @@ if (PICO_PLATFORM STREQUAL "host")
add_library(test_server_objs OBJECT
main.c
+ fs_flush.c
fs_shutdown.c
- fs_slowread.c
fs_whoami.c
)
target_include_directories(test_server_objs PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/config)
diff --git a/lib9p/tests/test_server/config/config.h b/lib9p/tests/test_server/config/config.h
index d9cf008..f49894b 100644
--- a/lib9p/tests/test_server/config/config.h
+++ b/lib9p/tests/test_server/config/config.h
@@ -14,6 +14,16 @@
#define CONFIG_9P_MAX_ERR_SIZE 128 /* 128 is what Plan 9 4e uses */
+#define CONFIG_9P_ENABLE_9P2000 1 /* bool */
+#define CONFIG_9P_ENABLE_9P2000_u 1 /* bool */
+#define CONFIG_9P_ENABLE_9P2000_e 0 /* bool */
+#define CONFIG_9P_ENABLE_9P2000_L 0 /* bool */
+#define CONFIG_9P_ENABLE_9P2000_p9p 0 /* bool */
+
+/* 9P_SRV *********************************************************************/
+
+#define CONFIG_9P_SRV_DEBUG 1 /* bool */
+
/**
* This max-msg-size is sized so that a Twrite message can return
* 8KiB of data.
@@ -39,12 +49,6 @@
*/
#define CONFIG_9P_SRV_MAX_HOSTMSG_SIZE CONFIG_9P_SRV_MAX_MSG_SIZE+16
-#define CONFIG_9P_ENABLE_9P2000 1 /* bool */
-#define CONFIG_9P_ENABLE_9P2000_u 1 /* bool */
-#define CONFIG_9P_ENABLE_9P2000_e 0 /* bool */
-#define CONFIG_9P_ENABLE_9P2000_L 0 /* bool */
-#define CONFIG_9P_ENABLE_9P2000_p9p 0 /* bool */
-
/* COROUTINE ******************************************************************/
#define CONFIG_COROUTINE_STACK_SIZE_DEFAULT (32*1024)
diff --git a/lib9p/tests/test_server/fs_flush.c b/lib9p/tests/test_server/fs_flush.c
new file mode 100644
index 0000000..779eb91
--- /dev/null
+++ b/lib9p/tests/test_server/fs_flush.c
@@ -0,0 +1,136 @@
+/* lib9p/tests/test_server/fs_flush.c - flush-* API endpoints
+ *
+ * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#include <libmisc/alloc.h>
+
+#define IMPLEMENTATION_FOR_LIB9P_SRV_H YES /* for ctx->flush_ch */
+#include "fs_flush.h"
+
+LO_IMPLEMENTATION_C(lib9p_srv_file, struct flush_file, flush_file, static);
+
+struct flush_fio {
+ struct flush_file *parent;
+};
+LO_IMPLEMENTATION_H(lib9p_srv_fio, struct flush_fio, flush_fio);
+LO_IMPLEMENTATION_C(lib9p_srv_fio, struct flush_fio, flush_fio, static);
+
+/* srv_file *******************************************************************/
+
+static void flush_file_free(struct flush_file *self) {
+ assert(self);
+}
+static struct lib9p_qid flush_file_qid(struct flush_file *self) {
+ assert(self);
+ return (struct lib9p_qid){
+ .type = LIB9P_QT_FILE,
+ .vers = 1,
+ .path = self->pathnum,
+ };
+}
+
+static struct lib9p_stat flush_file_stat(struct flush_file *self, struct lib9p_srv_ctx *ctx) {
+ assert(self);
+ assert(ctx);
+ return (struct lib9p_stat){
+ .kern_type = 0,
+ .kern_dev = 0,
+ .file_qid = flush_file_qid(self),
+ .file_mode = 0444,
+ .file_atime = UTIL9P_ATIME,
+ .file_mtime = UTIL9P_MTIME,
+ .file_size = 6,
+ .file_name = lib9p_str(self->name),
+ .file_owner_uid = lib9p_str("root"),
+ .file_owner_gid = lib9p_str("root"),
+ .file_last_modified_uid = lib9p_str("root"),
+ .file_extension = lib9p_str(NULL),
+ .file_owner_n_uid = 0,
+ .file_owner_n_gid = 0,
+ .file_last_modified_n_uid = 0,
+ };
+}
+static void flush_file_wstat(struct flush_file *self, struct lib9p_srv_ctx *ctx, struct lib9p_stat) {
+ assert(self);
+ assert(ctx);
+ lib9p_error(&ctx->basectx, LIB9P_ERRNO_L_EROFS, "cannot wstat API file");
+}
+static void flush_file_remove(struct flush_file *self, struct lib9p_srv_ctx *ctx) {
+ assert(self);
+ assert(ctx);
+ lib9p_error(&ctx->basectx, LIB9P_ERRNO_L_EROFS, "cannot remove API file");
+}
+
+LIB9P_SRV_NOTDIR(struct flush_file, flush_file)
+
+static lo_interface lib9p_srv_fio flush_file_fopen(struct flush_file *self, struct lib9p_srv_ctx *ctx, bool, bool, bool) {
+ assert(self);
+ assert(ctx);
+
+ struct flush_fio *ret = heap_alloc(1, struct flush_fio);
+ ret->parent = self;
+
+ return lo_box_flush_fio_as_lib9p_srv_fio(ret);
+}
+
+/* srv_fio ********************************************************************/
+
+static void flush_fio_iofree(struct flush_fio *self) {
+ assert(self);
+ free(self);
+}
+
+static struct lib9p_qid flush_fio_qid(struct flush_fio *self) {
+ assert(self);
+ return flush_file_qid(self->parent);
+}
+
+static uint32_t flush_fio_iounit(struct flush_fio *self) {
+ assert(self);
+ return 0;
+}
+
+static uint32_t flush_fio_pwrite(struct flush_fio *LM_UNUSED(self),
+ struct lib9p_srv_ctx *LM_UNUSED(ctx),
+ void *LM_UNUSED(buf), uint32_t LM_UNUSED(byte_count),
+ uint64_t LM_UNUSED(offset)) {
+ assert_notreached("not writable");
+}
+
+static void flush_fio_pread(struct flush_fio *self, struct lib9p_srv_ctx *ctx,
+ uint32_t byte_count, uint64_t LM_UNUSED(byte_offset),
+ struct iovec *ret) {
+ assert(self);
+ assert(ctx);
+ assert(ret);
+
+ /* Wait for first Tflush */
+ while (!lib9p_srv_flush_requested(ctx))
+ cr_yield();
+
+ /* Wait for the specified number of Tflush (may be higher *or*
+ * lower than 1; lower would mean that the first Tflush needs
+ * to be flushed itself). */
+ while (cr_chan_num_waiters(&ctx->flush_ch) != self->parent->flush_cnt)
+ cr_yield();
+
+ /* Return */
+ switch (self->parent->flush_behavior) {
+ case FLUSH_READ:
+ *ret = (struct iovec){
+ .iov_base = "Sloth\n",
+ .iov_len = 6 < byte_count ? 6 : byte_count,
+ };
+ break;
+ case FLUSH_ERROR:
+ lib9p_srv_acknowledge_flush(ctx);
+ lib9p_error(&ctx->basectx, LIB9P_ERRNO_L_ECANCELED, "request canceled by flush");
+ break;
+ case FLUSH_SILENT:
+ lib9p_srv_acknowledge_flush(ctx);
+ break;
+ }
+ cr_yield();
+}
diff --git a/lib9p/tests/test_server/fs_flush.h b/lib9p/tests/test_server/fs_flush.h
new file mode 100644
index 0000000..a509c4a
--- /dev/null
+++ b/lib9p/tests/test_server/fs_flush.h
@@ -0,0 +1,27 @@
+/* lib9p/tests/test_server/fs_flush.h - flush-* API endpoints
+ *
+ * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#ifndef _LIB9P_TESTS_TEST_SERVER_FS_FLUSH_H_
+#define _LIB9P_TESTS_TEST_SERVER_FS_FLUSH_H_
+
+#include <util9p/static.h>
+#include <libhw/host_net.h>
+
+struct flush_file {
+ char *name;
+ uint64_t pathnum;
+
+ unsigned int flush_cnt;
+ enum {
+ FLUSH_READ,
+ FLUSH_ERROR,
+ FLUSH_SILENT,
+ } flush_behavior;
+};
+LO_IMPLEMENTATION_H(lib9p_srv_file, struct flush_file, flush_file);
+#define lo_box_flush_file_as_lib9p_srv_file(obj) util9p_box(flush_file, obj)
+
+#endif /* _LIB9P_TESTS_TEST_SERVER_FS_FLUSH_H_ */
diff --git a/lib9p/tests/test_server/fs_shutdown.c b/lib9p/tests/test_server/fs_shutdown.c
index e872b78..e7375ef 100644
--- a/lib9p/tests/test_server/fs_shutdown.c
+++ b/lib9p/tests/test_server/fs_shutdown.c
@@ -4,7 +4,7 @@
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
-#include <stdlib.h>
+#include <libmisc/alloc.h>
#include "fs_shutdown.h"
@@ -68,7 +68,7 @@ static lo_interface lib9p_srv_fio shutdown_file_fopen(struct shutdown_file *self
assert(self);
assert(ctx);
- struct shutdown_fio *ret = malloc(sizeof(struct shutdown_fio));
+ struct shutdown_fio *ret = heap_alloc(1, struct shutdown_fio);
ret->parent = self;
return lo_box_shutdown_fio_as_lib9p_srv_fio(ret);
diff --git a/lib9p/tests/test_server/fs_slowread.c b/lib9p/tests/test_server/fs_slowread.c
deleted file mode 100644
index c94fba0..0000000
--- a/lib9p/tests/test_server/fs_slowread.c
+++ /dev/null
@@ -1,116 +0,0 @@
-/* lib9p/tests/test_server/fs_slowread.c - slowread API endpoint
- *
- * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-
-#include <stdlib.h>
-
-#include "fs_slowread.h"
-
-LO_IMPLEMENTATION_C(lib9p_srv_file, struct slowread_file, slowread_file, static);
-
-struct slowread_fio {
- struct slowread_file *parent;
-};
-LO_IMPLEMENTATION_H(lib9p_srv_fio, struct slowread_fio, slowread_fio);
-LO_IMPLEMENTATION_C(lib9p_srv_fio, struct slowread_fio, slowread_fio, static);
-
-/* srv_file *******************************************************************/
-
-static void slowread_file_free(struct slowread_file *self) {
- assert(self);
-}
-static struct lib9p_qid slowread_file_qid(struct slowread_file *self) {
- assert(self);
- return (struct lib9p_qid){
- .type = LIB9P_QT_FILE,
- .vers = 1,
- .path = self->pathnum,
- };
-}
-
-static struct lib9p_stat slowread_file_stat(struct slowread_file *self, struct lib9p_srv_ctx *ctx) {
- assert(self);
- assert(ctx);
- return (struct lib9p_stat){
- .kern_type = 0,
- .kern_dev = 0,
- .file_qid = slowread_file_qid(self),
- .file_mode = 0444,
- .file_atime = UTIL9P_ATIME,
- .file_mtime = UTIL9P_MTIME,
- .file_size = 6,
- .file_name = lib9p_str(self->name),
- .file_owner_uid = lib9p_str("root"),
- .file_owner_gid = lib9p_str("root"),
- .file_last_modified_uid = lib9p_str("root"),
- .file_extension = lib9p_str(NULL),
- .file_owner_n_uid = 0,
- .file_owner_n_gid = 0,
- .file_last_modified_n_uid = 0,
- };
-}
-static void slowread_file_wstat(struct slowread_file *self, struct lib9p_srv_ctx *ctx, struct lib9p_stat) {
- assert(self);
- assert(ctx);
- lib9p_error(&ctx->basectx, LIB9P_ERRNO_L_EROFS, "cannot wstat API file");
-}
-static void slowread_file_remove(struct slowread_file *self, struct lib9p_srv_ctx *ctx) {
- assert(self);
- assert(ctx);
- lib9p_error(&ctx->basectx, LIB9P_ERRNO_L_EROFS, "cannot remove API file");
-}
-
-LIB9P_SRV_NOTDIR(struct slowread_file, slowread_file)
-
-static lo_interface lib9p_srv_fio slowread_file_fopen(struct slowread_file *self, struct lib9p_srv_ctx *ctx, bool, bool, bool) {
- assert(self);
- assert(ctx);
-
- struct slowread_fio *ret = malloc(sizeof(struct slowread_fio));
- ret->parent = self;
-
- return lo_box_slowread_fio_as_lib9p_srv_fio(ret);
-}
-
-/* srv_fio ********************************************************************/
-
-static void slowread_fio_iofree(struct slowread_fio *self) {
- assert(self);
- free(self);
-}
-
-static struct lib9p_qid slowread_fio_qid(struct slowread_fio *self) {
- assert(self);
- return slowread_file_qid(self->parent);
-}
-
-static uint32_t slowread_fio_iounit(struct slowread_fio *self) {
- assert(self);
- return 0;
-}
-
-static uint32_t slowread_fio_pwrite(struct slowread_fio *LM_UNUSED(self),
- struct lib9p_srv_ctx *LM_UNUSED(ctx),
- void *LM_UNUSED(buf), uint32_t LM_UNUSED(byte_count),
- uint64_t LM_UNUSED(offset)) {
- assert_notreached("not writable");
-}
-static void slowread_fio_pread(struct slowread_fio *self, struct lib9p_srv_ctx *ctx,
- uint32_t byte_count, uint64_t LM_UNUSED(byte_offset),
- struct iovec *ret) {
- assert(self);
- assert(ctx);
- assert(ret);
-
- while (!lib9p_srv_flush_requested(ctx))
- cr_yield();
- if (self->parent->flushable)
- lib9p_srv_acknowledge_flush(ctx);
- else
- *ret = (struct iovec){
- .iov_base = "Sloth\n",
- .iov_len = 6 < byte_count ? 6 : byte_count,
- };
-}
diff --git a/lib9p/tests/test_server/fs_slowread.h b/lib9p/tests/test_server/fs_slowread.h
deleted file mode 100644
index ef4b65f..0000000
--- a/lib9p/tests/test_server/fs_slowread.h
+++ /dev/null
@@ -1,22 +0,0 @@
-/* lib9p/tests/test_server/fs_slowread.h - slowread API endpoint
- *
- * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-
-#ifndef _LIB9P_TESTS_TEST_SERVER_FS_SLOWREAD_H_
-#define _LIB9P_TESTS_TEST_SERVER_FS_SLOWREAD_H_
-
-#include <util9p/static.h>
-#include <libhw/host_net.h>
-
-struct slowread_file {
- char *name;
- uint64_t pathnum;
-
- bool flushable;
-};
-LO_IMPLEMENTATION_H(lib9p_srv_file, struct slowread_file, slowread_file);
-#define lo_box_slowread_file_as_lib9p_srv_file(obj) util9p_box(slowread_file, obj)
-
-#endif /* _LIB9P_TESTS_TEST_SERVER_FS_SLOWREAD_H_ */
diff --git a/lib9p/tests/test_server/fs_whoami.c b/lib9p/tests/test_server/fs_whoami.c
index 560e31f..653ac4b 100644
--- a/lib9p/tests/test_server/fs_whoami.c
+++ b/lib9p/tests/test_server/fs_whoami.c
@@ -5,7 +5,9 @@
*/
#include <stdio.h> /* for snprintf() */
-#include <stdlib.h> /* for malloc(), realloc(), free() */
+#include <stdlib.h> /* for realloc(), free() */
+
+#include <libmisc/alloc.h>
#include "fs_whoami.h"
@@ -89,7 +91,7 @@ static lo_interface lib9p_srv_fio whoami_file_fopen(struct whoami_file *self, st
assert(self);
assert(ctx);
- struct whoami_fio *ret = malloc(sizeof(struct whoami_fio));
+ struct whoami_fio *ret = heap_alloc(1, struct whoami_fio);
ret->parent = self;
ret->buf_len = 0;
ret->buf = NULL;
diff --git a/lib9p/tests/test_server/main.c b/lib9p/tests/test_server/main.c
index 0705747..d7819eb 100644
--- a/lib9p/tests/test_server/main.c
+++ b/lib9p/tests/test_server/main.c
@@ -19,8 +19,8 @@
#include <util9p/static.h>
#include "static.h"
+#include "fs_flush.h"
#include "fs_shutdown.h"
-#include "fs_slowread.h"
#include "fs_whoami.h"
/* configuration **************************************************************/
@@ -76,11 +76,12 @@ struct lib9p_srv_file root =
API_FILE(5, "shutdown", shutdown,
.listeners = globals.listeners,
.nlisteners = LM_ARRAY_LEN(globals.listeners)),
- API_FILE(6, "slowread", slowread,
- .flushable = false),
- API_FILE(7, "slowread-flushable", slowread,
- .flushable = true),
API_FILE(8, "whoami", whoami),
+ API_FILE(9, "flush-read", flush, .flush_cnt=1, .flush_behavior=FLUSH_READ),
+ API_FILE(10, "flush-error", flush, .flush_cnt=1, .flush_behavior=FLUSH_ERROR),
+ API_FILE(11, "flush-silent", flush, .flush_cnt=1, .flush_behavior=FLUSH_SILENT),
+ API_FILE(12, "flush-slowsilent", flush, .flush_cnt=2, .flush_behavior=FLUSH_SILENT),
+ API_FILE(13, "flush-slowread", flush, .flush_cnt=0, .flush_behavior=FLUSH_READ),
);
static lo_interface lib9p_srv_file get_root(struct lib9p_srv_ctx *LM_UNUSED(ctx), struct lib9p_s LM_UNUSED(treename)) {
diff --git a/lib9p/tests/testclient-p9p b/lib9p/tests/testclient-p9p
index 9c9fb5e..9c9f9f2 100755
--- a/lib9p/tests/testclient-p9p
+++ b/lib9p/tests/testclient-p9p
@@ -25,9 +25,12 @@ out=$("${client[@]}" ls -l '')
expect_lines \
'd-r-xr-xr-x M 0 root root 0 Oct 7 2024 Documentation' \
'--r--r--r-- M 0 root root 166 Oct 7 2024 README.md' \
+ '--r--r--r-- M 0 root root 6 Oct 7 2024 flush-error' \
+ '--r--r--r-- M 0 root root 6 Oct 7 2024 flush-read' \
+ '--r--r--r-- M 0 root root 6 Oct 7 2024 flush-silent' \
+ '--r--r--r-- M 0 root root 6 Oct 7 2024 flush-slowread' \
+ '--r--r--r-- M 0 root root 6 Oct 7 2024 flush-slowsilent' \
'---w--w--w- M 0 root root 0 Oct 7 2024 shutdown' \
- '--r--r--r-- M 0 root root 6 Oct 7 2024 slowread' \
- '--r--r--r-- M 0 root root 6 Oct 7 2024 slowread-flushable' \
'--r--r--r-- M 0 root root 9 Oct 7 2024 whoami'
out=$("${client[@]}" ls -l 'Documentation/')
diff --git a/lib9p/tests/testclient-p9p.explog b/lib9p/tests/testclient-p9p.explog
index e5901d2..7f3953d 100644
--- a/lib9p/tests/testclient-p9p.explog
+++ b/lib9p/tests/testclient-p9p.explog
@@ -19,8 +19,8 @@
> Topen { tag=0 fid=1 mode=(MODE_READ) }
< Ropen { tag=0 qid={ type=(DIR) vers=1 path=1 } iounit=0 }
> Tread { tag=0 fid=1 offset=0 count=4096 }
-< Rread { tag=0 count=428 data=<bytedata> }
-> Tread { tag=0 fid=1 offset=428 count=4096 }
+< Rread { tag=0 count=648 data=<bytedata> }
+> Tread { tag=0 fid=1 offset=648 count=4096 }
< Rread { tag=0 count=0 data="" }
> Tclunk { tag=0 fid=1 }
< Rclunk { tag=0 }
diff --git a/lib9p/tests/testclient-sess.c b/lib9p/tests/testclient-sess.c
index ded70d1..561c0c9 100644
--- a/lib9p/tests/testclient-sess.c
+++ b/lib9p/tests/testclient-sess.c
@@ -170,29 +170,62 @@ int main(int argc, char *argv[]) {
recv9p(); /* Rattach */
/* flush, but original response comes back first */
- wname[0] = lib9p_str("slowread"); send9p(Twalk, .tag=0, .fid=0, .newfid=1, .nwname=1, .wname=wname);
+ wname[0] = lib9p_str("flush-read"); send9p(Twalk, .tag=0, .fid=0, .newfid=1, .nwname=1, .wname=wname);
recv9p(); /* Rwalk */
send9p(Topen, .tag=0, .fid=1, .mode=LIB9P_O_MODE_READ);
recv9p(); /* Ropen */
- send9p(Tread, .tag=1, .fid=1, .offset=0, .count=6);
- send9p(Tflush, .tag=2, .oldtag=1);
+ send9p(Tread, .tag=0, .fid=1, .offset=0, .count=10);
+ send9p(Tflush, .tag=1, .oldtag=0);
recv9p(); /* Rread */
recv9p(); /* Rflush */
/* flush, original request is aborted with error */
- wname[0] = lib9p_str("slowread-flushable"); send9p(Twalk, .tag=1, .fid=0, .newfid=2, .nwname=1, .wname=wname);
+ wname[0] = lib9p_str("flush-error"); send9p(Twalk, .tag=0, .fid=0, .newfid=2, .nwname=1, .wname=wname);
recv9p(); /* Rwalk */
send9p(Topen, .tag=0, .fid=2, .mode=LIB9P_O_MODE_READ);
recv9p(); /* Ropen */
- send9p(Tread, .tag=1, .fid=2, .offset=0, .count=6);
- send9p(Tflush, .tag=2, .oldtag=1);
+ send9p(Tread, .tag=0, .fid=2, .offset=0, .count=10);
+ send9p(Tflush, .tag=1, .oldtag=0);
recv9p(); /* Rerror */
recv9p(); /* Rflush */
+ /* flush, original request is aborted without error */
+ wname[0] = lib9p_str("flush-silent"); send9p(Twalk, .tag=0, .fid=0, .newfid=3, .nwname=1, .wname=wname);
+ recv9p(); /* Rwalk */
+ send9p(Topen, .tag=0, .fid=3, .mode=LIB9P_O_MODE_READ);
+ recv9p(); /* Ropen */
+ send9p(Tread, .tag=0, .fid=3, .offset=0, .count=10);
+ send9p(Tflush, .tag=1, .oldtag=0);
+ recv9p(); /* Rflush */
+
+ /* multiflush, original request is aborted without error */
+ wname[0] = lib9p_str("flush-slowsilent"); send9p(Twalk, .tag=0, .fid=0, .newfid=4, .nwname=1, .wname=wname);
+ recv9p(); /* Rwalk */
+ send9p(Topen, .tag=0, .fid=4, .mode=LIB9P_O_MODE_READ);
+ recv9p(); /* Ropen */
+ send9p(Tread, .tag=0, .fid=4, .offset=0, .count=10);
+ send9p(Tflush, .tag=1, .oldtag=0);
+ send9p(Tflush, .tag=2, .oldtag=0);
+ recv9p(); /* Rflush */
+
+ /* flush, but flush is flushed */
+ wname[0] = lib9p_str("flush-slowread"); send9p(Twalk, .tag=0, .fid=0, .newfid=5, .nwname=1, .wname=wname);
+ recv9p(); /* Rwalk */
+ send9p(Topen, .tag=0, .fid=5, .mode=LIB9P_O_MODE_READ);
+ recv9p(); /* Ropen */
+ send9p(Tread, .tag=0, .fid=5, .offset=0, .count=10);
+ send9p(Tflush, .tag=1, .oldtag=0);
+ send9p(Tflush, .tag=2, .oldtag=1);
+ recv9p(); /* Rflush */
+ recv9p(); /* Rread */
+
/* flush, unknown tag */
send9p(Tflush, .tag=0, .oldtag=99);
recv9p(); /* Rflush */
+ /* flushed by Tversion */
+ send9p(Tread, .tag=0, .fid=3, .offset=0, .count=10);
+
/* shutdown ***********************************************************/
send9p(Tversion, .tag=0, .max_msg_size=(8*1024), .version=lib9p_str("9P2000"));
recv9p(); /* Rversion */
diff --git a/lib9p/tests/testclient-sess.explog b/lib9p/tests/testclient-sess.explog
index 3e2209a..74a2cd7 100644
--- a/lib9p/tests/testclient-sess.explog
+++ b/lib9p/tests/testclient-sess.explog
@@ -87,29 +87,62 @@
< Rattach { tag=0 qid={ type=(DIR) vers=1 path=1 } }
# flush, but original response comes back first
-> Twalk { tag=0 fid=0 newfid=1 nwname=1 wname=[ "slowread" ] }
-< Rwalk { tag=0 nwqid=1 wqid=[ { type=(0) vers=1 path=6 } ] }
+> Twalk { tag=0 fid=0 newfid=1 nwname=1 wname=[ "flush-read" ] }
+< Rwalk { tag=0 nwqid=1 wqid=[ { type=(0) vers=1 path=9 } ] }
> Topen { tag=0 fid=1 mode=(MODE_READ) }
-< Ropen { tag=0 qid={ type=(0) vers=1 path=6 } iounit=0 }
-> Tread { tag=1 fid=1 offset=0 count=6 }
-> Tflush { tag=2 oldtag=1 }
-< Rread { tag=1 count=6 data="Sloth\n" }
+< Ropen { tag=0 qid={ type=(0) vers=1 path=9 } iounit=0 }
+> Tread { tag=0 fid=1 offset=0 count=10 }
+> Tflush { tag=1 oldtag=0 }
+< Rread { tag=0 count=6 data="Sloth\n" }
+< Rflush { tag=1 }
+
+# flush, original request is aborted with error
+> Twalk { tag=0 fid=0 newfid=2 nwname=1 wname=[ "flush-error" ] }
+< Rwalk { tag=0 nwqid=1 wqid=[ { type=(0) vers=1 path=10 } ] }
+> Topen { tag=0 fid=2 mode=(MODE_READ) }
+< Ropen { tag=0 qid={ type=(0) vers=1 path=10 } iounit=0 }
+> Tread { tag=0 fid=2 offset=0 count=10 }
+> Tflush { tag=1 oldtag=0 }
+< Rerror { tag=0 errstr="request canceled by flush" errnum=L_ECANCELED }
+< Rflush { tag=1 }
+
+# flush, original request is aborted without error
+> Twalk { tag=0 fid=0 newfid=3 nwname=1 wname=[ "flush-silent" ] }
+< Rwalk { tag=0 nwqid=1 wqid=[ { type=(0) vers=1 path=11 } ] }
+> Topen { tag=0 fid=3 mode=(MODE_READ) }
+< Ropen { tag=0 qid={ type=(0) vers=1 path=11 } iounit=0 }
+> Tread { tag=0 fid=3 offset=0 count=10 }
+> Tflush { tag=1 oldtag=0 }
+< Rflush { tag=1 }
+
+# multiflush, original request is aborted without error
+> Twalk { tag=0 fid=0 newfid=4 nwname=1 wname=[ "flush-slowsilent" ] }
+< Rwalk { tag=0 nwqid=1 wqid=[ { type=(0) vers=1 path=12 } ] }
+> Topen { tag=0 fid=4 mode=(MODE_READ) }
+< Ropen { tag=0 qid={ type=(0) vers=1 path=12 } iounit=0 }
+> Tread { tag=0 fid=4 offset=0 count=10 }
+> Tflush { tag=1 oldtag=0 }
+> Tflush { tag=2 oldtag=0 }
< Rflush { tag=2 }
-# flush, succeeds
-> Twalk { tag=1 fid=0 newfid=2 nwname=1 wname=[ "slowread-flushable" ] }
-< Rwalk { tag=1 nwqid=1 wqid=[ { type=(0) vers=1 path=7 } ] }
-> Topen { tag=0 fid=2 mode=(MODE_READ) }
-< Ropen { tag=0 qid={ type=(0) vers=1 path=7 } iounit=0 }
-> Tread { tag=1 fid=2 offset=0 count=6 }
+# flush, but flush is flushed
+> Twalk { tag=0 fid=0 newfid=5 nwname=1 wname=[ "flush-slowread" ] }
+< Rwalk { tag=0 nwqid=1 wqid=[ { type=(0) vers=1 path=13 } ] }
+> Topen { tag=0 fid=5 mode=(MODE_READ) }
+< Ropen { tag=0 qid={ type=(0) vers=1 path=13 } iounit=0 }
+> Tread { tag=0 fid=5 offset=0 count=10 }
+> Tflush { tag=1 oldtag=0 }
> Tflush { tag=2 oldtag=1 }
< Rflush { tag=2 }
-< Rerror { tag=1 errstr="request canceled by flush" errnum=L_ECANCELED }
+< Rread { tag=0 count=6 data="Sloth\n" }
# flush, unknown tag
> Tflush { tag=0 oldtag=99 }
< Rflush { tag=0 }
+# flushed by Tversion
+> Tread { tag=0 fid=3 offset=0 count=10 }
+
# shutdown #####################################################################
> Tversion { tag=0 max_msg_size=8192 version="9P2000" }
< Rversion { tag=0 max_msg_size=4120 version="9P2000" }
diff --git a/libcr/tests/test_matrix.c b/libcr/tests/test_matrix.c
index 1f23455..eaa4bdc 100644
--- a/libcr/tests/test_matrix.c
+++ b/libcr/tests/test_matrix.c
@@ -1,6 +1,6 @@
/* libcr/tests/test_matrix.c - Tests for libcr
*
- * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
@@ -8,14 +8,14 @@
int a = 1;
-COROUTINE cr_init(void *) {
+COROUTINE init_cr(void *) {
cr_begin();
a = 2;
cr_end();
}
int main() {
- coroutine_add("init", cr_init, NULL);
+ coroutine_add("init", init_cr, NULL);
coroutine_main();
if (a != 2)
return 1;
diff --git a/libcr_ipc/chan.c b/libcr_ipc/chan.c
index 6ccfa44..b7ecfc8 100644
--- a/libcr_ipc/chan.c
+++ b/libcr_ipc/chan.c
@@ -4,60 +4,22 @@
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
-#include <alloca.h> /* for alloca() */
#include <string.h> /* for memcpy() */
#include <libcr/coroutine.h> /* for cid_t, cr_* */
#include <libmisc/assert.h>
#include <libmisc/rand.h>
+#define IMPLEMENTATION_FOR_LIBCR_IPC_CHAN_H YES
#include <libcr_ipc/chan.h>
-/* base channels **************************************************************/
-
-struct cr_chan_waiter {
- lm_dll_node;
+struct _cr_select_waiter {
cid_t cid;
- void *val_ptr;
- void (*dequeue)(void *, size_t);
- void *dequeue_arg1;
- size_t dequeue_arg2;
-};
-
-void cr_chan_dequeue(void *_ch, size_t) {
- struct _cr_chan *ch = _ch;
- lm_dll_pop_from_front(&ch->waiters);
-}
-
-void _cr_chan_xfer(enum _cr_chan_waiter_typ self_typ, struct _cr_chan *ch, void *val_ptr, size_t val_size) {
- assert(ch);
- assert(val_ptr);
+ struct _cr_select_arg_list_node *arg_vec;
+ size_t arg_cnt;
- if (ch->waiters.front && ch->waiter_typ != self_typ) { /* non-blocking fast-path */
- /* Copy. */
- struct cr_chan_waiter *front = lm_dll_node_cast(struct cr_chan_waiter, ch->waiters.front);
- if (self_typ == _CR_CHAN_SENDER)
- memcpy(front->val_ptr, val_ptr, val_size);
- else
- memcpy(val_ptr, front->val_ptr, val_size);
- cr_unpause(front->cid);
- front->dequeue(front->dequeue_arg1,
- front->dequeue_arg2);
- cr_yield();
- } else { /* blocking slow-path */
- struct cr_chan_waiter self = {
- .cid = cr_getcid(),
- .val_ptr = val_ptr,
- .dequeue = cr_chan_dequeue,
- .dequeue_arg1 = ch,
- };
- lm_dll_push_to_rear(&ch->waiters, &self);
- ch->waiter_typ = self_typ;
- cr_pause_and_yield();
- }
-}
-
-/* select *********************************************************************/
+ size_t ret;
+};
enum cr_select_class {
CR_SELECT_CLASS_DEFAULT,
@@ -65,40 +27,26 @@ enum cr_select_class {
CR_SELECT_CLASS_NONBLOCK,
};
-struct cr_select_waiters {
- size_t cnt;
- struct cr_select_arg *args;
- struct cr_chan_waiter *nodes;
-};
-
-static inline enum cr_select_class cr_select_getclass(struct cr_select_arg arg) {
- switch (arg.op) {
+static inline enum cr_select_class cr_select_getclass(struct _cr_select_arg *arg) {
+ switch (arg->op) {
case _CR_SELECT_OP_RECV:
- if (arg.ch->waiters.front && arg.ch->waiter_typ == _CR_CHAN_SENDER)
+ if (arg->ch->waiters.front && arg->ch->waiters.front->val.op == _CR_SELECT_OP_SEND)
return CR_SELECT_CLASS_NONBLOCK;
else
return CR_SELECT_CLASS_BLOCKING;
case _CR_SELECT_OP_SEND:
- if (arg.ch->waiters.front && arg.ch->waiter_typ == _CR_CHAN_RECVER)
+ if (arg->ch->waiters.front && arg->ch->waiters.front->val.op == _CR_SELECT_OP_RECV)
return CR_SELECT_CLASS_NONBLOCK;
else
return CR_SELECT_CLASS_BLOCKING;
case _CR_SELECT_OP_DEFAULT:
return CR_SELECT_CLASS_DEFAULT;
default:
- assert_notreached("invalid arg.op");
+ assert_notreached("invalid arg->op");
}
}
-void cr_select_dequeue(void *_waiters, size_t idx) {
- struct cr_select_waiters *waiters = _waiters;
- for (size_t i = 0; i < waiters->cnt; i++)
- lm_dll_remove(&(waiters->args[i].ch->waiters),
- &(waiters->nodes[i]));
- waiters->cnt = idx;
-}
-
-size_t cr_select_v(size_t arg_cnt, struct cr_select_arg arg_vec[]) {
+size_t cr_select_v(size_t arg_cnt, struct _cr_select_arg_list_node arg_vec[]) {
size_t cnt_blocking = 0;
size_t cnt_nonblock = 0;
size_t cnt_default = 0;
@@ -108,7 +56,7 @@ size_t cr_select_v(size_t arg_cnt, struct cr_select_arg arg_vec[]) {
cr_assert_in_coroutine();
for (size_t i = 0; i < arg_cnt; i++) {
- switch (cr_select_getclass(arg_vec[i])) {
+ switch (cr_select_getclass(&arg_vec[i].val)) {
case CR_SELECT_CLASS_BLOCKING:
cnt_blocking++;
break;
@@ -122,46 +70,65 @@ size_t cr_select_v(size_t arg_cnt, struct cr_select_arg arg_vec[]) {
}
if (cnt_nonblock) {
- size_t choice = rand_uint63n(cnt_nonblock);
- for (size_t i = 0, seen = 0; i < arg_cnt; i++) {
- if (cr_select_getclass(arg_vec[i]) == CR_SELECT_CLASS_NONBLOCK) {
- if (seen == choice) {
- _cr_chan_xfer(arg_vec[i].op == _CR_SELECT_OP_RECV
- ? _CR_CHAN_RECVER
- : _CR_CHAN_SENDER,
- arg_vec[i].ch,
- arg_vec[i].val_ptr,
- arg_vec[i].val_siz);
- return i;
- }
+ size_t choice_among_nonblock = rand_uint63n(cnt_nonblock);
+ size_t choice_among_all = arg_cnt;
+ for (size_t i = 0, seen = 0; i < choice_among_all; i++) {
+ if (cr_select_getclass(&arg_vec[i].val) == CR_SELECT_CLASS_NONBLOCK) {
+ if (seen == choice_among_nonblock)
+ choice_among_all = i;
seen++;
}
}
- assert_notreached("should have returned from inside for() loop");
+ assert(choice_among_all < arg_cnt);
+
+ struct _cr_select_arg *this = &arg_vec[choice_among_all].val;
+ assert(this->ch->waiters.front);
+ struct _cr_select_arg *other = &this->ch->waiters.front->val;
+ assert(this->val_siz == other->val_siz);
+ assert(this->ch == other->ch);
+ switch (this->op) {
+ case _CR_SELECT_OP_SEND:
+ assert(other->op == _CR_SELECT_OP_RECV);
+ memcpy(other->val_ptr, this->val_ptr, this->val_siz);
+ break;
+ case _CR_SELECT_OP_RECV:
+ assert(other->op == _CR_SELECT_OP_SEND);
+ memcpy(this->val_ptr, other->val_ptr, this->val_siz);
+ break;
+ case _CR_SELECT_OP_DEFAULT:
+ assert_notreached("_CR_SELECT_OP_DEFAULT is not CR_SELECT_CLASS_NONBLOCK");
+ }
+ struct _cr_select_waiter *waiter = other->waiter;
+ for (size_t i = 0; i < waiter->arg_cnt; i++) {
+ waiter->arg_vec[i].val.ch->nwaiters--;
+ dlist_remove(&waiter->arg_vec[i].val.ch->waiters, &waiter->arg_vec[i]);
+ if (&waiter->arg_vec[i].val == other)
+ waiter->ret = i;
+ }
+ cr_unpause(waiter->cid);
+ cr_yield();
+ return choice_among_all;
}
if (cnt_default) {
for (size_t i = 0; i < arg_cnt; i++)
- if (cr_select_getclass(arg_vec[i]) == CR_SELECT_CLASS_DEFAULT)
+ if (cr_select_getclass(&arg_vec[i].val) == CR_SELECT_CLASS_DEFAULT)
return i;
assert_notreached("should have returned from inside for() loop");
}
- struct cr_select_waiters waiters = {
- .cnt = arg_cnt,
- .args = arg_vec,
- .nodes = alloca(sizeof(struct cr_chan_waiter) * arg_cnt),
+ assert(cnt_blocking && cnt_blocking == arg_cnt);
+
+ struct _cr_select_waiter waiter = {
+ .cid = cr_getcid(),
+ .arg_vec = arg_vec,
+ .arg_cnt = arg_cnt,
};
for (size_t i = 0; i < arg_cnt; i++) {
- waiters.nodes[i] = (struct cr_chan_waiter){
- .cid = cr_getcid(),
- .val_ptr = arg_vec[i].val_ptr,
- .dequeue = cr_select_dequeue,
- .dequeue_arg1 = &waiters,
- .dequeue_arg2 = i,
- };
- lm_dll_push_to_rear(&arg_vec[i].ch->waiters, &waiters.nodes[i]);
+ arg_vec[i].val.waiter = &waiter;
+ arg_vec[i].val.ch->nwaiters++;
+ dlist_push_to_rear(&arg_vec[i].val.ch->waiters, &arg_vec[i]);
}
cr_pause_and_yield();
- return waiters.cnt;
+ return waiter.ret;
}
diff --git a/libcr_ipc/include/libcr_ipc/chan.h b/libcr_ipc/include/libcr_ipc/chan.h
index 80acdb8..853b4ad 100644
--- a/libcr_ipc/include/libcr_ipc/chan.h
+++ b/libcr_ipc/include/libcr_ipc/chan.h
@@ -10,16 +10,13 @@
#include <stdbool.h> /* for bool */
#include <stddef.h> /* for size_t */
-#include <libmisc/linkedlist.h> /* for lm_dll_root */
-#include <libmisc/macro.h> /* for LM_CAT2_() */
+#include <libmisc/linkedlist.h> /* for DLIST_DECLARE() */
+#include <libmisc/private.h>
/* base channels **************************************************************/
/**
- * CR_CHAN_DECLARE(NAME, VAL_T) declares the following type and
- * methods:
- *
- * type:
+ * CR_CHAN_DECLARE(NAME, VAL_T) declares the following type:
*
* / **
* * A NAME##_t is a fair unbuffered channel that transports
@@ -32,143 +29,163 @@
* * something from an interrupt handler.
* * /
* typedef ... NAME##_t;
+ */
+#define CR_CHAN_DECLARE(NAME, VAL_T) \
+ typedef struct { \
+ struct _cr_chan core; \
+ VAL_T val_typ[0]; \
+ } NAME##_t
+
+/**
+ * cr_chan_send(ch, val) sends `val` over `ch`.
*
- * methods:
+ * @runs_in coroutine
+ * @cr_pauses maybe
+ * @cr_yields always
*
- * / **
- * * NAME##_send(ch, val) sends `val` over `ch`.
- * *
- * * @runs_in coroutine
- * * @cr_pauses maybe
- * * @cr_yields always
- * * /
- * void NAME##_send(NAME##_t *ch, VAL_T val);
+ * void cr_chan_send(NAME##_t *ch, VAL_T val);
+ */
+#define cr_chan_send(CH, VAL) do { \
+ typeof((CH)->val_typ[0]) _val_lvalue = VAL; \
+ (void)cr_select_l(CR_SELECT_SEND(CH, &_val_lvalue)); \
+} while(0)
+
+/**
+ * cr_chan_recv(ch) reads and returns a value from ch.
*
- * / **
- * * NAME##_recv(ch) reads and returns a value from ch.
- * *
- * * @runs_in coroutine
- * * @cr_pauses maybe
- * * @cr_yields always
- * * /
- * VAL_T NAME##_recv(NAME##_t *ch);
+ * @runs_in coroutine
+ * @cr_pauses maybe
+ * @cr_yields always
*
- * / **
- * * NAME##_can_send(ch) returns whether NAME##_send(ch, val)
- * * would run without pausing.
- * *
- * * @runs_in coroutine
- * * @cr_pauses never
- * * @cr_yields never
- * * /
- * bool NAME##_can_send(NAME##_t *ch);
+ * VAL_T cr_chan_recv(NAME##_T ch);
+ */
+#define cr_chan_recv(CH) ({ \
+ typeof((CH)->val_typ[0]) _val_lvalue; \
+ (void)cr_select_l(CR_SELECT_RECV(CH, &_val_lvalue)); \
+ _val_lvalue; \
+})
+
+/**
+ * cr_chan_can_send(ch) returns whether cr_chan_send(ch, val) would
+ * run without pausing.
*
- * / **
- * * NAME##_can_recv(ch) returns whether NAME##_recv(ch) would
- * * return without pausing.
- * *
- * * @runs_in coroutine
- * * @cr_pauses never
- * * @cr_yields never
- * * /
- * NAME##_can_recv(NAME##_t *ch);
+ * @runs_in coroutine
+ * @cr_pauses never
+ * @cr_yields never
+ *
+ * bool cr_chan_can_send(NAME##_t *ch);
*/
-#define CR_CHAN_DECLARE(NAME, VAL_T) \
- typedef struct { \
- struct _cr_chan core; \
- VAL_T vals[0]; \
- } NAME##_t; \
- \
- static inline void NAME##_send(NAME##_t *ch, VAL_T val) { \
- cr_assert_in_coroutine(); \
- _cr_chan_xfer(_CR_CHAN_SENDER, &ch->core, &val, sizeof(val)); \
- } \
- \
- static inline VAL_T NAME##_recv(NAME##_t *ch) { \
- cr_assert_in_coroutine(); \
- VAL_T val; \
- _cr_chan_xfer(_CR_CHAN_RECVER, &ch->core, &val, sizeof(val)); \
- return val; \
- } \
- \
- static inline bool NAME##_can_send(NAME##_t *ch) { \
- cr_assert_in_coroutine(); \
- return ch->core.waiters.front && \
- ch->core.waiter_typ == _CR_CHAN_RECVER; \
- } \
- \
- static inline bool NAME##_can_recv(NAME##_t *ch) { \
- cr_assert_in_coroutine(); \
- return ch->core.waiters.front && \
- ch->core.waiter_typ == _CR_CHAN_SENDER; \
- } \
- \
- extern int LM_CAT2_(_CR_CHAN_FORCE_SEMICOLON_, __COUNTER__)
-
-enum _cr_chan_waiter_typ {
- _CR_CHAN_SENDER,
- _CR_CHAN_RECVER,
-};
+#define cr_chan_can_send(CH) ({ \
+ cr_assert_in_coroutine(); \
+ (bool)((CH)->core.waiters.front && \
+ (CH)->core.waiters.front->val.op == _CR_SELECT_OP_RECV); \
+})
+/**
+ * cr_chan_can_recv(ch) returns whether cr_chan_recv(ch) would return
+ * without pausing.
+ *
+ * @runs_in coroutine
+ * @cr_pauses never
+ * @cr_yields never
+ *
+ * bool cr_chan_can_recv(NAME##_t *ch);
+ */
+#define cr_chan_can_recv(CH) ({ \
+ cr_assert_in_coroutine(); \
+ (bool)((CH)->core.waiters.front && \
+ (CH)->core.waiters.front->val.op == _CR_SELECT_OP_SEND); \
+})
+
+/**
+ * cr_chan_num_waiters(ch) returns the number of coroutines currently
+ * blocked on the channel.
+ *
+ * @runs_in coroutine
+ * @cr_pauses never
+ * @cr_yields never
+ *
+ * size_t cr_chan_num_waiters(NAME##_t *ch);
+ */
+#define cr_chan_num_waiters(CH) ({ \
+ cr_assert_in_coroutine(); \
+ ((CH)->core.nwaiters); \
+})
+
+DLIST_DECLARE(_cr_select_arg_list);
struct _cr_chan {
- enum _cr_chan_waiter_typ waiter_typ;
- lm_dll_root waiters;
+ struct _cr_select_arg_list waiters;
+ size_t nwaiters;
};
-void _cr_chan_xfer(enum _cr_chan_waiter_typ self_typ, struct _cr_chan *ch, void *val_ptr, size_t val_size);
-
/* cr_select arguments ********************************************************/
/**
* Do not populate cr_select_arg yourself; use the
* CR_SELECT_{RECV,SEND,DEFAULT} macros.
*/
-struct cr_select_arg {
+struct _cr_select_waiter;
+struct _cr_select_arg {
enum {
_CR_SELECT_OP_RECV,
_CR_SELECT_OP_SEND,
_CR_SELECT_OP_DEFAULT,
- } op;
- struct _cr_chan *ch;
- void *val_ptr;
- size_t val_siz;
+ } op;
+ struct _cr_chan *ch;
+ void *val_ptr;
+ size_t val_siz;
+ BEGIN_PRIVATE(LIBCR_IPC_CHAN_H);
+ struct _cr_select_waiter *waiter;
+ END_PRIVATE(LIBCR_IPC_CHAN_H);
};
+DLIST_DECLARE_NODE(_cr_select_arg_list, struct _cr_select_arg);
+#define cr_select_arg _cr_select_arg_list_node
+
+#define CR_SELECT_RECV(CH, VALP) ((struct cr_select_arg){ .val = { \
+ .op = _CR_SELECT_OP_RECV, \
+ .ch = &((CH)->core), \
+ /* The _valp temporary variable is to get the compiler to check that \
+ * the types are compatible. */ \
+ .val_ptr = ({ typeof((CH)->val_typ[0]) *_valp = VALP; _valp; }), \
+ .val_siz = sizeof((CH)->val_typ[0]), \
+}})
-#define CR_SELECT_RECV(CH, VALP) \
- /* The _valp temporary variable is to get the compiler to check that \
- * the types are compatible. */ \
- ((struct cr_select_arg){ \
- .op = _CR_SELECT_OP_RECV, \
- .ch = &((CH)->core), \
- .val_ptr = ({ typeof((CH)->vals[0]) *_valp = VALP; _valp; }), \
- .val_siz = sizeof((CH)->vals[0]), \
- })
/* BUG: It's bogus that CR_SELECT_SEND takes VALP instead of VAL, but
* since we need an address, taking VAL would introduce uncomfortable
* questions about where VAL sits on the stack. */
-#define CR_SELECT_SEND(CH, VALP) \
- /* The _valp temporary variable is to get the compiler to check that \
- * the types are compatible. */ \
- ((struct cr_select_arg){ \
- .op = _CR_SELECT_OP_SEND, \
- .ch = &((CH)->core), \
- .val_ptr = ({ typeof((CH)->vals[0]) *_valp = VALP; _valp; }), \
- .val_siz = sizeof((CH)->vals[0]), \
- })
-#define CR_SELECT_DEFAULT \
- ((struct cr_select_arg){ \
- .op = _CR_SELECT_OP_DEFAULT, \
- })
+#define CR_SELECT_SEND(CH, VALP) ((struct cr_select_arg){ .val = { \
+ .op = _CR_SELECT_OP_SEND, \
+ .ch = &((CH)->core), \
+ /* The _valp temporary variable is to get the compiler to check that \
+ * the types are compatible. */ \
+ .val_ptr = ({ typeof((CH)->val_typ[0]) *_valp = VALP; _valp; }), \
+ .val_siz = sizeof((CH)->val_typ[0]), \
+}})
+
+#define CR_SELECT_DEFAULT ((struct cr_select_arg){ .val = { \
+ .op = _CR_SELECT_OP_DEFAULT, \
+}})
/* cr_select_v(arg_cnt, arg_vec) **********************************************/
+/**
+ * @runs_in coroutine
+ * @cr_pauses maybe
+ * @cr_yields always
+ */
size_t cr_select_v(size_t arg_cnt, struct cr_select_arg arg_vec[]);
/* cr_select_l(arg1, arg2, arg3, ...) ******************************************/
-#define cr_select_l(...) ({ \
- struct cr_select_arg _cr_select_args[] = { __VA_ARGS__ }; \
- cr_select_v(sizeof(_cr_select_args)/sizeof(_cr_select_args[0])); \
+/**
+ * @runs_in coroutine
+ * @cr_pauses maybe
+ * @cr_yields always
+ */
+#define cr_select_l(...) ({ \
+ struct cr_select_arg _cr_select_args[] = { __VA_ARGS__ }; \
+ cr_select_v(sizeof(_cr_select_args)/sizeof(_cr_select_args[0]), \
+ _cr_select_args); \
})
#endif /* _LIBCR_IPC_CHAN_H_ */
diff --git a/libcr_ipc/include/libcr_ipc/mutex.h b/libcr_ipc/include/libcr_ipc/mutex.h
index 0f3c9c2..e5f43c8 100644
--- a/libcr_ipc/include/libcr_ipc/mutex.h
+++ b/libcr_ipc/include/libcr_ipc/mutex.h
@@ -9,9 +9,11 @@
#include <stdbool.h> /* for bool */
-#include <libmisc/linkedlist.h>
+#include <libmisc/linkedlist.h> /* for SLIST_DECLARE() */
#include <libmisc/private.h>
+SLIST_DECLARE(_cr_mutex_waiter_list);
+
/**
* A cr_mutex_t is a fair mutex.
*
@@ -22,8 +24,8 @@
*/
typedef struct {
BEGIN_PRIVATE(LIBCR_IPC_MUTEX_H);
- bool locked;
- lm_sll_root waiters;
+ bool locked;
+ struct _cr_mutex_waiter_list waiters;
END_PRIVATE(LIBCR_IPC_MUTEX_H);
} cr_mutex_t;
diff --git a/libcr_ipc/include/libcr_ipc/rpc.h b/libcr_ipc/include/libcr_ipc/rpc.h
index f091685..ecf48cf 100644
--- a/libcr_ipc/include/libcr_ipc/rpc.h
+++ b/libcr_ipc/include/libcr_ipc/rpc.h
@@ -9,14 +9,10 @@
#include <stdbool.h> /* for bool */
-#include <libmisc/linkedlist.h> /* for lm_sll_root */
-#include <libmisc/macro.h> /* for LM_CAT2_() */
+#include <libmisc/linkedlist.h> /* for SLIST_DECLARE() */
/**
- * CR_RPC_DECLARE(NAME, REQ_T, RESP_T) declares the following types
- * and methods:
- *
- * type:
+ * CR_RPC_DECLARE(NAME, REQ_T, RESP_T) declares the following types:
*
* / **
* * A NAME##_t is a fair rpc-channel on which the requester submits a
@@ -38,114 +34,109 @@
* * the response RESP_T to the correct requester. `REQ_T req` is the
* * only public member.
* typedef struct { REQ_T req; ... } NAME##_req_t;
+ */
+#define CR_RPC_DECLARE(NAME, REQ_T, RESP_T) \
+ typedef struct { \
+ REQ_T req; \
+ \
+ RESP_T *_resp; /* where to write resp to */ \
+ cid_t _requester; \
+ } NAME##_req_t; \
+ \
+ typedef struct { \
+ struct _cr_rpc core; \
+ NAME##_req_t handle_typ[0]; \
+ } NAME##_t
+
+/* Methods for NAME##_t *******************************************************/
+
+/**
+ * cr_rpc_send_req(ch, req) submits the `req` request over `ch` and
+ * returns the response.
*
- * methods:
+ * @runs_in coroutine
+ * @cr_pauses always
+ * @cr_yields always
*
- * / **
- * * NAME##_send_req(ch, req) submits the `req` request over `ch` and
- * * returns the response.
- * *
- * * @runs_in coroutine
- * * @cr_pauses always
- * * @cr_yields always
- * * /
- * RESP_T NAME##_send_req(NAME##_t *ch, REQ_T req);
+ * RESP_T cr_rpc_send_req(NAME##_t *ch, REQ_T req);
+ */
+#define cr_rpc_send_req(CH, REQ) ({ \
+ cr_assert_in_coroutine(); \
+ typeof((CH)->handle_typ[0].req) _req_lvalue = REQ; \
+ typeof(*(CH)->handle_typ[0]._resp) _resp_lvalue; \
+ _cr_rpc_send_req(&(CH)->core, \
+ &_req_lvalue, sizeof(_req_lvalue), \
+ &_resp_lvalue); \
+ _resp_lvalue; \
+})
+
+/**
+ * cr_rpc_recv_req(ch) reads a request from ch, and returns a
+ * NAME##_req_t handle wrapping that request.
*
- * / **
- * * NAME##_recv_req(ch) reads a request from ch, and returns a
- * * NAME##_req_t handle wrapping that request.
- * *
- * * @runs_in coroutine
- * * @cr_pauses maybe
- * * @cr_yields maybe
- * * /
- * NAME##_req_t NAME##_recv_req(NAME##_t *ch);
+ * @runs_in coroutine
+ * @cr_pauses maybe
+ * @cr_yields maybe
*
- * / **
- * * NAME##_can_recv_req(ch) returns whether NAME##_recv_req(ch)
- * * would return without pausing.
- * *
- * * @runs_in coroutine
- * * @cr_pauses never
- * * @cr_yields never
- * * /
- * bool NAME##_can_recv_req(NAME##_t *ch);
+ * NAME##_req_t cr_rcp_recv_req(NAME##_t *ch);
+ */
+#define cr_rpc_recv_req(CH) ({ \
+ cr_assert_in_coroutine(); \
+ typeof((CH)->handle_typ[0]) ret; \
+ _cr_rpc_recv_req(&(CH)->core, \
+ &ret.req, sizeof(ret.req), \
+ (void **)&ret._resp, \
+ &ret._requester); \
+ ret; \
+})
+
+/**
+ * cr_rpc_can_recv_req(ch) returns whether NAME##_recv_req(ch)
+ * would return without pausing.
*
- * type:
+ * @runs_in coroutine
+ * @cr_pauses never
+ * @cr_yields never
*
- * / **
- * * A NAME##_req_t is a handle that wraps a REQ_T, and is a channel
- * * that a response may be written to.
- * * /
- * typedef ... NAME##_req_t;
+ * bool cr_rpc_can_recv_req(NAME##_t *ch);
+ */
+#define cr_rpc_can_recv_req(CH) ({ \
+ cr_assert_in_coroutine(); \
+ (bool)((CH)->core.waiters.front && \
+ (CH)->core.waiter_typ == _CR_RPC_REQUESTER); \
+})
+
+/* Methods for NAME##_req_t ***************************************************/
+
+/**
+ * cr_rpc_send_resp(req, resp) sends the given response to the given
+ * request.
*
- * methods:
+ * @runs_in coroutine
+ * @cr_pauses never
+ * @cr_yields always
*
- * / **
- * * cr_rpc_send_resp(req, resp) sends the given response to the given
- * * request.
- * *
- * * @runs_in coroutine
- * * @cr_pauses never
- * * @cr_yields always
- * * /
- * void NAME##_send_resp(NAME##_req_t req, RESP_T resp);
+ * void cr_rpc_send_resp(NAME##_req_t req, RESP_T resp);
*/
-#define CR_RPC_DECLARE(NAME, REQ_T, RESP_T) \
- typedef struct { \
- REQ_T req; \
- \
- RESP_T *_resp; /* where to write resp to */ \
- cid_t _requester; \
- } NAME##_req_t; \
- \
- typedef struct { \
- struct _cr_rpc core; \
- NAME##_req_t handle[0]; \
- } NAME##_t; \
- \
- static inline RESP_T NAME##_send_req(NAME##_t *ch, REQ_T req) { \
- cr_assert_in_coroutine(); \
- RESP_T resp; \
- _cr_rpc_send_req(&ch->core, \
- &req, sizeof(req), \
- &resp); \
- return resp; \
- } \
- \
- static inline NAME##_req_t NAME##_recv_req(NAME##_t *ch) { \
- cr_assert_in_coroutine(); \
- NAME##_req_t ret; \
- _cr_rpc_recv_req(&ch->core, \
- &ret.req, sizeof(ret.req), \
- (void **)&ret._resp, \
- &ret._requester); \
- return ret; \
- } \
- \
- static inline bool NAME##_can_recv_req(NAME##_t *ch) { \
- cr_assert_in_coroutine(); \
- return ch->core.waiters.front && \
- ch->core.waiter_typ == _CR_RPC_REQUESTER; \
- } \
- \
- static inline void NAME##_send_resp(NAME##_req_t req, RESP_T resp) { \
- cr_assert_in_coroutine(); \
- *(req._resp) = resp; \
- cr_unpause(req._requester); \
- cr_yield(); \
- } \
- \
- extern int LM_CAT2_(_CR_RPC_FORCE_SEMICOLON_, __COUNTER__)
+#define cr_rpc_send_resp(REQ, RESP) { \
+ cr_assert_in_coroutine(); \
+ *((REQ)._resp) = RESP; \
+ cr_unpause(REQ._requester); \
+ cr_yield(); \
+} while(0)
+
+/* Background details *********************************************************/
enum _cr_rpc_waiter_typ {
_CR_RPC_REQUESTER,
_CR_RPC_RESPONDER,
};
+SLIST_DECLARE(_cr_rpc_waiter_list);
+
struct _cr_rpc {
enum _cr_rpc_waiter_typ waiter_typ;
- lm_sll_root waiters;
+ struct _cr_rpc_waiter_list waiters;
};
void _cr_rpc_send_req(struct _cr_rpc *ch, void *req_ptr, size_t req_size, void *resp_ptr);
diff --git a/libcr_ipc/include/libcr_ipc/rwmutex.h b/libcr_ipc/include/libcr_ipc/rwmutex.h
index d48abe9..8ccae63 100644
--- a/libcr_ipc/include/libcr_ipc/rwmutex.h
+++ b/libcr_ipc/include/libcr_ipc/rwmutex.h
@@ -9,9 +9,11 @@
#include <stdbool.h>
-#include <libmisc/linkedlist.h>
+#include <libmisc/linkedlist.h> /* for SLIST_DECLARE() */
#include <libmisc/private.h>
+SLIST_DECLARE(_cr_rwmutex_waiter_list);
+
/**
* A cr_rwmutex_t is a fair read/write mutex.
*
@@ -25,10 +27,10 @@
*/
typedef struct {
BEGIN_PRIVATE(LIBCR_IPC_RWMUTEX_H);
- unsigned nreaders;
- bool locked;
- bool unpausing;
- lm_sll_root waiters;
+ unsigned nreaders;
+ bool locked;
+ bool unpausing;
+ struct _cr_rwmutex_waiter_list waiters;
END_PRIVATE(LIBCR_IPC_RWMUTEX_H);
} cr_rwmutex_t;
diff --git a/libcr_ipc/include/libcr_ipc/sema.h b/libcr_ipc/include/libcr_ipc/sema.h
index cc387f4..8b5ac5b 100644
--- a/libcr_ipc/include/libcr_ipc/sema.h
+++ b/libcr_ipc/include/libcr_ipc/sema.h
@@ -9,9 +9,11 @@
#include <stdbool.h>
-#include <libmisc/linkedlist.h>
+#include <libmisc/linkedlist.h> /* for SLIST_DECLARE() */
#include <libmisc/private.h>
+SLIST_DECLARE(_cr_sema_waiter_list);
+
/**
* A cr_sema_t is a fair unbounded[1] counting semaphore.
*
@@ -19,9 +21,9 @@
*/
typedef struct {
BEGIN_PRIVATE(LIBCR_IPC_SEMA_H);
- unsigned int cnt;
- bool unpausing;
- lm_sll_root waiters;
+ unsigned int cnt;
+ bool unpausing;
+ struct _cr_sema_waiter_list waiters;
END_PRIVATE(LIBCR_IPC_SEMA_H);
} cr_sema_t;
diff --git a/libcr_ipc/mutex.c b/libcr_ipc/mutex.c
index b0ebe05..1b4e626 100644
--- a/libcr_ipc/mutex.c
+++ b/libcr_ipc/mutex.c
@@ -5,14 +5,15 @@
*/
#include <libcr/coroutine.h> /* for cid_t, cr_* */
+#include <libmisc/assert.h>
#define IMPLEMENTATION_FOR_LIBCR_IPC_MUTEX_H YES
#include <libcr_ipc/mutex.h>
struct cr_mutex_waiter {
- lm_sll_node;
cid_t cid;
};
+SLIST_DECLARE_NODE(_cr_mutex_waiter_list, struct cr_mutex_waiter);
void cr_mutex_lock(cr_mutex_t *mu) {
assert(mu);
@@ -21,10 +22,10 @@ void cr_mutex_lock(cr_mutex_t *mu) {
if (!mu->locked) /* non-blocking fast-path */
mu->locked = true;
else { /* blocking slow-path */
- struct cr_mutex_waiter self = {
+ struct _cr_mutex_waiter_list_node self = { .val = {
.cid = cr_getcid(),
- };
- lm_sll_push_to_rear(&mu->waiters, &self);
+ }};
+ slist_push_to_rear(&mu->waiters, &self);
cr_pause_and_yield();
}
assert(mu->locked);
@@ -36,8 +37,8 @@ void cr_mutex_unlock(cr_mutex_t *mu) {
assert(mu->locked);
if (mu->waiters.front) {
- cr_unpause(lm_sll_node_cast(struct cr_mutex_waiter, mu->waiters.front)->cid);
- lm_sll_pop_from_front(&mu->waiters);
+ cr_unpause(mu->waiters.front->val.cid);
+ slist_pop_from_front(&mu->waiters);
} else
mu->locked = false;
}
diff --git a/libcr_ipc/rpc.c b/libcr_ipc/rpc.c
index 6d9422f..fcf51ba 100644
--- a/libcr_ipc/rpc.c
+++ b/libcr_ipc/rpc.c
@@ -7,23 +7,26 @@
#include <string.h> /* for memcpy() */
#include <libcr/coroutine.h> /* for cid_t, cr_* */
+#include <libmisc/assert.h>
#include <libcr_ipc/rpc.h>
struct cr_rpc_requester {
- lm_sll_node;
cid_t cid;
void *req_ptr; /* where to read req from */
void *resp_ptr; /* where to write resp to */
};
-
struct cr_rpc_responder {
- lm_sll_node;
/* before enqueued | after dequeued */
/* -------------------+-------------------- */
cid_t cid; /* responder cid | requester cid */
void *ptr; /* where to write req | where to write resp */
};
+union cr_rpc_waiter {
+ struct cr_rpc_requester requester;
+ struct cr_rpc_responder responder;
+};
+SLIST_DECLARE_NODE(_cr_rpc_waiter_list, union cr_rpc_waiter);
void _cr_rpc_send_req(struct _cr_rpc *ch, void *req_ptr, size_t req_size, void *resp_ptr) {
assert(ch);
@@ -31,9 +34,8 @@ void _cr_rpc_send_req(struct _cr_rpc *ch, void *req_ptr, size_t req_size, void *
assert(resp_ptr);
if (ch->waiters.front && ch->waiter_typ != _CR_RPC_REQUESTER) { /* fast-path (still blocks) */
- struct cr_rpc_responder *responder =
- lm_sll_node_cast(struct cr_rpc_responder, ch->waiters.front);
- lm_sll_pop_from_front(&ch->waiters);
+ struct cr_rpc_responder *responder = &ch->waiters.front->val.responder;
+ slist_pop_from_front(&ch->waiters);
/* Copy the req to the responder's stack. */
memcpy(responder->ptr, req_ptr, req_size);
/* Notify the responder that we have done so. */
@@ -43,12 +45,12 @@ void _cr_rpc_send_req(struct _cr_rpc *ch, void *req_ptr, size_t req_size, void *
/* Wait for the responder to set `*resp_ptr`. */
cr_pause_and_yield();
} else { /* blocking slow-path */
- struct cr_rpc_requester self = {
+ struct _cr_rpc_waiter_list_node self = { .val = { .requester = {
.cid = cr_getcid(),
.req_ptr = req_ptr,
.resp_ptr = resp_ptr,
- };
- lm_sll_push_to_rear(&ch->waiters, &self);
+ }}};
+ slist_push_to_rear(&ch->waiters, &self);
/* Wait for a responder to both copy our req and sed
* `*resp_ptr`. */
cr_pause_and_yield();
@@ -62,22 +64,21 @@ void _cr_rpc_recv_req(struct _cr_rpc *ch, void *req_ptr, size_t req_size, void *
assert(ret_requester);
if (ch->waiters.front && ch->waiter_typ != _CR_RPC_RESPONDER) { /* non-blocking fast-path */
- struct cr_rpc_requester *requester =
- lm_sll_node_cast(struct cr_rpc_requester, ch->waiters.front);
- lm_sll_pop_from_front(&ch->waiters);
+ struct cr_rpc_requester *requester = &ch->waiters.front->val.requester;
+ slist_pop_from_front(&ch->waiters);
memcpy(req_ptr, requester->req_ptr, req_size);
*ret_requester = requester->cid;
*ret_resp_ptr = requester->resp_ptr;
} else { /* blocking slow-path */
- struct cr_rpc_responder self = {
+ struct _cr_rpc_waiter_list_node self = { .val = { .responder = {
.cid = cr_getcid(),
.ptr = req_ptr,
- };
- lm_sll_push_to_rear(&ch->waiters, &self);
+ }}};
+ slist_push_to_rear(&ch->waiters, &self);
ch->waiter_typ = _CR_RPC_RESPONDER;
cr_pause_and_yield();
- *ret_requester = self.cid;
- *ret_resp_ptr = self.ptr;
+ *ret_requester = self.val.responder.cid;
+ *ret_resp_ptr = self.val.responder.ptr;
}
}
diff --git a/libcr_ipc/rwmutex.c b/libcr_ipc/rwmutex.c
index 4c5da81..191b7fe 100644
--- a/libcr_ipc/rwmutex.c
+++ b/libcr_ipc/rwmutex.c
@@ -5,31 +5,32 @@
*/
#include <libcr/coroutine.h> /* for cid_t, cr_* */
+#include <libmisc/assert.h>
#define IMPLEMENTATION_FOR_LIBCR_IPC_RWMUTEX_H YES
#include <libcr_ipc/rwmutex.h>
struct cr_rwmutex_waiter {
- lm_sll_node;
bool is_reader;
cid_t cid;
};
+SLIST_DECLARE_NODE(_cr_rwmutex_waiter_list, struct cr_rwmutex_waiter);
void cr_rwmutex_lock(cr_rwmutex_t *mu) {
assert(mu);
cr_assert_in_coroutine();
- struct cr_rwmutex_waiter self = {
+ struct _cr_rwmutex_waiter_list_node self = { .val = {
.is_reader = false,
.cid = cr_getcid(),
- };
- lm_sll_push_to_rear(&mu->waiters, &self);
- if (mu->waiters.front != &self.lm_sll_node || mu->locked)
+ }};
+ slist_push_to_rear(&mu->waiters, &self);
+ if (mu->waiters.front != &self || mu->locked)
cr_pause_and_yield();
- assert(mu->waiters.front == &self.lm_sll_node);
+ assert(mu->waiters.front == &self);
/* We now hold the lock (and are mu->waiters.front). */
- lm_sll_pop_from_front(&mu->waiters);
+ slist_pop_from_front(&mu->waiters);
assert(mu->nreaders == 0);
mu->locked = true;
mu->unpausing = false;
@@ -39,24 +40,23 @@ void cr_rwmutex_rlock(cr_rwmutex_t *mu) {
assert(mu);
cr_assert_in_coroutine();
- struct cr_rwmutex_waiter self = {
+ struct _cr_rwmutex_waiter_list_node self = { .val = {
.is_reader = true,
.cid = cr_getcid(),
- };
- lm_sll_push_to_rear(&mu->waiters, &self);
- if (mu->waiters.front != &self.lm_sll_node || (mu->locked && mu->nreaders == 0))
+ }};
+ slist_push_to_rear(&mu->waiters, &self);
+ if (mu->waiters.front != &self || (mu->locked && mu->nreaders == 0))
cr_pause_and_yield();
- assert(mu->waiters.front == &self.lm_sll_node);
+ assert(mu->waiters.front == &self);
/* We now hold the lock (and are mu->waiters.front). */
- lm_sll_pop_from_front(&mu->waiters);
+ slist_pop_from_front(&mu->waiters);
mu->nreaders++;
mu->locked = true;
- struct cr_rwmutex_waiter *waiter =
- lm_sll_node_cast(struct cr_rwmutex_waiter, mu->waiters.front);
- if (waiter && waiter->is_reader) {
+ struct _cr_rwmutex_waiter_list_node *waiter = mu->waiters.front;
+ if (waiter && waiter->val.is_reader) {
assert(mu->unpausing);
- cr_unpause(waiter->cid);
+ cr_unpause(waiter->val.cid);
} else {
mu->unpausing = false;
}
@@ -70,10 +70,9 @@ void cr_rwmutex_unlock(cr_rwmutex_t *mu) {
assert(mu->nreaders == 0);
assert(!mu->unpausing);
if (mu->waiters.front) {
- struct cr_rwmutex_waiter *waiter =
- lm_sll_node_cast(struct cr_rwmutex_waiter, mu->waiters.front);
+ struct _cr_rwmutex_waiter_list_node *waiter = mu->waiters.front;
mu->unpausing = true;
- cr_unpause(waiter->cid);
+ cr_unpause(waiter->val.cid);
} else {
mu->locked = false;
}
@@ -88,11 +87,10 @@ void cr_rwmutex_runlock(cr_rwmutex_t *mu) {
mu->nreaders--;
if (mu->nreaders == 0 && !mu->unpausing) {
if (mu->waiters.front) {
- struct cr_rwmutex_waiter *waiter =
- lm_sll_node_cast(struct cr_rwmutex_waiter, mu->waiters.front);
- assert(!waiter->is_reader);
+ struct _cr_rwmutex_waiter_list_node *waiter = mu->waiters.front;
+ assert(!waiter->val.is_reader);
mu->unpausing = true;
- cr_unpause(waiter->cid);
+ cr_unpause(waiter->val.cid);
} else {
mu->locked = false;
}
diff --git a/libcr_ipc/sema.c b/libcr_ipc/sema.c
index cb984b6..f2ac9b6 100644
--- a/libcr_ipc/sema.c
+++ b/libcr_ipc/sema.c
@@ -5,14 +5,15 @@
*/
#include <libcr/coroutine.h> /* for cid_t, cr_* */
+#include <libmisc/assert.h>
#define IMPLEMENTATION_FOR_LIBCR_IPC_SEMA_H YES
#include <libcr_ipc/sema.h>
struct cr_sema_waiter {
- lm_sll_node;
- cid_t cid;
+ cid_t cid;
};
+SLIST_DECLARE_NODE(_cr_sema_waiter_list, struct cr_sema_waiter);
void cr_sema_signal(cr_sema_t *sema) {
assert(sema);
@@ -21,8 +22,7 @@ void cr_sema_signal(cr_sema_t *sema) {
bool saved = cr_save_and_disable_interrupts();
sema->cnt++;
if (sema->waiters.front && !sema->unpausing) {
- cr_unpause(
- lm_sll_node_cast(struct cr_sema_waiter, sema->waiters.front)->cid);
+ cr_unpause(sema->waiters.front->val.cid);
sema->unpausing = true;
}
cr_restore_interrupts(saved);
@@ -34,8 +34,7 @@ void cr_sema_signal_from_intrhandler(cr_sema_t *sema) {
sema->cnt++;
if (sema->waiters.front && !sema->unpausing) {
- cr_unpause_from_intrhandler(
- lm_sll_node_cast(struct cr_sema_waiter, sema->waiters.front)->cid);
+ cr_unpause_from_intrhandler(sema->waiters.front->val.cid);
sema->unpausing = true;
}
}
@@ -46,18 +45,17 @@ void cr_sema_wait(cr_sema_t *sema) {
bool saved = cr_save_and_disable_interrupts();
- struct cr_sema_waiter self = {
+ struct _cr_sema_waiter_list_node self = { .val = {
.cid = cr_getcid(),
- };
- lm_sll_push_to_rear(&sema->waiters, &self);
- if (sema->waiters.front != &self.lm_sll_node || !sema->cnt)
+ }};
+ slist_push_to_rear(&sema->waiters, &self);
+ if (sema->waiters.front != &self || !sema->cnt)
cr_pause_and_yield();
- assert(sema->waiters.front == &self.lm_sll_node && sema->cnt);
- lm_sll_pop_from_front(&sema->waiters);
+ assert(sema->waiters.front == &self && sema->cnt);
+ slist_pop_from_front(&sema->waiters);
sema->cnt--;
if (sema->cnt && sema->waiters.front)
- cr_unpause(
- lm_sll_node_cast(struct cr_sema_waiter, sema->waiters.front)->cid);
+ cr_unpause(sema->waiters.front->val.cid);
else
sema->unpausing = false;
cr_restore_interrupts(saved);
diff --git a/libcr_ipc/tests/test_chan.c b/libcr_ipc/tests/test_chan.c
index 9b6f018..4788dd4 100644
--- a/libcr_ipc/tests/test_chan.c
+++ b/libcr_ipc/tests/test_chan.c
@@ -11,30 +11,30 @@
CR_CHAN_DECLARE(intchan, int);
-COROUTINE cr_producer(void *_ch) {
+COROUTINE producer_cr(void *_ch) {
intchan_t *ch = _ch;
cr_begin();
- intchan_send(ch, 1);
+ cr_chan_send(ch, 1);
- while (!intchan_can_send(ch))
+ while (!cr_chan_can_send(ch))
cr_yield();
- intchan_send(ch, 2);
+ cr_chan_send(ch, 2);
cr_end();
}
-COROUTINE cr_consumer(void *_ch) {
+COROUTINE consumer_cr(void *_ch) {
int x;
intchan_t *ch = _ch;
cr_begin();
- x = intchan_recv(ch);
+ x = cr_chan_recv(ch);
test_assert(x == 1);
- x = intchan_recv(ch);
+ x = cr_chan_recv(ch);
test_assert(x == 2);
cr_end();
@@ -42,8 +42,8 @@ COROUTINE cr_consumer(void *_ch) {
int main() {
intchan_t ch = {0};
- coroutine_add("producer", cr_producer, &ch);
- coroutine_add("consumer", cr_consumer, &ch);
+ coroutine_add("producer", producer_cr, &ch);
+ coroutine_add("consumer", consumer_cr, &ch);
coroutine_main();
return 0;
}
diff --git a/libcr_ipc/tests/test_mutex.c b/libcr_ipc/tests/test_mutex.c
index 43714c9..d08315d 100644
--- a/libcr_ipc/tests/test_mutex.c
+++ b/libcr_ipc/tests/test_mutex.c
@@ -11,7 +11,7 @@
int counter = 0;
-COROUTINE cr_worker(void *_mu) {
+COROUTINE worker_cr(void *_mu) {
cr_mutex_t *mu = _mu;
cr_begin();
@@ -29,8 +29,8 @@ COROUTINE cr_worker(void *_mu) {
int main() {
cr_mutex_t mu = {};
- coroutine_add("a", cr_worker, &mu);
- coroutine_add("b", cr_worker, &mu);
+ coroutine_add("a", worker_cr, &mu);
+ coroutine_add("b", worker_cr, &mu);
coroutine_main();
test_assert(counter == 200);
return 0;
diff --git a/libcr_ipc/tests/test_rpc.c b/libcr_ipc/tests/test_rpc.c
index 1e3c471..1461450 100644
--- a/libcr_ipc/tests/test_rpc.c
+++ b/libcr_ipc/tests/test_rpc.c
@@ -14,46 +14,46 @@ CR_RPC_DECLARE(intrpc, int, int);
/* Test that the RPC is fair, have worker1 start waiting first, and
* ensure that it gets the first request. */
-COROUTINE cr_caller(void *_ch) {
+COROUTINE caller_cr(void *_ch) {
intrpc_t *ch = _ch;
cr_begin();
- int resp = intrpc_send_req(ch, 1);
+ int resp = cr_rpc_send_req(ch, 1);
test_assert(resp == 2);
- resp = intrpc_send_req(ch, 3);
+ resp = cr_rpc_send_req(ch, 3);
test_assert(resp == 4);
cr_exit();
}
-COROUTINE cr_worker1(void *_ch) {
+COROUTINE worker1_cr(void *_ch) {
intrpc_t *ch = _ch;
cr_begin();
- intrpc_req_t req = intrpc_recv_req(ch);
+ intrpc_req_t req = cr_rpc_recv_req(ch);
test_assert(req.req == 1);
- intrpc_send_resp(req, 2);
+ cr_rpc_send_resp(req, 2);
cr_exit();
}
-COROUTINE cr_worker2(void *_ch) {
+COROUTINE worker2_cr(void *_ch) {
intrpc_t *ch = _ch;
cr_begin();
- intrpc_req_t req = intrpc_recv_req(ch);
+ intrpc_req_t req = cr_rpc_recv_req(ch);
test_assert(req.req == 3);
- intrpc_send_resp(req, 4);
+ cr_rpc_send_resp(req, 4);
cr_exit();
}
int main() {
intrpc_t ch = {0};
- coroutine_add("worker1", cr_worker1, &ch);
- coroutine_add("caller", cr_caller, &ch);
- coroutine_add("worker2", cr_worker2, &ch);
+ coroutine_add("worker1", worker1_cr, &ch);
+ coroutine_add("caller", caller_cr, &ch);
+ coroutine_add("worker2", worker2_cr, &ch);
coroutine_main();
return 0;
}
diff --git a/libcr_ipc/tests/test_select.c b/libcr_ipc/tests/test_select.c
index 1db645b..9b5d117 100644
--- a/libcr_ipc/tests/test_select.c
+++ b/libcr_ipc/tests/test_select.c
@@ -14,7 +14,7 @@ CR_CHAN_DECLARE(intchan, int);
intchan_t ch[10] = {0};
intchan_t fch = {0};
-COROUTINE cr_consumer(void *) {
+COROUTINE consumer_cr(void *) {
cr_begin();
struct cr_select_arg args[11];
@@ -53,27 +53,26 @@ COROUTINE cr_consumer(void *) {
test_assert(ret_arg == 0);
send = 890;
- args[0] = CR_SELECT_SEND(&fch, &send);
- args[1] = CR_SELECT_DEFAULT;
- ret_arg = cr_select_v(2, args);
+ ret_arg = cr_select_l(CR_SELECT_SEND(&fch, &send),
+ CR_SELECT_DEFAULT);
test_assert(ret_arg == 1);
cr_end();
}
-COROUTINE cr_producer(void *_n) {
+COROUTINE producer_cr(void *_n) {
int n = *(int *)_n;
cr_begin();
- intchan_send(&ch[n], n);
+ cr_chan_send(&ch[n], n);
cr_end();
}
-COROUTINE cr_final(void *) {
+COROUTINE final_cr(void *) {
cr_begin();
- int ret = intchan_recv(&fch);
+ int ret = cr_chan_recv(&fch);
printf("ret=%d\n", ret);
test_assert(ret == 567);
@@ -82,9 +81,9 @@ COROUTINE cr_final(void *) {
int main() {
for (int i = 0; i < 10; i++)
- coroutine_add("producer", cr_producer, &i);
- coroutine_add("consumer", cr_consumer, NULL);
- coroutine_add("final", cr_final, NULL);
+ coroutine_add("producer", producer_cr, &i);
+ coroutine_add("consumer", consumer_cr, NULL);
+ coroutine_add("final", final_cr, NULL);
coroutine_main();
return 0;
}
diff --git a/libcr_ipc/tests/test_sema.c b/libcr_ipc/tests/test_sema.c
index e5b22a5..435c01a 100644
--- a/libcr_ipc/tests/test_sema.c
+++ b/libcr_ipc/tests/test_sema.c
@@ -13,7 +13,7 @@
int counter = 0;
-COROUTINE cr_first(void *_sema) {
+COROUTINE first_cr(void *_sema) {
cr_sema_t *sema = _sema;
cr_begin();
@@ -24,7 +24,7 @@ COROUTINE cr_first(void *_sema) {
cr_exit();
}
-COROUTINE cr_second(void *_sema) {
+COROUTINE second_cr(void *_sema) {
cr_sema_t *sema = _sema;
cr_begin();
@@ -35,7 +35,7 @@ COROUTINE cr_second(void *_sema) {
cr_exit();
}
-COROUTINE cr_producer(void *_sema) {
+COROUTINE producer_cr(void *_sema) {
cr_sema_t *sema = _sema;
cr_begin();
@@ -45,7 +45,7 @@ COROUTINE cr_producer(void *_sema) {
cr_end();
}
-COROUTINE cr_consumer(void *_sema) {
+COROUTINE consumer_cr(void *_sema) {
cr_sema_t *sema = _sema;
cr_begin();
@@ -59,16 +59,16 @@ int main() {
cr_sema_t sema = {};
printf("== test 1 =========================================\n");
- coroutine_add("first", cr_first, &sema);
- coroutine_add("second", cr_second, &sema);
+ coroutine_add("first", first_cr, &sema);
+ coroutine_add("second", second_cr, &sema);
coroutine_main();
test_assert(sema.cnt == 0);
printf("== test 2 =========================================\n");
- coroutine_add("consumer", cr_consumer, &sema);
- coroutine_add("producer", cr_producer, &sema);
+ coroutine_add("consumer", consumer_cr, &sema);
+ coroutine_add("producer", producer_cr, &sema);
coroutine_main();
- coroutine_add("consumer", cr_consumer, &sema);
+ coroutine_add("consumer", consumer_cr, &sema);
coroutine_main();
test_assert(sema.cnt == 0);
diff --git a/libhw_cr/host_net.c b/libhw_cr/host_net.c
index 6ed6e46..ba634a6 100644
--- a/libhw_cr/host_net.c
+++ b/libhw_cr/host_net.c
@@ -19,6 +19,7 @@
#include <signal.h> /* for siginfo_t, struct sigaction, enum sigval, sigaction(), SA_SIGINFO */
#include <libcr/coroutine.h>
+#include <libmisc/alloc.h>
#include <libmisc/assert.h>
#include <libmisc/macro.h>
#include <libobj/obj.h>
@@ -283,7 +284,7 @@ static void *hostnet_pthread_writev(void *_args) {
struct hostnet_pthread_writev_args *args = _args;
size_t count = 0;
- struct iovec *iov = alloca(sizeof(struct iovec)*args->iovcnt);
+ struct iovec *iov = stack_alloc(args->iovcnt, struct iovec);
for (int i = 0; i < args->iovcnt; i++) {
iov[i] = args->iov[i];
count += args->iov[i].iov_len;
diff --git a/libhw_cr/rp2040_hwspi.c b/libhw_cr/rp2040_hwspi.c
index d181650..29a7bac 100644
--- a/libhw_cr/rp2040_hwspi.c
+++ b/libhw_cr/rp2040_hwspi.c
@@ -4,7 +4,6 @@
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
-#include <alloca.h>
#include <inttypes.h> /* for PRIu{n} */
#include <hardware/clocks.h> /* for clock_get_hz() and clk_peri */
@@ -12,6 +11,7 @@
#include <hardware/spi.h>
#include <libcr/coroutine.h>
+#include <libmisc/alloc.h>
#include <libmisc/assert.h>
#define LOG_NAME RP2040_SPI
@@ -198,8 +198,8 @@ static void rp2040_hwspi_readwritev(struct rp2040_hwspi *self, const struct dupl
* happens, so the IRQ machinery doesn't need to be engaged
* at all.
*/
- struct dma_alias1 *tx_data_blocks = alloca(sizeof(struct dma_alias1)*(pruned_iovcnt+1));
- struct dma_alias0 *rx_data_blocks = alloca(sizeof(struct dma_alias0)*(pruned_iovcnt+1));
+ struct dma_alias1 *tx_data_blocks = stack_alloc(pruned_iovcnt+1, struct dma_alias1);
+ struct dma_alias0 *rx_data_blocks = stack_alloc(pruned_iovcnt+1, struct dma_alias0);
static_assert(!DMA_IS_TRIGGER(typeof(tx_data_blocks[0]), ctrl));
static_assert(DMA_IS_TRIGGER(typeof(rx_data_blocks[0]), ctrl));
diff --git a/libhw_cr/w5500.c b/libhw_cr/w5500.c
index fa427d9..c04e344 100644
--- a/libhw_cr/w5500.c
+++ b/libhw_cr/w5500.c
@@ -217,7 +217,7 @@ static COROUTINE w5500_irq_cr(void *_chip) {
}
if (send_bits) {
debugf("w5500_irq_cr(): signal sock[%"PRIu8"]->write_ch", socknum);
- _w5500_sockintr_ch_send(&socket->write_ch, send_bits);
+ cr_chan_send(&socket->write_ch, send_bits);
}
break;
}
@@ -666,7 +666,7 @@ static ssize_t w5500_tcp_writev(struct _w5500_socket *socket, const struct iovec
w5500_socket_cmd(socket, CMD_SEND);
cr_mutex_unlock(&chip->mu);
- switch (_w5500_sockintr_ch_recv(&socket->write_ch)) {
+ switch (cr_chan_recv(&socket->write_ch)) {
case SOCKINTR_SEND_OK:
debugf(" => sent %zu", freesize);
done += freesize;
@@ -853,7 +853,7 @@ static ssize_t w5500_udp_sendto(struct _w5500_socket *socket, void *buf, size_t
w5500_socket_cmd(socket, CMD_SEND);
cr_mutex_unlock(&chip->mu);
- switch (_w5500_sockintr_ch_recv(&socket->write_ch)) {
+ switch (cr_chan_recv(&socket->write_ch)) {
case SOCKINTR_SEND_OK:
debugf(" => sent");
return count;
diff --git a/libhw_cr/w5500_ll.h b/libhw_cr/w5500_ll.h
index 2506cd2..8b98f9d 100644
--- a/libhw_cr/w5500_ll.h
+++ b/libhw_cr/w5500_ll.h
@@ -10,10 +10,10 @@
#ifndef _LIBHW_CR_W5500_LL_H_
#define _LIBHW_CR_W5500_LL_H_
-#include <alloca.h> /* for alloca() */
#include <stdint.h> /* for uint{n}_t */
#include <string.h> /* for memcmp() */
+#include <libmisc/alloc.h> /* for stack_alloc() */
#include <libmisc/assert.h> /* for assert(), static_assert() */
#include <libmisc/endian.h> /* for uint16be_t */
@@ -94,7 +94,7 @@ w5500ll_writev(
(block & CTL_MASK_BLOCK) | CTL_W | CTL_OM_VDM,
};
int inner_cnt = 1+io_slice_cnt(iov, iovcnt, skip, max);
- struct duplex_iovec *inner = alloca(sizeof(struct duplex_iovec)*inner_cnt);
+ struct duplex_iovec *inner = stack_alloc(inner_cnt, struct duplex_iovec);
inner[0] = (struct duplex_iovec){
.iov_read_to = IOVEC_DISCARD,
.iov_write_from = header,
@@ -131,7 +131,7 @@ w5500ll_readv(
(block & CTL_MASK_BLOCK) | CTL_R | CTL_OM_VDM,
};
int inner_cnt = 1+io_slice_cnt(iov, iovcnt, 0, max);
- struct duplex_iovec *inner = alloca(sizeof(struct duplex_iovec)*inner_cnt);
+ struct duplex_iovec *inner = stack_alloc(inner_cnt, struct duplex_iovec);
inner[0] = (struct duplex_iovec){
.iov_read_to = IOVEC_DISCARD,
.iov_write_from = header,
diff --git a/libmisc/include/libmisc/alloc.h b/libmisc/include/libmisc/alloc.h
new file mode 100644
index 0000000..afddbce
--- /dev/null
+++ b/libmisc/include/libmisc/alloc.h
@@ -0,0 +1,26 @@
+/* libmisc/alloc.h - Type-safe wrappers around alloca and malloc
+ *
+ * Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#ifndef _LIBMISC_ALLOC_H_
+#define _LIBMISC_ALLOC_H_
+
+#include <alloca.h> /* for alloca() */
+#include <stdlib.h> /* for calloc(), free() */
+#include <string.h> /* for memset() */
+
+#define stack_alloc(N, TYP) ({ \
+ size_t _size; \
+ TYP *_ret = NULL; \
+ if (!__builtin_mul_overflow(N, sizeof(TYP), &_size)) { \
+ _ret = alloca(_size); \
+ memset(_ret, 0, _size); \
+ } \
+ _ret; \
+})
+
+#define heap_alloc(N, TYP) ((TYP *)calloc(N, sizeof(TYP)))
+
+#endif /* _LIBMISC_ALLOC_H_ */
diff --git a/libmisc/include/libmisc/linkedlist.h b/libmisc/include/libmisc/linkedlist.h
index 8adef66..e8c65db 100644
--- a/libmisc/include/libmisc/linkedlist.h
+++ b/libmisc/include/libmisc/linkedlist.h
@@ -7,40 +7,102 @@
#ifndef _LIBMISC_LINKEDLIST_H_
#define _LIBMISC_LINKEDLIST_H_
-#include <libmisc/assert.h>
-#include <libmisc/macro.h>
+/* low-level (intrusive) singly linked list ***********************************/
-/* singly linked list *********************************************************/
+struct _slist_node {
+ struct _slist_node *rear;
+};
-typedef struct _lm_sll_node {
- struct _lm_sll_node *rear;
-} lm_sll_node;
+struct _slist_root {
+ struct _slist_node *front, *rear;
+};
-typedef struct {
- lm_sll_node *front, *rear;
-} lm_sll_root;
+void _slist_push_to_rear(struct _slist_root *root, struct _slist_node *node);
+void _slist_pop_from_front(struct _slist_root *root);
-#define lm_sll_node_cast(node_typ, node_ptr) \
- LM_CAST_FIELD_TO_STRUCT(node_typ, lm_sll_node, node_ptr)
+/* low-level (intrusive) doubly linked list ***********************************/
-void lm_sll_push_to_rear(lm_sll_root *root, lm_sll_node *node);
-void lm_sll_pop_from_front(lm_sll_root *root);
+struct _dlist_node {
+ struct _dlist_node *front, *rear;
+};
-/* doubly linked list *********************************************************/
+struct _dlist_root {
+ struct _dlist_node *front, *rear;
+};
-typedef struct _lm_dll_node {
- struct _lm_dll_node *front, *rear;
-} lm_dll_node;
+void _dlist_push_to_rear(struct _dlist_root *root, struct _dlist_node *node);
+void _dlist_remove(struct _dlist_root *root, struct _dlist_node *node);
+void _dlist_pop_from_front(struct _dlist_root *root);
-typedef struct {
- lm_dll_node *front, *rear;
-} lm_dll_root;
+/* singly linked list (non-intrusive) *****************************************/
-#define lm_dll_node_cast(node_typ, node_ptr) \
- LM_CAST_FIELD_TO_STRUCT(node_typ, lm_dll_node, node_ptr)
+#define SLIST_DECLARE(NAME) \
+ struct NAME##_node; \
+ struct NAME { \
+ struct NAME##_node *front, *rear; \
+ struct NAME *_slist_root_typ[0]; \
+ }
-void lm_dll_push_to_rear(lm_dll_root *root, lm_dll_node *node);
-void lm_dll_remove(lm_dll_root *root, lm_dll_node *node);
-void lm_dll_pop_from_front(lm_dll_root *root);
+#define SLIST_DECLARE_NODE(NAME, VAL_T) \
+ struct NAME##_node { \
+ struct NAME##_node *rear; \
+ VAL_T val; \
+ }
+
+#define slist_push_to_rear(ROOT, NODE) { \
+ /* These temporary variables are to get the compiler to check \
+ * the types. */ \
+ typeof(*(ROOT)->_slist_root_typ[0]) *_rootp = ROOT; \
+ typeof(*_rootp->front) *_nodep = NODE; \
+ _slist_push_to_rear((struct _slist_root *)_rootp, \
+ (struct _slist_node *)_nodep); \
+} while(0)
+
+#define slist_pop_from_front(ROOT) { \
+ /* This temporary variables are to get the compiler to check \
+ * the type. */ \
+ typeof(*(ROOT)->_slist_root_typ[0]) *_rootp = ROOT; \
+ _slist_pop_from_front((struct _slist_root *)_rootp); \
+} while(0)
+
+/* doubly linked list (non-intrusive) *****************************************/
+
+#define DLIST_DECLARE(NAME) \
+ struct NAME##_node; \
+ struct NAME { \
+ struct NAME##_node *front, *rear; \
+ struct NAME *_dlist_root_typ[0]; \
+ }
+
+#define DLIST_DECLARE_NODE(NAME, VAL_T) \
+ struct NAME##_node { \
+ struct NAME##_node *front, *rear; \
+ VAL_T val; \
+ }
+
+#define dlist_push_to_rear(ROOT, NODE) { \
+ /* These temporary variables are to get the compiler to check \
+ * the types. */ \
+ typeof(*(ROOT)->_dlist_root_typ[0]) *_rootp = ROOT; \
+ typeof(*_rootp->front) *_nodep = NODE; \
+ _dlist_push_to_rear((struct _dlist_root *)_rootp, \
+ (struct _dlist_node *)_nodep); \
+} while(0)
+
+#define dlist_remove(ROOT, NODE) { \
+ /* These temporary variables are to get the compiler to check \
+ * the types. */ \
+ typeof(*(ROOT)->_dlist_root_typ[0]) *_rootp = ROOT; \
+ typeof(*_rootp->front) *_nodep = NODE; \
+ _dlist_remove((struct _dlist_root *)_rootp, \
+ (struct _dlist_node *)_nodep); \
+} while(0)
+
+#define dlist_pop_from_front(ROOT) { \
+ /* This temporary variables are to get the compiler to check \
+ * the type. */ \
+ typeof(*(ROOT)->_dlist_root_typ[0]) *_rootp = ROOT; \
+ _dlist_pop_from_front((struct _dlist_root *)_rootp); \
+} while(0)
#endif /* _LIBMISC_LINKEDLIST_H_ */
diff --git a/libmisc/include/libmisc/map.h b/libmisc/include/libmisc/map.h
index f846f57..41ac069 100644
--- a/libmisc/include/libmisc/map.h
+++ b/libmisc/include/libmisc/map.h
@@ -15,12 +15,14 @@
/* Type ***********************************************************************/
+DLIST_DECLARE(_map_kv_list);
+
struct _map {
- size_t len;
- size_t nbuckets;
- lm_dll_root *buckets;
+ size_t len;
+ size_t nbuckets;
+ struct _map_kv_list *buckets;
- unsigned iterating;
+ unsigned int iterating;
size_t sizeof_kv;
size_t offsetof_k, sizeof_k;
@@ -30,25 +32,26 @@ struct _map {
/**
* MAP_DECLARE(MAPNAME, KEY_T, VAL_T) declares `struct MAPNAME`.
*/
-#define MAP_DECLARE(MAPNAME, KEY_T, VAL_T) \
- struct MAPNAME { \
- struct _map core; \
- struct { \
- lm_dll_node; \
- uint8_t flags; \
- KEY_T key; \
- VAL_T val; \
- } kv_typ[0]; \
+#define MAP_DECLARE(MAPNAME, KEY_T, VAL_T) \
+ struct _##MAPNAME##_kv { \
+ uint8_t flags; \
+ KEY_T key; \
+ VAL_T val; \
+ }; \
+ DLIST_DECLARE_NODE(_##MAPNAME##_kv_list, struct _##MAPNAME##_kv); \
+ struct MAPNAME { \
+ struct _map core; \
+ struct _##MAPNAME##_kv_list_node kv_typ[0]; \
}
-#define _map_init(M) do { \
- if (!(M)->core.sizeof_kv) { \
- (M)->core.sizeof_kv = sizeof((M)->kv_typ[0]); \
- (M)->core.sizeof_k = sizeof((M)->kv_typ[0].key); \
- (M)->core.sizeof_v = sizeof((M)->kv_typ[0].val); \
- (M)->core.offsetof_k = offsetof(typeof((M)->kv_typ[0]), key); \
- (M)->core.offsetof_v = offsetof(typeof((M)->kv_typ[0]), val); \
- } \
+#define _map_init(M) do { \
+ if (!(M)->core.sizeof_kv) { \
+ (M)->core.sizeof_kv = sizeof((M)->kv_typ[0]); \
+ (M)->core.sizeof_k = sizeof((M)->kv_typ[0].val.key); \
+ (M)->core.sizeof_v = sizeof((M)->kv_typ[0].val.val); \
+ (M)->core.offsetof_k = offsetof(typeof((M)->kv_typ[0]), val.key); \
+ (M)->core.offsetof_v = offsetof(typeof((M)->kv_typ[0]), val.val); \
+ } \
} while(0)
/* Methods ********************************************************************/
@@ -64,8 +67,8 @@ struct _map {
*/
#define map_load(M, K) ({ \
_map_init(M); \
- typeof((M)->kv_typ[0].key) _k = K; \
- (typeof((M)->kv_typ[0].val)*)_map_load(&(M)->core, &_k); \
+ typeof((M)->kv_typ[0].val.key) _k = K; \
+ (typeof((M)->kv_typ[0].val.val)*)_map_load(&(M)->core, &_k); \
})
void *_map_load(struct _map *m, void *kp);
@@ -73,10 +76,10 @@ void *_map_load(struct _map *m, void *kp);
* map_del(map, key) ensures that `key` is not present in `map`.
* Returns whether `key` was in `map` before the call.
*/
-#define map_del(M, K) ({ \
- _map_init(M); \
- typeof((M)->kv_typ[0].key) _k = K; \
- _map_del(&(M)->core, &_k); \
+#define map_del(M, K) ({ \
+ _map_init(M); \
+ typeof((M)->kv_typ[0].val.key) _k = K; \
+ _map_del(&(M)->core, &_k); \
})
bool _map_del(struct _map *m, void *kp);
@@ -84,11 +87,11 @@ bool _map_del(struct _map *m, void *kp);
* map_store(map, key, val) sets a value in the map. Returns a
* pointer to the map's copy of `val`.
*/
-#define map_store(M, K, ...) ({ \
- _map_init(M); \
- typeof((M)->kv_typ[0].key) _k = K; \
- typeof((M)->kv_typ[0].val) _v = __VA_ARGS__; \
- (typeof((M)->kv_typ[0].val)*)_map_store(&(M)->core, &_k, &_v); \
+#define map_store(M, K, ...) ({ \
+ _map_init(M); \
+ typeof((M)->kv_typ[0].val.key) _k = K; \
+ typeof((M)->kv_typ[0].val.val) _v = __VA_ARGS__; \
+ (typeof((M)->kv_typ[0].val.val)*)_map_store(&(M)->core, &_k, &_v); \
})
void *_map_store(struct _map *m, void *kp, void *vp);
@@ -100,18 +103,13 @@ void _map_free(struct _map *m);
/* Iteration ******************************************************************/
-struct _map_kv {
- lm_dll_node;
- uint8_t flags;
-};
-
struct _map_iter {
- struct _map *m;
- void *keyp;
- void **valpp;
+ struct _map *m;
+ void *keyp;
+ void **valpp;
- size_t i;
- struct _map_kv *kv;
+ size_t i;
+ struct _map_kv_list_node *kv;
};
/**
@@ -128,8 +126,8 @@ struct _map_iter {
#define MAP_FOREACH(M, KNAME, VNAME) _MAP_FOREACH(__COUNTER__, M, KNAME, VNAME)
#define _MAP_FOREACH(CNT, M, KNAME, VNAME) \
for (bool _once_##CNT = true; _once_##CNT;) \
- for (typeof((M)->kv_typ[0].key) KNAME; _once_##CNT;) \
- for (typeof((M)->kv_typ[0].val) *VNAME; _once_##CNT;) \
+ for (typeof((M)->kv_typ[0].val.key) KNAME; _once_##CNT;) \
+ for (typeof((M)->kv_typ[0].val.val) *VNAME; _once_##CNT;) \
for ( \
struct _map_iter _iter_##CNT = ({ \
_map_init(M); \
diff --git a/libmisc/linkedlist.c b/libmisc/linkedlist.c
index 5fe0977..71a0aa9 100644
--- a/libmisc/linkedlist.c
+++ b/libmisc/linkedlist.c
@@ -6,11 +6,13 @@
#include <stddef.h> /* for NULL */
+#include <libmisc/assert.h>
+
#include <libmisc/linkedlist.h>
/* singly linked list *********************************************************/
-void lm_sll_push_to_rear(lm_sll_root *root, lm_sll_node *node) {
+void _slist_push_to_rear(struct _slist_root *root, struct _slist_node *node) {
assert(root);
node->rear = NULL;
if (root->rear)
@@ -20,7 +22,7 @@ void lm_sll_push_to_rear(lm_sll_root *root, lm_sll_node *node) {
root->rear = node;
}
-void lm_sll_pop_from_front(lm_sll_root *root) {
+void _slist_pop_from_front(struct _slist_root *root) {
assert(root);
assert(root->front);
root->front = root->front->rear;
@@ -30,7 +32,7 @@ void lm_sll_pop_from_front(lm_sll_root *root) {
/* doubly linked list *********************************************************/
-void lm_dll_push_to_rear(lm_dll_root *root, lm_dll_node *node) {
+void _dlist_push_to_rear(struct _dlist_root *root, struct _dlist_node *node) {
assert(root);
assert(node);
node->front = root->rear;
@@ -42,7 +44,7 @@ void lm_dll_push_to_rear(lm_dll_root *root, lm_dll_node *node) {
root->rear = node;
}
-void lm_dll_remove(lm_dll_root *root, lm_dll_node *node) {
+void _dlist_remove(struct _dlist_root *root, struct _dlist_node *node) {
assert(root);
assert(node);
if (node->front)
@@ -55,8 +57,8 @@ void lm_dll_remove(lm_dll_root *root, lm_dll_node *node) {
root->rear = node->front;
}
-void lm_dll_pop_from_front(lm_dll_root *root) {
+void _dlist_pop_from_front(struct _dlist_root *root) {
assert(root);
assert(root->front);
- lm_dll_remove(root, root->front);
+ _dlist_remove(root, root->front);
}
diff --git a/libmisc/map.c b/libmisc/map.c
index bb8a2d2..cc34c16 100644
--- a/libmisc/map.c
+++ b/libmisc/map.c
@@ -8,6 +8,7 @@
#include <string.h>
#include <libmisc/hash.h>
+#include <libmisc/alloc.h>
#include <libmisc/assert.h>
#include <libmisc/map.h>
@@ -16,14 +17,19 @@
/* Internal utilities *********************************************************/
-#define cast(n) lm_dll_node_cast(struct _map_kv, n)
+struct _map_kv {
+ uint8_t flags;
+ /* opaque key; */
+ /* opaque val; */
+};
+DLIST_DECLARE_NODE(_map_kv_list, struct _map_kv);
-static inline void *_map_kv_keyp(struct _map *m, struct _map_kv *kv) {
+static inline void *_map_kv_keyp(struct _map *m, struct _map_kv_list_node *kv) {
assert(m);
assert(kv);
return ((void*)kv)+m->offsetof_k;
}
-static inline void *_map_kv_valp(struct _map *m, struct _map_kv *kv) {
+static inline void *_map_kv_valp(struct _map *m, struct _map_kv_list_node *kv) {
assert(m);
assert(kv);
return ((void*)kv)+m->offsetof_v;
@@ -31,8 +37,8 @@ static inline void *_map_kv_valp(struct _map *m, struct _map_kv *kv) {
static inline void _map_lookup(struct _map *m, void *keyp,
hash_t *ret_hash,
- lm_dll_root **ret_bucket,
- struct _map_kv **ret_kv) {
+ struct _map_kv_list **ret_bucket,
+ struct _map_kv_list_node **ret_kv) {
assert(m);
assert(keyp);
assert(ret_hash);
@@ -45,8 +51,8 @@ static inline void _map_lookup(struct _map *m, void *keyp,
return;
}
*ret_bucket = &m->buckets[*ret_hash % m->nbuckets];
- for (struct _map_kv *kv = cast((*ret_bucket)->front); kv; kv = cast(kv->rear)) {
- if (!(kv->flags & FLAG_DEL) &&
+ for (struct _map_kv_list_node *kv = (*ret_bucket)->front; kv; kv = kv->rear) {
+ if (!(kv->val.flags & FLAG_DEL) &&
memcmp(_map_kv_keyp(m, kv), keyp, m->sizeof_k) == 0) {
*ret_kv = kv;
return;
@@ -58,13 +64,13 @@ static inline void _map_lookup(struct _map *m, void *keyp,
static inline void _map_resize(struct _map *m, size_t new_nbuckets) {
assert(m);
assert(new_nbuckets);
- lm_dll_root *new_buckets = calloc(new_nbuckets, sizeof(lm_dll_root));
+ struct _map_kv_list *new_buckets = heap_alloc(new_nbuckets, struct _map_kv_list);
for (size_t i = 0; i < m->nbuckets; i++) {
while (m->buckets[i].front) {
- struct _map_kv *kv = cast(m->buckets[i].front);
- lm_dll_pop_from_front(&m->buckets[i]);
+ struct _map_kv_list_node *kv = m->buckets[i].front;
+ dlist_pop_from_front(&m->buckets[i]);
hash_t h = hash(_map_kv_keyp(m, kv), m->sizeof_k);
- lm_dll_push_to_rear(&new_buckets[h % new_nbuckets], kv);
+ dlist_push_to_rear(&new_buckets[h % new_nbuckets], kv);
}
}
m->nbuckets = new_nbuckets;
@@ -91,8 +97,8 @@ void *_map_load(struct _map *m, void *keyp) {
assert(keyp);
hash_t h;
- lm_dll_root *bucket;
- struct _map_kv *kv;
+ struct _map_kv_list *bucket;
+ struct _map_kv_list_node *kv;
_map_lookup(m, keyp, &h, &bucket, &kv);
if (!kv)
@@ -105,16 +111,16 @@ bool _map_del(struct _map *m, void *keyp) {
assert(keyp);
hash_t h;
- lm_dll_root *bucket;
- struct _map_kv *kv;
+ struct _map_kv_list *bucket;
+ struct _map_kv_list_node *kv;
_map_lookup(m, keyp, &h, &bucket, &kv);
if (!kv)
return false;
- if (kv->flags & FLAG_ITER) {
- kv->flags |= FLAG_DEL;
+ if (kv->val.flags & FLAG_ITER) {
+ kv->val.flags |= FLAG_DEL;
} else {
- lm_dll_remove(bucket, kv);
+ dlist_remove(bucket, kv);
free(kv);
}
m->len--;
@@ -127,12 +133,12 @@ void *_map_store(struct _map *m, void *keyp, void *valp) {
assert(valp);
hash_t h;
- lm_dll_root *bucket;
- struct _map_kv *old;
+ struct _map_kv_list *bucket;
+ struct _map_kv_list_node *old;
_map_lookup(m, keyp, &h, &bucket, &old);
if (old) {
- lm_dll_remove(bucket, old);
+ dlist_remove(bucket, old);
free(old);
m->len--;
}
@@ -141,10 +147,10 @@ void *_map_store(struct _map *m, void *keyp, void *valp) {
h = hash(keyp, m->sizeof_k);
bucket = &m->buckets[h % m->nbuckets];
}
- struct _map_kv *kv = calloc(1, m->sizeof_kv);
+ struct _map_kv_list_node *kv = calloc(1, m->sizeof_kv);
memcpy(_map_kv_keyp(m, kv), keyp, m->sizeof_k);
memcpy(_map_kv_valp(m, kv), valp, m->sizeof_v);
- lm_dll_push_to_rear(bucket, kv);
+ dlist_push_to_rear(bucket, kv);
return _map_kv_valp(m, kv);
}
@@ -153,8 +159,8 @@ void _map_free(struct _map *m) {
for (size_t i = 0; i < m->nbuckets; i++) {
while (m->buckets[i].front) {
- struct _map_kv *kv = cast(m->buckets[i].front);
- lm_dll_pop_from_front(&m->buckets[i]);
+ struct _map_kv_list_node *kv = m->buckets[i].front;
+ dlist_pop_from_front(&m->buckets[i]);
free(kv);
}
}
@@ -199,14 +205,14 @@ bool _map_iter_next(struct _map_iter *state) {
return false;
while (!state->m->buckets[state->i].front)
state->i++;
- state->kv = cast(state->m->buckets[state->i].front);
+ state->kv = state->m->buckets[state->i].front;
} else {
- struct _map_kv *old_kv = state->kv;
- state->kv = cast(old_kv->rear);
+ struct _map_kv_list_node *old_kv = state->kv;
+ state->kv = old_kv->rear;
- old_kv->flags &= ~FLAG_ITER;
- if (old_kv->flags & FLAG_DEL) {
- lm_dll_remove(&state->m->buckets[state->i], old_kv);
+ old_kv->val.flags &= ~FLAG_ITER;
+ if (old_kv->val.flags & FLAG_DEL) {
+ dlist_remove(&state->m->buckets[state->i], old_kv);
free(old_kv);
}
@@ -214,10 +220,10 @@ bool _map_iter_next(struct _map_iter *state) {
state->i++;
if (state->i == state->m->nbuckets)
return false;
- state->kv = cast(state->m->buckets[state->i].front);
+ state->kv = state->m->buckets[state->i].front;
}
}
- state->kv->flags |= FLAG_ITER;
+ state->kv->val.flags |= FLAG_ITER;
memcpy(state->keyp, _map_kv_keyp(state->m, state->kv), state->m->sizeof_k);
*(state->valpp) = _map_kv_valp(state->m, state->kv);
return true;