libxtc 0.4.0
Async concurrency for C: Tokio + Seastar + BEAM, in one library
Loading...
Searching...
No Matches
xtc_dispatch.h
1/*-
2 * Copyright (c) 2026, The XTC Project
3 * @stability stable
4 * Use of this source code is governed by the ISC License,
5 * a copy of which is in the file LICENSE in the top-level directory
6 * of this distribution.
7 *
8 * src/inc/xtc_dispatch.h
9 * Dispatcher: the callback -> fiber bridge (roadmap B1, Cats
10 * Effect's Dispatcher). The blessed one-call front door for
11 * crossing INTO the runtime from an IMPURE callback or a FOREIGN
12 * OS thread -- a C library's completion callback, a signal
13 * handler's follow-up, an embedder's own I/O thread -- with a way
14 * to await the effect's result and to cancel it.
15 *
16 * This is the inverse of xtc_blocking (xtc_blocking.h), which runs
17 * blocking work OFF a loop: the dispatcher gets work ON a loop from
18 * the outside.
19 *
20 * Model. xtc_dispatch(loop, fn, arg, &fut, &h) spawns a fiber on
21 * `loop` that runs fn(arg) to completion, then completes a future
22 * with fn's return value (as the intptr_t value, status XTC_OK).
23 * The caller -- whatever thread it is on -- awaits that future
24 * (xtc_future_await from a fiber, xtc_future_wait from a plain OS
25 * thread) for the result, and may xtc_dispatch_cancel(h) to abort a
26 * dispatch that has not completed. The future ALWAYS resolves
27 * exactly once: with fn's value on completion, or with
28 * XTC_E_ABORTED if the dispatched fiber is cancelled or crashes
29 * before it returns -- never lost, never doubled, never a hang.
30 *
31 * Thread-safety. xtc_dispatch and xtc_dispatch_cancel are safe to
32 * call from ANY OS thread, including one libxtc does not manage.
33 * This is not new machinery: it inherits the thread-safety of
34 * xtc_proc_spawn (a cross-thread spawn posts to the target loop's
35 * MPSC inbox and pings its I/O backend; see src/evt/task.c
36 * __xtc_task_spawn_ex) and of xtc_promise_set (documented safe from
37 * any thread; see xtc_future.h). The dispatcher is the ergonomic
38 * composition of those two proven primitives plus the A1/A2
39 * scope/cancellation core, packaged as the front door consumers
40 * kept reinventing.
41 */
42
43#ifndef XTC_DISPATCH_H
44#define XTC_DISPATCH_H
45
46#include "xtc_export.h"
47
48#include <stdint.h>
49
50#include "xtc.h"
51#include "xtc_loop.h"
52#include "xtc_future.h"
53
54typedef struct xtc_dispatch_handle xtc_dispatch_handle_t;
55
56/*
57 * PUBLIC: int xtc_dispatch __P((xtc_loop_t *, int (*)(void *), void *, xtc_future_t **, xtc_dispatch_handle_t **));
58 *
59 * Submit fn(arg) to run as a fiber on `loop` and hand back a way to
60 * await its result and to cancel it. Callable from ANY OS thread.
61 *
62 * out_future -- on XTC_OK, receives a future that resolves with fn's
63 * return value (value = (intptr_t)fn(arg), status
64 * XTC_OK) when fn returns, or (value 0, status
65 * XTC_E_ABORTED) if the dispatch is cancelled or the
66 * fiber crashes first. The caller owns and consumes it
67 * with xtc_future_await / xtc_future_wait, exactly like
68 * any other future. Required (non-NULL).
69 * out_handle -- on XTC_OK, receives an opaque cancel handle. May be
70 * NULL if the caller never needs to cancel (fire and
71 * await). If non-NULL, the caller MUST eventually pass
72 * it to xtc_dispatch_cancel OR xtc_dispatch_handle_free
73 * to release it (cancel frees it too).
74 *
75 * Returns XTC_OK, XTC_E_INVAL (NULL loop / fn / out_future),
76 * XTC_E_NOMEM, or a spawn error (e.g. XTC_E_RESOURCE if the loop's
77 * proc/task caps are hit). On any error nothing is spawned and neither
78 * out is written to a live object.
79 */
80XTC_API int xtc_dispatch(xtc_loop_t *loop, int (*fn)(void *), void *arg,
81 xtc_future_t **out_future, xtc_dispatch_handle_t **out_handle);
82
83/*
84 * PUBLIC: int xtc_dispatch_cancel __P((xtc_dispatch_handle_t *));
85 *
86 * Request cancellation of the dispatched fiber and release the handle.
87 * Callable from ANY OS thread. Cancellation is COOPERATIVE and
88 * composes with A1/A2: the dispatched fiber observes it at its next
89 * cancellation point (a park, xtc_cancel_requested, or the end of an
90 * xtc_uncancelable region), runs any xtc_scope / at-exit finalizers,
91 * and unwinds -- so a resource acquired under a scope is still
92 * released. If the fiber has already completed, this is a harmless
93 * no-op on the result (the future keeps fn's value). Either way the
94 * future is guaranteed already-resolved-or-will-resolve exactly once.
95 * The handle is consumed; do not use it again. Returns XTC_OK, or
96 * XTC_E_INVAL on a NULL handle.
97 */
98XTC_API int xtc_dispatch_cancel(xtc_dispatch_handle_t *h);
99
100/*
101 * PUBLIC: void xtc_dispatch_handle_free __P((xtc_dispatch_handle_t *));
102 *
103 * Release a cancel handle WITHOUT cancelling (the caller decided it
104 * will never cancel this dispatch). A no-op on NULL. Do not use the
105 * handle after this. Not needed if out_handle was passed NULL to
106 * xtc_dispatch, or if xtc_dispatch_cancel was already called.
107 */
108XTC_API void xtc_dispatch_handle_free(xtc_dispatch_handle_t *h);
109
110#endif /* XTC_DISPATCH_H */