1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
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;
}
}
|