|
NeoGraph 0.16.0
A C++20 Graph Agent Engine Library — LangGraph for C++
|
Cooperative cancel primitive shared between caller and engine. More...
#include <cancel.h>
Public Member Functions | |
| asio::any_io_executor | bind_executor (asio::any_io_executor ex) |
Bind a serial executor that owns this token's cancellation_signal. | |
| void | cancel () noexcept |
| Request cancellation. | |
| std::shared_ptr< CancelToken > | fork () |
| Create a child token that cascades from this one. | |
| bool | is_cancelled () const noexcept |
| Thread-safe observation of the same state used by subscriptions. | |
| asio::cancellation_slot | slot () noexcept |
Cancellation slot for binding to a coroutine via asio::bind_cancellation_slot at co_spawn. | |
| std::stop_token | stop_token () const noexcept |
| Native cancellation subscriptions independent of Asio slot ownership. | |
| void | throw_if_cancelled (const std::string &detail={}) const |
Throws CancelledException if cancelled. | |
| void | unbind_executor () noexcept |
| Detach the serial executor after every bound operation drained. | |
Cooperative cancel primitive shared between caller and engine.
One stop source owns the cancellation state; Asio signals retain their serial-executor contract. Native subscriptions need no executor binding. Pass via RunConfig::cancel_token (shared_ptr) to opt in. Re-entrant: cancel() is idempotent; callers may share the token across concurrent runs to fan out a single abort.
|
inline |
Bind a serial executor that owns this token's cancellation_signal.
Returns the serial executor that must initiate every co_spawn bound to slot(). Callers running on another executor first hop with co_await asio::post(bound,
asio::use_awaitable). This keeps slot connection/destruction and emit() on one strand; generic multi-threaded executors do not provide that guarantee themselves.
If cancel() has already been requested, bind schedules an immediate emit. The coroutine started on the returned executor must still check throw_if_cancelled() before beginning I/O, because that queued emit may precede its slot connection.
|
inlinenoexcept |
Request cancellation.
Thread-safe, idempotent.
Sets the stop state before synchronously invoking native subscriptions, then posts the Asio signal onto its bound serial executor. Subscriptions run without either token mutex held and must not throw.
Safe before bind_executor(): native stop callbacks still run, and a subsequently bound coroutine observes the same eager stop state.
Engine-created operation children retain themselves until a posted emit executes. A directly constructed token has no such ownership source: callers that use bind_executor() themselves must keep that token alive until the executor has drained all posted work.
|
inline |
Create a child token that cascades from this one.
Each child has its own cancellation_signal, so concurrent consumers (such as multi-Send fan-out workers bridging HTTP coroutines through run_sync with their own io_context) never overwrite each other's cancellation slot. Calling cancel() on the parent walks the live children list and cascades — every child's signal fires on its own bound executor.
Lifetime / ownership: returns a shared_ptr<CancelToken>; the parent stores a weak_ptr so a forked child that goes out of scope is automatically pruned from the cascade list on the next cancel() / fork(). A forked child also records a weak self-reference in its existing child list. A posted signal emit locks and captures that reference, retaining engine operation children until the emit has executed.
Eager-cancel safety: if the parent is already cancelled at the time of fork(), the new child is constructed with its polling flag pre-set (is_cancelled() == true). When the caller subsequently bind_executor s the child, the eager- emit branch in bind_executor fires the child's signal on its first co_await checkpoint. This closes the v0.3.2 "emit-vs-bind race" without an explicit short-circuit at every consumer site.
Pass-by-value into a coroutine frame is fine — shared_ptr copy is cheap and the parent reference inside the child stays valid via shared ownership of the parent. (PR 2 trap shape does NOT apply.)
|
inlinenoexcept |
Cancellation slot for binding to a coroutine via asio::bind_cancellation_slot at co_spawn.
asio propagates the cancel through nested co_awaits down to socket operations.
Slots are not thread-safe; this is consumed once by the spawn site, then never read again by user code.
|
inline |
Throws CancelledException if cancelled.
Convenience wrapper used at engine super-step boundaries.
|
inlinenoexcept |
Detach the serial executor after every bound operation drained.
This does not drain slot-bound operations. The caller must first ensure that no signal handler or slot-bound operation remains active, then call it before destroying the executor's execution context. As with slot connection, rebinding must not race this teardown.
It serializes with cancel() and posted emits: detachment closes emit admission and waits for an in-progress emit to finish, while queued work from the old binding cannot emit into this or a later binding. Callers must still drain queued work before context teardown and keep directly constructed tokens alive until that drain.
Destroys the last installed slot handler while its context is alive. A completed co_spawn may leave that handler installed, retaining its strand independently of the executor stored in this token.
GraphEngine and the synchronous bridges call this on their internal operation children. Direct callers of bind_executor() must do the same after draining their executor when they retain the token longer than that executor.