/* libcr_ipc/rpc.c - Simple request/response system for libcr * * Copyright (C) 2024-2025 Luke T. Shumaker * SPDX-License-Identifier: AGPL-3.0-or-later */ #include /* for memcpy() */ #include /* for cid_t, cr_* */ #include #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; } }