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