libxtc 0.4.0
Async concurrency for C: Tokio + Seastar + BEAM, in one library
Loading...
Searching...
No Matches
xtc_svr.h
1/*-
2 * Copyright (c) 2026, The XTC Project
3 * Use of this source code is governed by the ISC License.
4 *
5 * src/inc/xtc_svr.h
6 * The L4 gen_server: a structured pattern for a long-running
7 * process that handles three kinds of incoming traffic:
8 *
9 * - call: a synchronous request expecting a reply.
10 * - cast: a fire-and-forget command.
11 * - info: any other message that lands in the mailbox
12 * (timer ticks, monitor DOWNs, raw sends, etc.)
13 *
14 * The server runs as an xtc_proc. Behaviour is supplied via a
15 * vtable of callbacks. All callbacks run in the server's
16 * process; they may use any xtc_proc / xtc_sync API.
17 *
18 * Modeled on Erlang's gen_server. The xtc_call_t handle ties
19 * a synchronous reply back to its caller via xtc_chan_oneshot
20 * so the caller can park on the reply with a timeout.
21 */
22
23#ifndef XTC_SVR_H
24#define XTC_SVR_H
25
26#include <stddef.h>
27#include <stdint.h>
28
29#include "xtc.h"
30#include "xtc_chan.h"
31#include "xtc_loop.h"
32#include "xtc_proc.h"
33#include "xtc_sync.h"
34
35/* Result codes for handle_call / handle_cast / handle_info: the
36 * callback may either keep running (XTC_SVR_CONTINUE) or request
37 * the server to stop (XTC_SVR_STOP). handle_call may also defer its
38 * reply (XTC_SVR_NOREPLY): it saved the call with xtc_svr_call_save
39 * and will xtc_svr_reply later. */
40#define XTC_SVR_CONTINUE 0
41#define XTC_SVR_STOP 1
42#define XTC_SVR_NOREPLY 2
43
44typedef struct xtc_svr xtc_svr_t;
45typedef struct xtc_svr_call xtc_svr_call_t;
46
47typedef struct xtc_svr_callbacks {
48 int (*init) (void *state); /* OK to be NULL */
49 int (*handle_call) (void *state, const void *req, size_t req_size,
50 xtc_svr_call_t *call); /* required if calls used */
51 int (*handle_cast) (void *state, const void *msg, size_t size); /* OK to be NULL */
52 int (*handle_info) (void *state, const void *msg, size_t size); /* OK to be NULL */
53 /* Runs before the next recv when a callback armed a continuation via
54 * xtc_svr_continue(cont); `cont` is that argument. Lets init (or a
55 * handler) return fast -- unblocking the caller / the supervisor --
56 * and finish expensive work off the critical path, race-free (no
57 * self-send that could race an incoming message). A handle_continue
58 * may itself arm another continuation. OK to be NULL (an armed
59 * continuation with no handler is dropped). */
60 int (*handle_continue)(void *state, void *cont); /* OK to be NULL */
61 void (*terminate) (void *state, int reason); /* OK to be NULL */
63
64typedef struct xtc_svr_opts {
65 const char *name; /* optional, for logs */
66 size_t mailbox_cap; /* 0 = default */
68
69/*
70 * PUBLIC: int xtc_svr_start __P((xtc_loop_t *, const xtc_svr_callbacks_t *, void *, const xtc_svr_opts_t *, xtc_svr_t **));
71 * PUBLIC: int xtc_svr_stop __P((xtc_svr_t *));
72 * PUBLIC: int xtc_svr_join __P((xtc_svr_t *, int64_t));
73 * PUBLIC: xtc_pid_t xtc_svr_pid __P((const xtc_svr_t *));
74 *
75 * PUBLIC: int xtc_svr_call __P((xtc_pid_t, const void *, size_t, void **, size_t *, int64_t));
76 * PUBLIC: int xtc_svr_call_abortable __P((xtc_pid_t, const void *, size_t, void **, size_t *, int64_t, xtc_abort_token_t *));
77 * PUBLIC: int xtc_svr_cast __P((xtc_pid_t, const void *, size_t));
78 * PUBLIC: int xtc_svr_reply __P((xtc_svr_call_t *, const void *, size_t));
79 * PUBLIC: int xtc_svr_continue __P((void *));
80 * PUBLIC: xtc_svr_call_t *xtc_svr_call_save __P((const xtc_svr_call_t *));
81 */
82
83int xtc_svr_start(xtc_loop_t *loop,
84 const xtc_svr_callbacks_t *cb,
85 void *state,
86 const xtc_svr_opts_t *opts,
87 xtc_svr_t **out);
88
89int xtc_svr_stop(xtc_svr_t *svr);
90int xtc_svr_join(xtc_svr_t *svr, int64_t timeout_ns);
91xtc_pid_t xtc_svr_pid(const xtc_svr_t *svr);
92
93/* Synchronous call: send `req`, wait for reply, copy reply into a
94 * heap-allocated buffer that the caller must xtc_free. Returns:
95 * XTC_OK -- *out_reply / *out_size populated
96 * XTC_E_AGAIN -- timeout
97 * XTC_E_INVAL -- bad pid / not a server
98 */
99int xtc_svr_call(xtc_pid_t target,
100 const void *req, size_t req_size,
101 void **out_reply, size_t *out_size,
102 int64_t timeout_ns);
103
104/* Like xtc_svr_call, but cancellable: while waiting for the reply the
105 * abort token is polled, and the call returns XTC_E_ABORTED if it
106 * fires first. Fire the token's source (xtc_abort_source_fire) from
107 * a timeout or a cancel-request path -- the cooperative cancellation
108 * primitive (e.g. a statement timeout delivering a cancel at the next
109 * wait point). Cancellation stops only the caller's wait; the
110 * server keeps processing and a late reply is discarded. */
111int xtc_svr_call_abortable(xtc_pid_t target,
112 const void *req, size_t req_size,
113 void **out_reply, size_t *out_size,
114 int64_t timeout_ns, xtc_abort_token_t *tok);
115
116/* Fire-and-forget: send `msg` to the server. Server's handle_cast
117 * (if non-NULL) will see it; if NULL, falls through to handle_info. */
118int xtc_svr_cast(xtc_pid_t target, const void *msg, size_t size);
119
120/* From inside handle_call, send the reply and release the call.
121 * Each call must be replied exactly once. */
122int xtc_svr_reply(xtc_svr_call_t *call,
123 const void *reply, size_t size);
124
125/*
126 * Arm a continuation from within a server callback (init or a handle_*):
127 * handle_continue(state, cont) runs before the server's next recv. Use
128 * it to return from init quickly (unblocking the supervisor / the
129 * xtc_svr_start caller) and finish expensive setup before the first
130 * message, without the self-send that races an incoming message.
131 * Returns XTC_E_INVAL if called outside a server callback.
132 */
133int xtc_svr_continue(void *cont);
134
135/*
136 * Deferred reply (gen_server:reply/2). The xtc_svr_call_t passed to
137 * handle_call is valid only for the duration of that callback. To
138 * reply LATER -- after a batch fills, after other shards answer, after
139 * a timer fires -- call xtc_svr_call_save() inside handle_call to get
140 * a heap-allocated handle that outlives the callback, stash it in the
141 * server state, return XTC_SVR_NOREPLY, and call xtc_svr_reply() on
142 * the saved handle from any later callback. xtc_svr_reply frees a
143 * saved handle after sending. Each saved handle must be replied
144 * exactly once.
145 *
146 * Safe for in-proc callers (the reply routes to the caller's mailbox
147 * by tag, so it is harmless even if the caller has gone). For an
148 * off-proc caller the reply targets the caller's stack-resident reply
149 * slot, so the caller must remain blocked in xtc_svr_call (not time
150 * out) until the deferred reply is sent.
151 */
152xtc_svr_call_t *xtc_svr_call_save(const xtc_svr_call_t *call);
153
154#endif /* XTC_SVR_H */