diff options
author | Luke T. Shumaker <lukeshu@lukeshu.com> | 2025-02-04 21:03:37 -0700 |
---|---|---|
committer | Luke T. Shumaker <lukeshu@lukeshu.com> | 2025-02-04 21:03:37 -0700 |
commit | 5c373cfe544df4ddba44adf3f6b60399340e4bd5 (patch) | |
tree | f96f5c57bbb042b3962bed49853621f6d344de04 | |
parent | 65f2a6fb8797385e06e61e13bac7a33d27f2097d (diff) | |
parent | 57d4c9f154cacd5d0dcc2f45033d784b3e0b6734 (diff) |
Merge branch 'lukeshu/9p-exit'
-rw-r--r-- | lib9p/include/lib9p/srv.h | 2 | ||||
-rw-r--r-- | lib9p/srv.c | 15 | ||||
-rwxr-xr-x | lib9p/tests/runtest | 9 | ||||
-rw-r--r-- | lib9p/tests/test_server/main.c | 105 | ||||
-rw-r--r-- | libcr/coroutine.c | 105 | ||||
-rw-r--r-- | libcr/include/libcr/coroutine.h | 20 | ||||
-rw-r--r-- | libcr_ipc/CMakeLists.txt | 4 | ||||
-rw-r--r-- | libcr_ipc/include/libcr_ipc/_linkedlist.h | 7 | ||||
-rw-r--r-- | libcr_ipc/include/libcr_ipc/rpc.h | 164 | ||||
-rw-r--r-- | libcr_ipc/tests/test_rpc.c | 59 | ||||
-rw-r--r-- | libhw/host_net.c | 5 | ||||
-rw-r--r-- | libmisc/include/libmisc/log.h | 16 |
12 files changed, 386 insertions, 125 deletions
diff --git a/lib9p/include/lib9p/srv.h b/lib9p/include/lib9p/srv.h index 55cf5db..d45d9a4 100644 --- a/lib9p/include/lib9p/srv.h +++ b/lib9p/include/lib9p/srv.h @@ -100,6 +100,8 @@ struct lib9p_srv { /* For internal use */ BEGIN_PRIVATE(LIB9P_SRV_H) + unsigned int readers; + unsigned int writers; _lib9p_srv_reqch_t _reqch; END_PRIVATE(LIB9P_SRV_H) }; diff --git a/lib9p/srv.c b/lib9p/srv.c index 7e6402e..10a8a96 100644 --- a/lib9p/srv.c +++ b/lib9p/srv.c @@ -200,6 +200,8 @@ static void handle_message(struct _lib9p_srv_req *ctx); assert(srv->rootdir); assert(listener); + srv->readers++; + uint32_t initial_rerror_overhead = rerror_overhead_for_version(0, buf); for (;;) { @@ -210,7 +212,11 @@ static void handle_message(struct _lib9p_srv_req *ctx); }; if (!conn.fd) { nonrespond_errorf("accept: error"); - continue; + srv->readers--; + if (srv->readers == 0) + while (srv->writers > 0) + _lib9p_srv_reqch_send_req(&srv->_reqch, NULL); + cr_exit(); } struct _srv_sess sess = { @@ -287,9 +293,16 @@ COROUTINE lib9p_srv_write_cr(void *_srv) { assert(srv->rootdir); cr_begin(); + srv->writers++; + for (;;) { /* Receive the request from the reader coroutine. ************/ rpc_handle = _lib9p_srv_reqch_recv_req(&srv->_reqch); + if (!rpc_handle.req) { + srv->writers--; + _lib9p_srv_reqch_send_resp(rpc_handle, 0); + cr_exit(); + } /* Deep-copy the request from the reader coroutine's * stack to our stack. */ req = *rpc_handle.req; diff --git a/lib9p/tests/runtest b/lib9p/tests/runtest index 29d2089..d963c53 100755 --- a/lib9p/tests/runtest +++ b/lib9p/tests/runtest @@ -26,7 +26,8 @@ while [[ -d /proc/$server_pid && "$(readlink /proc/$server_pid/fd/4 2>/dev/null) out=$("${client[@]}" ls -l '') expect_lines \ 'd-r-xr-xr-x M 0 root root 0 Oct 7 15:51 Documentation' \ - '--r--r--r-- M 0 root root 14 Oct 7 15:51 README.md' + '--r--r--r-- M 0 root root 14 Oct 7 15:51 README.md' \ + '---w--w--w- M 0 root root 0 Oct 7 15:51 shutdown' out=$("${client[@]}" ls -l 'Documentation/') expect_lines \ @@ -43,3 +44,9 @@ expect_lines \ out=$("${client[@]}" stat 'Documentation/x') expect_lines \ "'x' 'root' 'root' 'root' q (0000000000000009 1 ) m 0444 at 1728337905 mt 1728337904 l 4 t 0 d 0" + +out=$("${client[@]}" write 'shutdown' <<<1) +expect_lines '' + +wait "$server_pid" +trap - EXIT diff --git a/lib9p/tests/test_server/main.c b/lib9p/tests/test_server/main.c index 8e5011a..71c6cc2 100644 --- a/lib9p/tests/test_server/main.c +++ b/lib9p/tests/test_server/main.c @@ -24,7 +24,80 @@ #error config.h must define CONFIG_SRV9P_NUM_CONNS #endif -/* implementation *************************************************************/ +/* globals ********************************************************************/ + +static implements_lib9p_srv_file *get_root(struct lib9p_srv_ctx *, struct lib9p_s); + +const char *hexdig = "0123456789abcdef"; + +struct { + struct hostnet_tcp_listener listeners[CONFIG_SRV9P_NUM_CONNS]; + struct lib9p_srv srv; +} globals = { + .srv = (struct lib9p_srv){ + .rootdir = get_root, + }, +}; + +/* api ************************************************************************/ + +struct api_file { + implements_lib9p_srv_file; + + uint64_t pathnum; +}; + +static implements_lib9p_srv_file *api_clone(implements_lib9p_srv_file *self, struct lib9p_srv_ctx *) { return self; } +static void api_free(implements_lib9p_srv_file *, struct lib9p_srv_ctx *) {} + +static uint32_t api_io(implements_lib9p_srv_file *, struct lib9p_srv_ctx *, lib9p_o_t) { return 0; } +static void api_wstat(implements_lib9p_srv_file *, struct lib9p_srv_ctx *ctx, struct lib9p_stat) { lib9p_error(&ctx->basectx, LINUX_EROFS, "cannot wstat API file"); } +static void api_remove(implements_lib9p_srv_file *, struct lib9p_srv_ctx *ctx) { lib9p_error(&ctx->basectx, LINUX_EROFS, "cannot remove API file"); } + +static struct lib9p_stat api_stat(implements_lib9p_srv_file *_self, struct lib9p_srv_ctx *) { + struct api_file *self = VCALL_SELF(struct api_file, implements_lib9p_srv_file, _self); + return (struct lib9p_stat){ + .kern_type = 0, + .kern_dev = 0, + .file_qid = { + .type = LIB9P_QT_FILE, + .vers = 1, + .path = self->pathnum, + }, + .file_mode = 0222, + .file_atime = 1728337905, + .file_mtime = 1728337904, + .file_size = 0, + .file_name = lib9p_str("shutdown"), + .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 uint32_t api_pwrite(implements_lib9p_srv_file *, struct lib9p_srv_ctx *, void *, uint32_t byte_count, uint64_t) { + if (byte_count == 0) + return 0; + for (int i = 0; i < CONFIG_SRV9P_NUM_CONNS; i++) + VCALL(&globals.listeners[i], close); + return byte_count; +} + +static struct lib9p_srv_file_vtable api_file_vtable = { + .clone = api_clone, + .free = api_free, + + .io = api_io, + .stat = api_stat, + .wstat = api_wstat, + .remove = api_remove, + + .pread = NULL, + .pwrite = api_pwrite, +}; /* file tree ******************************************************************/ @@ -74,6 +147,10 @@ static struct util9p_static_dir root = { }, }), STATIC_FILE("README.md", README_md), + &((struct api_file){ + .vtable = &api_file_vtable, + .pathnum = __COUNTER__, + }), NULL, }, }; @@ -84,26 +161,17 @@ static implements_lib9p_srv_file *get_root(struct lib9p_srv_ctx *LM_UNUSED(ctx), /* main ***********************************************************************/ -static COROUTINE read_cr(void *_srv) { - struct lib9p_srv *srv = _srv; - assert(srv); - +static COROUTINE read_cr(void *_i) { + int i = *((int *)_i); cr_begin(); - struct hostnet_tcp_listener listener; - hostnet_tcp_listener_init(&listener, 9000); + hostnet_tcp_listener_init(&globals.listeners[i], 9000); - lib9p_srv_read_cr(srv, &listener); + lib9p_srv_read_cr(&globals.srv, &globals.listeners[i]); cr_end(); } -const char *hexdig = "0123456789abcdef"; - -struct lib9p_srv srv = { - .rootdir = get_root, -}; - static COROUTINE init_cr(void *) { cr_begin(); @@ -111,13 +179,13 @@ static COROUTINE init_cr(void *) { for (int i = 0; i < CONFIG_SRV9P_NUM_CONNS; i++) { char name[] = {'r', 'e', 'a', 'd', '-', hexdig[i], '\0'}; - if (!coroutine_add(name, read_cr, &srv)) - error(1, 0, "coroutine_add(read_cr, &srv)"); + if (!coroutine_add(name, read_cr, &i)) + error(1, 0, "coroutine_add(read_cr, &i)"); } for (int i = 0; i < 2*CONFIG_SRV9P_NUM_CONNS; i++) { char name[] = {'w', 'r', 'i', 't', 'e', '-', hexdig[i], '\0'}; - if (!coroutine_add(name, lib9p_srv_write_cr, &srv)) - error(1, 0, "coroutine_add(lib9p_srv_write_cr, &srv)"); + if (!coroutine_add(name, lib9p_srv_write_cr, &globals.srv)) + error(1, 0, "coroutine_add(lib9p_srv_write_cr, &globals.srv)"); } cr_exit(); @@ -126,4 +194,5 @@ static COROUTINE init_cr(void *) { int main() { coroutine_add("init", init_cr, NULL); coroutine_main(); + return 0; } diff --git a/libcr/coroutine.c b/libcr/coroutine.c index 708e2ea..c6eeb43 100644 --- a/libcr/coroutine.c +++ b/libcr/coroutine.c @@ -387,23 +387,24 @@ static_assert(CONFIG_COROUTINE_NUM > 1); /* types **********************************************************************/ -enum coroutine_state { - CR_NONE = 0, /* this slot in the table is empty */ - CR_INITIALIZING, /* running, before cr_begin() */ - CR_RUNNING, /* running, after cr_begin() */ - CR_RUNNABLE, /* not running, but runnable */ - CR_PAUSED, /* not running, and not runnable */ -}; - struct coroutine { + /* 1. state *************************************************/ volatile enum coroutine_state state; - cr_plat_jmp_buf env; + + /* 2. name **************************************************/ + [[gnu::nonstring]] char name[CONFIG_COROUTINE_NAME_LEN]; + + /* 3. stack *************************************************/ + /* stack_size *includes* CR_STACK_GUARD at each end. */ size_t stack_size; + /* stack is the bottom of the CR_STACK_GUARD at the bottom of the stack. */ void *stack; #if CONFIG_COROUTINE_VALGRIND unsigned stack_id; #endif - [[gnu::nonstring]] char name[CONFIG_COROUTINE_NAME_LEN]; + + /* 4. env ***************************************************/ + cr_plat_jmp_buf env; }; /* constants ******************************************************************/ @@ -476,11 +477,6 @@ static size_t coroutine_cnt = 0; /* utility functions **********************************************************/ -static inline const char* coroutine_state_str(enum coroutine_state state) { - assert(state < LM_ARRAY_LEN(coroutine_state_strs)); - return coroutine_state_strs[state]; -} - static inline void coroutine_ringbuf_push(cid_t val) { coroutine_ringbuf.buf[coroutine_ringbuf.head++ % LM_ARRAY_LEN(coroutine_ringbuf.buf)] = val; assert((coroutine_ringbuf.head % LM_ARRAY_LEN(coroutine_ringbuf.buf)) != @@ -576,37 +572,43 @@ cid_t coroutine_add_with_stack_size(size_t stack_size, return 0; debugf("...child=%zu", child); + /* 1. state *************************************************/ + coroutine_table[child-1].state = CR_INITIALIZING; + + /* 2. name **************************************************/ if (name) strncpy(coroutine_table[child-1].name, name, sizeof(coroutine_table[child-1].name)); else memset(coroutine_table[child-1].name, 0, sizeof(coroutine_table[child-1].name)); - coroutine_table[child-1].stack_size = stack_size; - infof("allocing \"%s\" stack with size %zu", name, stack_size); + /* 3. stack *************************************************/ + coroutine_table[child-1].stack_size = stack_size + 2*CR_STACK_GUARD_SIZE; + infof("allocing \"%s\" stack with size %zu+2*%zu=%zu", + name, stack_size, CR_STACK_GUARD_SIZE, coroutine_table[child-1].stack_size); coroutine_table[child-1].stack = - aligned_alloc(CR_PLAT_STACK_ALIGNMENT, stack_size); - infof("...done"); + aligned_alloc(CR_PLAT_STACK_ALIGNMENT, coroutine_table[child-1].stack_size); + infof("... done, stack is [%#zx,%#zx)", + coroutine_table[child-1].stack + CR_STACK_GUARD_SIZE, + coroutine_table[child-1].stack + CR_STACK_GUARD_SIZE + stack_size); #if CONFIG_COROUTINE_MEASURE_STACK || CONFIG_COROUTINE_PROTECT_STACK - for (size_t i = 0; i < stack_size; i++) + for (size_t i = 0; i < coroutine_table[child-1].stack_size; i++) ((uint8_t *)coroutine_table[child-1].stack)[i] = stack_pattern[i%sizeof(stack_pattern)]; #endif #if CONFIG_COROUTINE_VALGRIND coroutine_table[child-1].stack_id = VALGRIND_STACK_REGISTER( coroutine_table[child-1].stack + CR_STACK_GUARD_SIZE, - coroutine_table[child-1].stack + stack_size - CR_STACK_GUARD_SIZE); + coroutine_table[child-1].stack + CR_STACK_GUARD_SIZE + stack_size); #endif + /* 4. env ***************************************************/ coroutine_running = child; - coroutine_table[child-1].state = CR_INITIALIZING; coroutine_cnt++; if (!cr_setjmp(&coroutine_add_env)) { /* point=a */ void *stack_base = coroutine_table[child-1].stack + + CR_STACK_GUARD_SIZE #if CR_PLAT_STACK_GROWS_DOWNWARD + stack_size - - CR_STACK_GUARD_SIZE -#else - + CR_STACK_GUARD_SIZE #endif ; debugf("...stack =%p", coroutine_table[child-1].stack); @@ -785,20 +787,35 @@ void cr_assert_in_intrhandler(void) { } #endif -/* cr_cid_info() **************************************************************/ - -#if CONFIG_COROUTINE_MEASURE_STACK +/* answering questions about coroutines ***************************************/ void cr_cid_info(cid_t cid, struct cr_cid_info *ret) { - assert_cid(cid); + assert(cid > 0); + assert(cid <= CONFIG_COROUTINE_NUM); assert(ret); + memset(ret, 0, sizeof(*ret)); + if (coroutine_table[cid-1].state == CR_NONE) + return; + assert_cid(cid); + + /* 1. state *************************************************/ + ret->state = coroutine_table[cid-1].state; + + /* 2. name **************************************************/ + memcpy(ret->name, coroutine_table[cid-1].name, CONFIG_COROUTINE_NAME_LEN); + + /* 3. stack *************************************************/ +#if CONFIG_COROUTINE_MEASURE_STACK + uint8_t *stack = (uint8_t *)coroutine_table[cid-1].stack; + uint8_t *stack_lo = stack + CR_STACK_GUARD_SIZE; + uint8_t *stack_hi = stack + coroutine_table[cid-1].stack_size - CR_STACK_GUARD_SIZE; + /* stack_cap */ - ret->stack_cap = coroutine_table[cid-1].stack_size - 2*CR_STACK_GUARD_SIZE; + ret->stack_cap = stack_hi - stack_lo; /* stack_max */ ret->stack_max = ret->stack_cap; - uint8_t *stack = (uint8_t *)coroutine_table[cid-1].stack; for (;;) { size_t i = #if CR_PLAT_STACK_GROWS_DOWNWARD @@ -807,8 +824,17 @@ void cr_cid_info(cid_t cid, struct cr_cid_info *ret) { ret->stack_max - 1 - CR_STACK_GUARD_SIZE #endif ; - if (ret->stack_max == 0 || - stack[i] != stack_pattern[i%sizeof(stack_pattern)]) + if (ret->stack_max == 0) + break; + assert(stack_lo <= &stack[i] && &stack[i] < stack_hi); +#if CONFIG_COROUTINE_VALGRIND + VALGRIND_DISABLE_ERROR_REPORTING; +#endif + uint8_t v = stack[i]; +#if CONFIG_COROUTINE_VALGRIND + VALGRIND_ENABLE_ERROR_REPORTING; +#endif + if (v != stack_pattern[i%sizeof(stack_pattern)]) break; ret->stack_max--; } @@ -822,12 +848,17 @@ void cr_cid_info(cid_t cid, struct cr_cid_info *ret) { else sp = cr_plat_setjmp_get_sp(&coroutine_table[cid-1].env); assert(sp); - uintptr_t sb = (uintptr_t)coroutine_table[cid-1].stack; #if CR_PLAT_STACK_GROWS_DOWNWARD - ret->stack_cur = (sb - CR_STACK_GUARD_SIZE) - sp; + uintptr_t sb = (uintptr_t)stack_hi; + ret->stack_cur = sb - sp; #else - ret->stack_cur = sp - (sb + CR_STACK_GUARD_SIZE); + uintptr_t sb = (uintptr_t)stack_lo; + ret->stack_cur = sp - sb; #endif +#endif /* CONFIG_COROUTINE_MEASURE_STACK */ } -#endif /* CONFIG_COROUTINE_MEASURE_STACK */ +const char *coroutine_state_str(enum coroutine_state state) { + assert(state < LM_ARRAY_LEN(coroutine_state_strs)); + return coroutine_state_strs[state]; +} diff --git a/libcr/include/libcr/coroutine.h b/libcr/include/libcr/coroutine.h index eb5828b..756352e 100644 --- a/libcr/include/libcr/coroutine.h +++ b/libcr/include/libcr/coroutine.h @@ -1,6 +1,6 @@ /* libcr/coroutine.h - Simple embeddable coroutine implementation * - * 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 */ @@ -29,6 +29,8 @@ #include <stddef.h> /* for size_t */ #include <stdbool.h> /* for bool */ +#include "config.h" + /* typedefs *******************************************************************/ /** @@ -187,13 +189,25 @@ void cr_assert_in_intrhandler(void); /* answering questions about coroutines ***************************************/ -/* While the following are defined here unconditionally, the - * implementations are #if'd on CONFIG_COROUTINE_MEASURE_STACK. */ +enum coroutine_state { + CR_NONE = 0, /* this slot in the table is empty */ + CR_INITIALIZING, /* running, before cr_begin() */ + CR_RUNNING, /* running, after cr_begin() */ + CR_RUNNABLE, /* not running, but runnable */ + CR_PAUSED, /* not running, and not runnable */ +}; + +const char *coroutine_state_str(enum coroutine_state); struct cr_cid_info { + enum coroutine_state state; + [[gnu::nonstring]] char name[CONFIG_COROUTINE_NAME_LEN]; + +#if CONFIG_COROUTINE_MEASURE_STACK size_t stack_cap; size_t stack_max; size_t stack_cur; +#endif }; void cr_cid_info(cid_t cid, struct cr_cid_info *ret); diff --git a/libcr_ipc/CMakeLists.txt b/libcr_ipc/CMakeLists.txt index 3ab56ab..05e6fa3 100644 --- a/libcr_ipc/CMakeLists.txt +++ b/libcr_ipc/CMakeLists.txt @@ -1,6 +1,6 @@ # libcr_ipc/CMakeLists.txt - TODO # -# 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 add_library(libcr_ipc INTERFACE) @@ -14,7 +14,7 @@ set(ipc_tests #select #mutex #owned_mutex - #rpc + rpc #sema ) if (ENABLE_TESTS) diff --git a/libcr_ipc/include/libcr_ipc/_linkedlist.h b/libcr_ipc/include/libcr_ipc/_linkedlist.h index 67bff24..543e058 100644 --- a/libcr_ipc/include/libcr_ipc/_linkedlist.h +++ b/libcr_ipc/include/libcr_ipc/_linkedlist.h @@ -1,6 +1,6 @@ /* libcr_ipc/_linkedlist.h - Common low-level linked lists for use in libcr_ipc * - * 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 */ @@ -33,10 +33,9 @@ static inline void _cr_ipc_sll_push_to_rear(_cr_ipc_sll_root *root, _cr_ipc_sll_ node->rear = NULL; if (root->rear) root->rear->rear = node; - else { + else root->front = node; - root->rear = node; - } + root->rear = node; } static inline void _cr_ipc_sll_pop_from_front(_cr_ipc_sll_root *root) { diff --git a/libcr_ipc/include/libcr_ipc/rpc.h b/libcr_ipc/include/libcr_ipc/rpc.h index 80eee74..0ff8bbf 100644 --- a/libcr_ipc/include/libcr_ipc/rpc.h +++ b/libcr_ipc/include/libcr_ipc/rpc.h @@ -8,6 +8,7 @@ #define _LIBCR_IPC_RPC_H_ #include <stdbool.h> /* for bool */ +#include <string.h> /* for memcpy() */ #include <libcr/coroutine.h> /* for cid_t, cr_* */ @@ -34,6 +35,12 @@ * * _recv_req() and _send_resp(). * typedef ... NAME##_t; * + * /** + * * A NAME##_req_t is handle that wraps a REQ_T and is used to return + * * the response RESP_T to the correct requester. `REQ_T req` is the + * * only public member. + * typedef struct { REQ_T req; ... } NAME##_req_t; + * * methods: * * /** @@ -89,71 +96,41 @@ #define CR_RPC_DECLARE(NAME, REQ_T, RESP_T) \ typedef struct { \ REQ_T req; \ - \ - RESP_T *_resp; \ + \ + RESP_T *_resp; /* where to write resp to */ \ cid_t _requester; \ } NAME##_req_t; \ - \ - struct _##NAME##_waiting_req { \ - _cr_ipc_sll_node; \ - REQ_T *req; \ - RESP_T *resp; \ - cid_t cid; \ - }; \ - \ - struct _##NAME##_waiting_resp { \ - _cr_ipc_sll_node; \ - cid_t cid; \ - }; \ - \ + \ typedef struct { \ - _cr_ipc_sll_root waiting_reqs; \ - _cr_ipc_sll_root waiting_resps; \ + 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; \ - struct _##NAME##_waiting_req self = { \ - .req = &req, \ - .resp = &resp, \ - .cid = cr_getcid(), \ - }; \ - _cr_ipc_sll_push_to_rear(&ch->waiting_reqs, &self); \ - if (ch->waiting_resps.front) { \ - cr_unpause(_cr_ipc_sll_node_cast(struct _##NAME##_waiting_resp, ch->waiting_resps.front)->cid); \ - _cr_ipc_sll_pop_from_front(&ch->waiting_resps); \ - } \ - cr_pause_and_yield(); \ + _cr_rpc_send_req(&ch->core, \ + &req, sizeof(req), \ + &resp, sizeof(resp)); \ return resp; \ } \ - \ + \ static inline NAME##_req_t NAME##_recv_req(NAME##_t *ch) { \ cr_assert_in_coroutine(); \ - if (!ch->waiting_reqs.front) { \ - struct _##NAME##_waiting_resp self = { \ - .cid = cr_getcid(), \ - }; \ - _cr_ipc_sll_push_to_rear(&ch->waiting_resps, &self); \ - cr_pause_and_yield(); \ - } \ - assert(ch->waiting_reqs.front); \ - struct _##NAME##_waiting_req *front_req = \ - _cr_ipc_sll_node_cast(struct _##NAME##_waiting_req, ch->waiting_reqs.front); \ - NAME##_req_t ret = { \ - .req = *(front_req->req), \ - ._resp = front_req->resp, \ - ._requester = front_req->cid, \ - }; \ - _cr_ipc_sll_pop_from_front(&ch->waiting_reqs); \ + 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->waiting_reqs.front != NULL; \ + 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; \ @@ -161,4 +138,93 @@ cr_yield(); \ } +enum _cr_rpc_waiter_typ { + _CR_RPC_REQUESTER, + _CR_RPC_RESPONDER, +}; + +struct _cr_rpc_requester { + _cr_ipc_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 { + _cr_ipc_sll_node; + /* /* before enqueued | after dequeued */ + /* /* -------------------+-------------------- */ + cid_t cid; /* responder cid | requester cid */ + void *ptr; /* where to write req | where to write resp */ +}; + +struct _cr_rpc { + enum _cr_rpc_waiter_typ waiter_typ; + _cr_ipc_sll_root waiters; +}; + +static inline void _cr_rpc_send_req(struct _cr_rpc *ch, + void *req_ptr, size_t req_size, + void *resp_ptr, size_t resp_size) +{ + assert(ch); + assert(req_ptr); + assert(resp_ptr); + + if (ch->waiters.front && ch->waiter_typ != _CR_RPC_REQUESTER) { /* fast-path (still blocks) */ + struct _cr_rpc_responder *responder = + _cr_ipc_sll_node_cast(struct _cr_rpc_responder, ch->waiters.front); + _cr_ipc_sll_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. */ + cr_unpause(responder->cid); + responder->cid = cr_getcid(); + responder->ptr = resp_ptr; + /* Wait for the responder to set `*resp_ptr`. */ + cr_pause_and_yield(); + } else { /* blocking slow-path */ + struct _cr_rpc_requester self = { + .cid = cr_getcid(), + .req_ptr = req_ptr, + .resp_ptr = resp_ptr, + }; + _cr_ipc_sll_push_to_rear(&ch->waiters, &self); + /* Wait for a responder to both copy our req and sed + * `*resp_ptr`. */ + cr_pause_and_yield(); + } +} + +static inline void _cr_rpc_recv_req(struct _cr_rpc *ch, + void *req_ptr, size_t req_size, + void **ret_resp_ptr, + cid_t *ret_requester) +{ + assert(ch); + assert(req_ptr); + assert(ret_resp_ptr); + assert(ret_requester); + + if (ch->waiters.front && ch->waiter_typ != _CR_RPC_RESPONDER) { /* non-blocking fast-path */ + struct _cr_rpc_requester *requester = + _cr_ipc_sll_node_cast(struct _cr_rpc_requester, ch->waiters.front); + _cr_ipc_sll_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 = { + .cid = cr_getcid(), + .ptr = req_ptr, + }; + _cr_ipc_sll_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; + } +} + #endif /* _LIBCR_IPC_RPC_H_ */ diff --git a/libcr_ipc/tests/test_rpc.c b/libcr_ipc/tests/test_rpc.c new file mode 100644 index 0000000..4aff5ca --- /dev/null +++ b/libcr_ipc/tests/test_rpc.c @@ -0,0 +1,59 @@ +/* libcr_ipc/tests/test_rpc.c - Tests for <libcr_ipc/rpc.h> + * + * Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com> + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +#include <libcr/coroutine.h> +#include <libcr_ipc/rpc.h> + +#include "test.h" + +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) { + intrpc_t *ch = _ch; + cr_begin(); + + int resp = intrpc_send_req(ch, 1); + test_assert(resp == 2); + + resp = intrpc_send_req(ch, 3); + test_assert(resp == 4); + + cr_exit(); +} + +COROUTINE cr_worker1(void *_ch) { + intrpc_t *ch = _ch; + cr_begin(); + + intrpc_req_t req = intrpc_recv_req(ch); + test_assert(req.req == 1); + intrpc_send_resp(req, 2); + + cr_exit(); +} + +COROUTINE cr_worker2(void *_ch) { + intrpc_t *ch = _ch; + cr_begin(); + + intrpc_req_t req = intrpc_recv_req(ch); + test_assert(req.req == 3); + intrpc_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_main(); + return 0; +} diff --git a/libhw/host_net.c b/libhw/host_net.c index d5a5ad5..bf79480 100644 --- a/libhw/host_net.c +++ b/libhw/host_net.c @@ -190,6 +190,9 @@ static implements_net_stream_conn *hostnet_tcplist_accept(implements_net_stream_ if (RUN_PTHREAD(hostnet_pthread_accept, &args)) return NULL; + if (ret_connfd < 0) + return NULL; + listener->active_conn.vtable = &hostnet_tcp_conn_vtable; listener->active_conn.fd = ret_connfd; listener->active_conn.read_deadline_ns = 0; @@ -203,7 +206,7 @@ static int hostnet_tcplist_close(implements_net_stream_listener *_listener) { VCALL_SELF(struct hostnet_tcp_listener, implements_net_stream_listener, _listener); assert(listener); - return hostnet_map_negerrno(close(listener->fd) ? -errno : 0, OP_NONE); + return hostnet_map_negerrno(shutdown(listener->fd, SHUT_RDWR) ? -errno : 0, OP_NONE); } /* TCP read() *****************************************************************/ diff --git a/libmisc/include/libmisc/log.h b/libmisc/include/libmisc/log.h index 121b0e1..79c0ab6 100644 --- a/libmisc/include/libmisc/log.h +++ b/libmisc/include/libmisc/log.h @@ -1,6 +1,6 @@ /* libmisc/log.h - stdio logging * - * 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 */ @@ -12,25 +12,23 @@ #include <libmisc/macro.h> #include <libmisc/_intercept.h> -#ifndef LOG_NAME - #error "each compilation unit that includes <libmisc/log.h> must define LOG_NAME" -#endif - #ifdef NDEBUG #define _LOG_NDEBUG 1 #else #define _LOG_NDEBUG 0 #endif +const char *const_byte_str(uint8_t b); + #define n_errorf(nam, fmt, ...) do { __lm_printf("error: " LM_STR_(nam) ": " fmt "\n" __VA_OPT__(,) __VA_ARGS__); } while (0) #define n_infof(nam, fmt, ...) do { __lm_printf("info : " LM_STR_(nam) ": " fmt "\n" __VA_OPT__(,) __VA_ARGS__); } while (0) #define n_debugf(nam, fmt, ...) do { if (LM_CAT3_(CONFIG_, nam, _DEBUG) && !_LOG_NDEBUG) \ __lm_printf("debug: " LM_STR_(nam) ": " fmt "\n" __VA_OPT__(,) __VA_ARGS__); } while (0) +#endif /* _LIBMISC_LOG_H_ */ + +#if defined(LOG_NAME) && !defined(errorf) #define errorf(fmt, ...) n_errorf(LOG_NAME, fmt, __VA_ARGS__) #define infof(fmt, ...) n_infof(LOG_NAME, fmt, __VA_ARGS__) #define debugf(fmt, ...) n_debugf(LOG_NAME, fmt, __VA_ARGS__) - -const char *const_byte_str(uint8_t b); - -#endif /* _LIBMISC_LOG_H_ */ +#endif |