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 void (*terminate) (void *state, int reason); /* OK to be NULL */
55
56typedef struct xtc_svr_opts {
57 const char *name; /* optional, for logs */
58 size_t mailbox_cap; /* 0 = default */
60
61/*
62 * PUBLIC: int xtc_svr_start __P((xtc_loop_t *, const xtc_svr_callbacks_t *, void *, const xtc_svr_opts_t *, xtc_svr_t **));
63 * PUBLIC: int xtc_svr_stop __P((xtc_svr_t *));
64 * PUBLIC: int xtc_svr_join __P((xtc_svr_t *, int64_t));
65 * PUBLIC: xtc_pid_t xtc_svr_pid __P((const xtc_svr_t *));
66 *
67 * PUBLIC: int xtc_svr_call __P((xtc_pid_t, const void *, size_t, void **, size_t *, int64_t));
68 * PUBLIC: int xtc_svr_call_abortable __P((xtc_pid_t, const void *, size_t, void **, size_t *, int64_t, xtc_abort_token_t *));
69 * PUBLIC: int xtc_svr_cast __P((xtc_pid_t, const void *, size_t));
70 * PUBLIC: int xtc_svr_reply __P((xtc_svr_call_t *, const void *, size_t));
71 * PUBLIC: xtc_svr_call_t *xtc_svr_call_save __P((const xtc_svr_call_t *));
72 */
73
74int xtc_svr_start(xtc_loop_t *loop,
75 const xtc_svr_callbacks_t *cb,
76 void *state,
77 const xtc_svr_opts_t *opts,
78 xtc_svr_t **out);
79
80int xtc_svr_stop(xtc_svr_t *svr);
81int xtc_svr_join(xtc_svr_t *svr, int64_t timeout_ns);
82xtc_pid_t xtc_svr_pid(const xtc_svr_t *svr);
83
84/* Synchronous call: send `req`, wait for reply, copy reply into a
85 * heap-allocated buffer that the caller must xtc_free. Returns:
86 * XTC_OK -- *out_reply / *out_size populated
87 * XTC_E_AGAIN -- timeout
88 * XTC_E_INVAL -- bad pid / not a server
89 */
90int xtc_svr_call(xtc_pid_t target,
91 const void *req, size_t req_size,
92 void **out_reply, size_t *out_size,
93 int64_t timeout_ns);
94
95/* Like xtc_svr_call, but cancellable: while waiting for the reply the
96 * abort token is polled, and the call returns XTC_E_ABORTED if it
97 * fires first. Fire the token's source (xtc_abort_source_fire) from
98 * a timeout or a cancel-request path -- the cooperative cancellation
99 * primitive (e.g. a statement timeout delivering a cancel at the next
100 * wait point). Cancellation stops only the caller's wait; the
101 * server keeps processing and a late reply is discarded. */
102int xtc_svr_call_abortable(xtc_pid_t target,
103 const void *req, size_t req_size,
104 void **out_reply, size_t *out_size,
105 int64_t timeout_ns, xtc_abort_token_t *tok);
106
107/* Fire-and-forget: send `msg` to the server. Server's handle_cast
108 * (if non-NULL) will see it; if NULL, falls through to handle_info. */
109int xtc_svr_cast(xtc_pid_t target, const void *msg, size_t size);
110
111/* From inside handle_call, send the reply and release the call.
112 * Each call must be replied exactly once. */
113int xtc_svr_reply(xtc_svr_call_t *call,
114 const void *reply, size_t size);
115
116/*
117 * Deferred reply (gen_server:reply/2). The xtc_svr_call_t passed to
118 * handle_call is valid only for the duration of that callback. To
119 * reply LATER -- after a batch fills, after other shards answer, after
120 * a timer fires -- call xtc_svr_call_save() inside handle_call to get
121 * a heap-allocated handle that outlives the callback, stash it in the
122 * server state, return XTC_SVR_NOREPLY, and call xtc_svr_reply() on
123 * the saved handle from any later callback. xtc_svr_reply frees a
124 * saved handle after sending. Each saved handle must be replied
125 * exactly once.
126 *
127 * Safe for in-proc callers (the reply routes to the caller's mailbox
128 * by tag, so it is harmless even if the caller has gone). For an
129 * off-proc caller the reply targets the caller's stack-resident reply
130 * slot, so the caller must remain blocked in xtc_svr_call (not time
131 * out) until the deferred reply is sent.
132 */
133xtc_svr_call_t *xtc_svr_call_save(const xtc_svr_call_t *call);
134
135#endif /* XTC_SVR_H */