summaryrefslogtreecommitdiff
path: root/libcr_ipc/rpc.c
diff options
context:
space:
mode:
Diffstat (limited to 'libcr_ipc/rpc.c')
-rw-r--r--libcr_ipc/rpc.c85
1 files changed, 85 insertions, 0 deletions
diff --git a/libcr_ipc/rpc.c b/libcr_ipc/rpc.c
new file mode 100644
index 0000000..a648fde
--- /dev/null
+++ b/libcr_ipc/rpc.c
@@ -0,0 +1,85 @@
+/* libcr_ipc/rpc.c - Simple request/response system for libcr
+ *
+ * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#include <string.h> /* for memcpy() */
+
+#include <libcr/coroutine.h> /* for cid_t, cr_* */
+
+#include <libcr_ipc/rpc.h>
+
+#include "_linkedlist.h"
+
+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 */
+};
+
+void _cr_rpc_send_req(struct _cr_rpc *ch, void *req_ptr, size_t req_size, void *resp_ptr) {
+ 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();
+ }
+}
+
+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;
+ }
+}