A11 (C++ runtime)
Native C++ implementation of the A11 action and streaming runtime
Loading...
Searching...
No Matches
action.h
Go to the documentation of this file.
1// Copyright 2026 The A11 Authors.
2
18#ifndef A11_ACTIONS_ACTION_H_
19#define A11_ACTIONS_ACTION_H_
20
21#include <cstddef>
22#include <functional>
23#include <memory>
24#include <optional>
25#include <string>
26#include <string_view>
27#include <vector>
28
29#include <absl/base/thread_annotations.h>
30#include <absl/container/flat_hash_map.h>
31#include <absl/container/flat_hash_set.h>
32#include <absl/status/status.h>
33#include <absl/status/statusor.h>
34#include <absl/time/time.h>
35
36#include "a11/actions/schema.h"
38#include "a11/data/types.h"
39#include "a11/obs/span.h"
40#include "thread/boost_primitives.h"
41
42namespace thread {
43class PermanentEvent;
44} // namespace thread
45
46namespace a11::net {
47class WireStream;
48} // namespace a11::net
49
50namespace a11::nodes {
51class AsyncNode;
52class NodeMap;
53} // namespace a11::nodes
54
55namespace a11::service {
56class Session;
57} // namespace a11::service
58
59namespace a11::actions {
60
61class Action;
62class ActionRegistry;
63
65inline constexpr size_t kDefaultMaxConcurrentNestedActions = 64;
66
68using ActionHandler = std::function<a11::Task(std::shared_ptr<Action>)>;
70using SyncActionHandler = std::function<absl::Status(std::shared_ptr<Action>)>;
72using OnActionCancelled = std::function<absl::Status(std::shared_ptr<Action>)>;
73
76
85 public:
87 static absl::StatusOr<std::shared_ptr<ActionLimiter>> Create(size_t maximum);
89 absl::Status Acquire();
91 void Release();
92
93 private:
94 explicit ActionLimiter(size_t maximum);
95
96 thread::Mutex mu_;
97 const size_t maximum_;
98 size_t active_ ABSL_GUARDED_BY(mu_) = 0;
99 std::shared_ptr<thread::PermanentEvent> changed_ ABSL_GUARDED_BY(mu_);
100};
101
112class Action : public std::enable_shared_from_this<Action> {
113 public:
126 static absl::StatusOr<std::shared_ptr<Action>> Create(
127 ActionSchema schema, std::string action_id = {},
128 ActionHandler handler = {},
129 std::shared_ptr<nodes::NodeMap> node_map = nullptr,
130 std::shared_ptr<net::WireStream> stream = nullptr,
131 std::shared_ptr<service::Session> session = nullptr,
132 std::shared_ptr<ActionRegistry> registry = nullptr,
133 size_t max_concurrent_nested_actions =
135
137 static absl::StatusOr<std::string> MakeNodeId(std::string_view action_id,
138 std::string_view node_name);
139
141 [[nodiscard]] std::string GetId() const;
143 absl::Status SetId(std::string action_id);
145 [[nodiscard]] ActionSchema GetSchema() const;
147 absl::Status SetSchema(ActionSchema schema);
149 absl::Status BindHandler(ActionHandler handler);
151 [[nodiscard]] ActionHandler GetHandler() const;
153 [[nodiscard]] bool HasHandler() const;
154
156 [[nodiscard]] ActionSettings GetSettings() const;
158 absl::Status SetSettings(ActionSettings settings);
160 absl::Status BindStreamsOnInputsByDefault(bool bind);
162 absl::Status BindStreamsOnOutputsByDefault(bool bind);
164 absl::Status ClearInputsAfterRun(bool clear = true);
166 absl::Status ClearOutputsAfterRun(bool clear = true);
167
169 absl::Status BindNodeMap(std::shared_ptr<nodes::NodeMap> node_map);
171 [[nodiscard]] std::shared_ptr<nodes::NodeMap> GetNodeMap() const;
173 absl::Status BindStream(std::shared_ptr<net::WireStream> stream);
175 [[nodiscard]] std::shared_ptr<net::WireStream> GetStream() const;
177 absl::Status BindRegistry(std::shared_ptr<ActionRegistry> registry);
179 [[nodiscard]] std::shared_ptr<ActionRegistry> GetRegistry() const;
181 absl::Status BindSession(std::shared_ptr<service::Session> session);
183 [[nodiscard]] std::shared_ptr<service::Session> GetSession() const;
184
186 absl::StatusOr<std::shared_ptr<nodes::AsyncNode>> GetNode(
187 std::string node_id);
194 absl::StatusOr<std::shared_ptr<nodes::AsyncNode>> GetInput(
195 std::string name, std::optional<bool> bind_stream = std::nullopt);
202 absl::StatusOr<std::shared_ptr<nodes::AsyncNode>> GetOutput(
203 std::string name, std::optional<bool> bind_stream = std::nullopt);
205 absl::StatusOr<std::shared_ptr<nodes::AsyncNode>> GetPort(std::string name);
207 [[nodiscard]] bool ContainsPort(std::string_view name) const;
208
210 [[nodiscard]] data::ActionMessage GetActionMessage() const;
212 absl::Status MapPortsFromMessage(const data::ActionMessage& message);
213
215 [[nodiscard]] data::ByteMap Headers() const;
217 absl::StatusOr<std::optional<data::Bytes>> GetHeader(
218 std::string_view name) const;
220 [[nodiscard]] bool HasHeader(std::string_view name) const;
222 absl::Status SetHeader(std::string name, data::Bytes value);
224 absl::Status RemoveHeader(std::string_view name);
226 absl::Status ForwardHeader(const std::shared_ptr<Action>& target,
227 std::string_view name) const;
229 absl::Status ForwardHeadersWithPrefix(
230 const std::shared_ptr<Action>& target,
231 std::string_view prefix = kActionHeaderPrefix) const;
232
246 absl::StatusOr<std::shared_ptr<Action>> MakeNested(
247 const ActionSchema& schema, bool propagate_io = true,
248 bool forward_headers = true);
260 absl::StatusOr<std::shared_ptr<Action>> MakeNested(
261 std::string_view action_name, bool propagate_io = true,
262 bool forward_headers = true);
263
273 absl::StatusOr<std::shared_ptr<Action>> Run();
286 absl::Duration timeout = absl::InfiniteDuration());
294 absl::Duration timeout = absl::InfiniteDuration());
296 absl::Status Cancel();
298 absl::Status SetOnCancelled(OnActionCancelled callback);
299
306 [[nodiscard]] std::string TraceId() const;
308 [[nodiscard]] std::string SpanId() const;
309
317 void SetSpanAttribute(std::string_view key, std::string_view value);
319 void SetSpanAttribute(std::string_view key, std::int64_t value);
321 void SetSpanAttribute(std::string_view key, bool value);
323 void SetSpanAttribute(std::string_view key, double value);
325 void SetSpanName(std::string_view name);
332 void SetSpanStatus(obs::SpanStatus status, std::string_view description = {});
333
335 [[nodiscard]] bool IsDone() const;
337 [[nodiscard]] bool HasBeenRun() const;
339 [[nodiscard]] bool HasBeenCalled() const;
341 [[nodiscard]] bool Cancelled() const;
343 [[nodiscard]] absl::Status GetStatus() const;
345 [[nodiscard]] std::optional<absl::Status> GetDispatchStatus() const;
346
347 private:
348 enum class Mode { kNone, kRun, kCall, kCancelled };
349
350 Action(ActionSchema schema, std::string id, ActionHandler handler,
351 std::shared_ptr<nodes::NodeMap> node_map,
352 std::shared_ptr<net::WireStream> stream,
353 std::shared_ptr<service::Session> session,
354 std::shared_ptr<ActionRegistry> registry,
355 std::shared_ptr<ActionLimiter> nested_limiter);
356
357 absl::Status Begin(Mode mode);
358 absl::Status RemapDefaultPorts() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mu_);
359 absl::Status AttachStreamIfRequested(
360 const std::shared_ptr<nodes::AsyncNode>& node, bool bind);
361 absl::Status ValidateMessagePorts(
362 const std::vector<data::Port>& ports,
363 const absl::flat_hash_map<std::string, ActionPortSchema>& schema_ports,
364 std::string_view kind) const;
365 void RunHandler(std::shared_ptr<ActionLimiter> limiter);
366 absl::Status ApplyInputAutofills();
367 [[nodiscard]] std::vector<data::NodeFragment> CollectAutofillFragments()
368 const;
369 void StartFinish(absl::Status status);
370 absl::Status FinishRun(absl::Status status);
371 absl::Status FinishOutputNodes(const absl::Status& status);
372 absl::Status CommunicateStatus(const absl::Status& status);
373 absl::Status AbortInputs(const absl::Status& status);
374 absl::Status SendNodeAbortStatuses(
375 const absl::flat_hash_set<std::string>& node_ids,
376 const absl::Status& status);
377 absl::Status ReleaseNodesAfterRun();
378 absl::Status DetachBoundStreamNodes();
379 absl::Status SendRemoteCancel();
380 void CompleteCall(absl::Status status, bool remove_from_session);
381 void AbortLocalCallOutputs(absl::Status status);
382 absl::Status TrackInSession(const std::shared_ptr<service::Session>& session);
383 void UntrackFromSession();
384 void SetDispatchStatus(absl::Status status);
385 void SetCompletionStatus(absl::Status status);
386
387 // Tracing hooks (a11::obs). StartActionSpan opens this action's span after a
388 // successful Begin(); it fails (so the action fails) when the reserved OTel
389 // headers are present but inconsistent, and is a no-op when none are present.
390 // EndActionSpan closes it with the final status. MakeChildSpan lets a child
391 // action open a span parented to this (parent) action's live span.
392 absl::Status StartActionSpan(Mode mode);
393 void EndActionSpan(const absl::Status& status);
394 obs::Span MakeChildSpan(std::string_view name, obs::SpanKind kind);
395 void RecordActionCallEvent(std::string_view name, std::string_view id);
396
397 mutable thread::Mutex mu_;
398 ActionSchema schema_ ABSL_GUARDED_BY(mu_);
399 ActionHandler handler_ ABSL_GUARDED_BY(mu_);
400 std::string id_ ABSL_GUARDED_BY(mu_);
401 data::ByteMap headers_ ABSL_GUARDED_BY(mu_);
402 ActionSettings settings_ ABSL_GUARDED_BY(mu_);
403 std::shared_ptr<nodes::NodeMap> node_map_ ABSL_GUARDED_BY(mu_);
404 std::shared_ptr<net::WireStream> stream_ ABSL_GUARDED_BY(mu_);
405 std::weak_ptr<service::Session> session_ ABSL_GUARDED_BY(mu_);
406 std::weak_ptr<service::Session> tracked_session_ ABSL_GUARDED_BY(mu_);
407 std::shared_ptr<ActionRegistry> registry_ ABSL_GUARDED_BY(mu_);
408 absl::flat_hash_map<std::string, std::string> input_ids_ ABSL_GUARDED_BY(mu_);
409 absl::flat_hash_map<std::string, std::string> output_ids_
410 ABSL_GUARDED_BY(mu_);
411 absl::flat_hash_set<std::shared_ptr<nodes::AsyncNode>> input_nodes_
412 ABSL_GUARDED_BY(mu_);
413 absl::flat_hash_set<std::shared_ptr<nodes::AsyncNode>> output_nodes_
414 ABSL_GUARDED_BY(mu_);
415 absl::flat_hash_set<std::shared_ptr<nodes::AsyncNode>> stream_bound_nodes_
416 ABSL_GUARDED_BY(mu_);
417 Mode mode_ ABSL_GUARDED_BY(mu_) = Mode::kNone;
418 bool input_autofills_applied_ ABSL_GUARDED_BY(mu_) = false;
419 a11::Task task_ ABSL_GUARDED_BY(mu_);
420 obs::Span span_ ABSL_GUARDED_BY(mu_);
421 bool span_status_set_by_user_ ABSL_GUARDED_BY(mu_) = false;
422 bool cancel_requested_ ABSL_GUARDED_BY(mu_) = false;
423 bool finishing_ ABSL_GUARDED_BY(mu_) = false;
424 std::optional<absl::Status> completion_status_ ABSL_GUARDED_BY(mu_);
425 std::optional<absl::Status> dispatch_status_ ABSL_GUARDED_BY(mu_);
426 std::shared_ptr<a11::Promise<a11::Unit>> done_promise_ ABSL_GUARDED_BY(mu_);
427 a11::Task done_future_ ABSL_GUARDED_BY(mu_);
428 std::shared_ptr<a11::Promise<a11::Unit>> dispatch_promise_
429 ABSL_GUARDED_BY(mu_);
430 a11::Task dispatch_future_ ABSL_GUARDED_BY(mu_);
431 std::vector<OnActionCancelled> cancel_callbacks_ ABSL_GUARDED_BY(mu_);
432 std::weak_ptr<Action> parent_ ABSL_GUARDED_BY(mu_);
433 absl::flat_hash_set<std::shared_ptr<Action>> children_ ABSL_GUARDED_BY(mu_);
434 std::shared_ptr<ActionLimiter> nested_limiter_ ABSL_GUARDED_BY(mu_);
435
436 friend class ActionRegistry;
437 friend class service::Session;
438};
439
440} // namespace a11::actions
441
442#endif // A11_ACTIONS_ACTION_H_
Shared handle to one asynchronous result.
Definition future.h:110
Cancellation-aware counting semaphore for nested-action concurrency.
Definition action.h:84
absl::Status Acquire()
Acquires a slot, blocking until one is free or cancelled.
Definition action.cc:119
void Release()
Releases a previously acquired slot.
Definition action.cc:139
static absl::StatusOr< std::shared_ptr< ActionLimiter > > Create(size_t maximum)
Creates a limiter admitting at most maximum holders.
Definition action.cc:105
A thread-safe catalogue mapping action names to schema and handler.
Definition registry.h:43
A11's unit of work: a schema-described, asynchronously run operation.
Definition action.h:112
absl::Status ClearInputsAfterRun(bool clear=true)
Sets whether inputs are released after each run.
Definition action.cc:331
void SetSpanStatus(obs::SpanStatus status, std::string_view description={})
Sets the span status explicitly.
Definition action.cc:1093
absl::StatusOr< std::shared_ptr< nodes::AsyncNode > > GetInput(std::string name, std::optional< bool > bind_stream=std::nullopt)
Returns the input port node named name.
Definition action.cc:447
absl::Status BindStream(std::shared_ptr< net::WireStream > stream)
Binds the wire stream used to dispatch the action remotely.
Definition action.cc:354
std::optional< absl::Status > GetDispatchStatus() const
The remote dispatch status, or nullopt when not (yet) dispatched.
Definition action.cc:973
bool HasBeenRun() const
Whether the action has been started with Run.
Definition action.cc:951
absl::Status SetId(std::string action_id)
Sets this action's instance id.
Definition action.cc:250
absl::Status BindSession(std::shared_ptr< service::Session > session)
Binds the owning session.
Definition action.cc:406
absl::StatusOr< std::optional< data::Bytes > > GetHeader(std::string_view name) const
Returns header name, or nullopt when absent.
Definition action.cc:581
data::ByteMap Headers() const
Returns a copy of all headers.
Definition action.cc:576
absl::Status SetOnCancelled(OnActionCancelled callback)
Registers a callback invoked when the action is cancelled.
Definition action.cc:938
std::string GetId() const
Returns this action's instance id.
Definition action.cc:245
absl::Status RemoveHeader(std::string_view name)
Removes header name.
Definition action.cc:610
std::string TraceId() const
This action's trace id as lowercase hex.
Definition action.cc:1058
bool HasBeenCalled() const
Whether the action has been dispatched with Call.
Definition action.cc:956
ActionSchema GetSchema() const
Returns this action's schema.
Definition action.cc:268
std::shared_ptr< nodes::NodeMap > GetNodeMap() const
Returns the bound node map.
Definition action.cc:349
std::shared_ptr< service::Session > GetSession() const
Returns the owning session, if any.
Definition action.cc:429
ActionSettings GetSettings() const
Returns the action's current settings.
Definition action.cc:308
void SetSpanName(std::string_view name)
Overrides the display name of this action's span.
Definition action.cc:1088
absl::Status SetSchema(ActionSchema schema)
Replaces this action's schema (validated).
Definition action.cc:273
bool ContainsPort(std::string_view name) const
Whether the schema declares a port named name.
Definition action.cc:531
absl::Status ForwardHeadersWithPrefix(const std::shared_ptr< Action > &target, std::string_view prefix=kActionHeaderPrefix) const
Copies all headers starting with prefix onto target.
Definition action.cc:631
absl::Status ClearOutputsAfterRun(bool clear=true)
Sets whether outputs are released after each run.
Definition action.cc:337
absl::Status BindRegistry(std::shared_ptr< ActionRegistry > registry)
Binds the registry used to resolve nested actions by name.
Definition action.cc:395
static absl::StatusOr< std::shared_ptr< Action > > Create(ActionSchema schema, std::string action_id={}, ActionHandler handler={}, std::shared_ptr< nodes::NodeMap > node_map=nullptr, std::shared_ptr< net::WireStream > stream=nullptr, std::shared_ptr< service::Session > session=nullptr, std::shared_ptr< ActionRegistry > registry=nullptr, size_t max_concurrent_nested_actions=kDefaultMaxConcurrentNestedActions)
Creates an action.
Definition action.cc:152
absl::StatusOr< std::shared_ptr< nodes::AsyncNode > > GetPort(std::string name)
Returns the input or output port node named name.
Definition action.cc:511
absl::Status MapPortsFromMessage(const data::ActionMessage &message)
Binds this action's ports to the nodes named in message.
Definition action.cc:554
absl::Status BindStreamsOnInputsByDefault(bool bind)
Sets whether input port streams are bound by default.
Definition action.cc:319
bool Cancelled() const
Whether cancellation has been requested/applied.
Definition action.cc:961
a11::Future< std::shared_ptr< Action > > Wait(absl::Duration timeout=absl::InfiniteDuration())
Awaits completion of the action.
Definition action.cc:857
a11::Future< std::shared_ptr< Action > > Call(data::ByteMap wire_headers={})
Dispatches the action to a peer over the bound wire stream.
Definition action.cc:759
absl::Status GetStatus() const
The action's completion status (OK while still running).
Definition action.cc:968
absl::Status SetHeader(std::string name, data::Bytes value)
Sets header name to value.
Definition action.cc:601
absl::Status BindNodeMap(std::shared_ptr< nodes::NodeMap > node_map)
Binds the node map that backs this action's ports.
Definition action.cc:343
absl::Status BindHandler(ActionHandler handler)
Binds the handler invoked when the action runs.
Definition action.cc:286
data::ActionMessage GetActionMessage() const
Returns the wire a11::data::ActionMessage describing this action.
Definition action.cc:537
absl::StatusOr< std::shared_ptr< nodes::AsyncNode > > GetOutput(std::string name, std::optional< bool > bind_stream=std::nullopt)
Returns the output port node named name.
Definition action.cc:479
std::string SpanId() const
This action's span id as lowercase hex, or empty when untraced.
Definition action.cc:1063
absl::Status Cancel()
Requests cancellation of the action (local or remote).
Definition action.cc:883
bool HasHandler() const
Whether a handler is bound.
Definition action.cc:303
bool IsDone() const
Whether the action has finished (successfully or not).
Definition action.cc:946
std::shared_ptr< ActionRegistry > GetRegistry() const
Returns the bound registry.
Definition action.cc:401
bool HasHeader(std::string_view name) const
Whether header name is set.
Definition action.cc:594
void SetSpanAttribute(std::string_view key, std::string_view value)
Sets a string attribute on this action's span.
Definition action.cc:1068
a11::Future< absl::Status > WaitForDispatch(absl::Duration timeout=absl::InfiniteDuration())
Awaits acceptance of a remote dispatch.
Definition action.cc:813
static absl::StatusOr< std::string > MakeNodeId(std::string_view action_id, std::string_view node_name)
Derives the node id for port node_name of action action_id.
Definition action.cc:230
absl::Status BindStreamsOnOutputsByDefault(bool bind)
Sets whether output port streams are bound by default.
Definition action.cc:325
absl::StatusOr< std::shared_ptr< Action > > Run()
Runs the action's handler locally.
Definition action.cc:709
std::shared_ptr< net::WireStream > GetStream() const
Returns the bound wire stream.
Definition action.cc:390
absl::StatusOr< std::shared_ptr< nodes::AsyncNode > > GetNode(std::string node_id)
Returns the port node with raw id node_id.
Definition action.cc:434
absl::Status ForwardHeader(const std::shared_ptr< Action > &target, std::string_view name) const
Copies header name from this action onto target.
Definition action.cc:619
absl::Status SetSettings(ActionSettings settings)
Replaces the action's settings.
Definition action.cc:313
ActionHandler GetHandler() const
Returns the currently bound handler.
Definition action.cc:298
absl::StatusOr< std::shared_ptr< Action > > MakeNested(const ActionSchema &schema, bool propagate_io=true, bool forward_headers=true)
Creates a nested action from a schema, parented to this action.
Definition action.cc:647
A bidirectional, message-oriented channel between two A11 endpoints.
Definition wire_stream.h:89
An asynchronous, ordered stream of chunks read from and written to A11.
Definition async_node.h:70
A thread-safe registry of AsyncNodes keyed by id.
Definition node_map.h:51
Move-only RAII handle to one action, session, or transport span.
Definition span.h:37
A connection-scoped runtime that multiplexes wire streams and runs actions.
Definition session.h:112
Completion values used by every asynchronous A11 operation.
Definition action.cc:46
ActionHandler MakeAsyncActionHandler(SyncActionHandler handler)
Adapts a synchronous handler into an asynchronous ActionHandler.
Definition action.cc:89
std::function< absl::Status(std::shared_ptr< Action >)> SyncActionHandler
Synchronous action handler returning a completion status.
Definition action.h:70
constexpr std::string_view kActionHeaderPrefix
Prefix reserved for A11's framework headers.
Definition schema.h:47
constexpr size_t kDefaultMaxConcurrentNestedActions
Default cap on concurrently running nested actions.
Definition action.h:65
std::function< absl::Status(std::shared_ptr< Action >)> OnActionCancelled
Callback invoked when an action is cancelled.
Definition action.h:72
std::function< a11::Task(std::shared_ptr< Action >)> ActionHandler
Asynchronous action handler: runs the action, returns an awaitable.
Definition action.h:68
std::string Bytes
Raw byte payload; an alias of std::string.
Definition types.h:40
absl::flat_hash_map< std::string, Bytes > ByteMap
String-keyed map of byte values (headers, attributes, etc.).
Definition types.h:42
Definition action.h:46
Definition action.h:50
SpanStatus
OpenTelemetry outcome states, mirrored to keep OTel out of this API.
Definition span.h:24
SpanKind
OpenTelemetry span relationships, mirrored to keep OTel out of this API.
Definition span.h:21
Definition action.h:55
Future< Unit > Task
Asynchronous operation whose only successful result is completion itself.
Definition future.h:403
Definition action.h:42
Schemas describing an action's typed interface and its settings.
The full typed interface of an action.
Definition schema.h:107
Per-action runtime settings for stream binding and cleanup.
Definition schema.h:139
The wire description of an action invocation.
Definition types.h:239
A11's core wire value types: chunks, node fragments and messages.