summaryrefslogtreecommitdiff
path: root/libcr_ipc/rpc.c
blob: a648fde4dd2dae82331e170422b13e88308c911a (plain)
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;
	}
}