Aria 2.0.0
C++23 MVVM framework (C++20 minimum) — reactive, coroutine-first, ABI-layered
Loading...
Searching...
No Matches
Async & Coroutines

Aria's async layer supports C++23 with a C++20 minimum and provides coroutine-based primitives for asynchronous work, built on top of the reactive graph. The key types:

  • Task<T> — lazy, single-shot coroutine awaitable
  • AsyncCommand<R, Args...> — three-state async action (executing / error / result)
  • CoroutineScope / ViewModelScope — structured concurrency tied to a lifetime
  • CancellationToken — cooperative cancellation
  • Combinatorswhen_all, when_any, with_timeout

Include: #include "aria/async/task.hpp", #include "aria/async/async_command.hpp", etc.


Task<T>

Task<T> is a lazy coroutine — it does nothing until co_awaited.

Basic Usage

aria::async::Task<int> compute_value() {
co_return 42;
}
aria::async::Task<void> show_result() {
int val = co_await compute_value();
std::cout << "Got: " << val << "\n";
}
Definition task.hpp:78

Void Specialization

aria::async::Task<void> log_message(std::string msg) {
std::cout << msg << "\n";
co_return;
}

Exception Handling

Exceptions thrown inside the coroutine body are stored and re-thrown at the co_await site:

throw std::runtime_error("boom");
co_return 0; // unreachable
}
try {
int v = co_await risky();
} catch (const std::runtime_error& e) {
// Caught here
}
}

Fire-and-Forget (Detached)

aria::async::Task<void> background_work() {
// Long-running work...
co_return;
}
// Start without awaiting — runs independently
background_work().start_detached();
void start_detached() &&
Start the task and detach it: the coroutine frame stays alive until the coroutine completes,...
Definition task.hpp:156

Pass a fresh task to start_detached(). An already-completed task is released without being resumed; a task still suspended in an asynchronous operation must remain under that operation's resumption control.

Warning: Detached tasks have no lifetime guard. Ensure captured references outlive the coroutine.


CancellationToken / CancellationSource

Cooperative cancellation. A CancellationSource owns the flag; a CancellationToken is a read-only view.

Destroying a source cancels its outstanding tokens and wakes cancellation waiters. Move assignment does the same for the destination's previous state before taking ownership of the incoming state; self-move is a no-op.

tok.is_cancelled(); // false
src.cancel();
tok.is_cancelled(); // true
Definition cancellation.hpp:218
void cancel()
Trigger cancellation.
Definition cancellation.hpp:239
CancellationToken token() const noexcept
Definition cancellation.hpp:222
Definition cancellation.hpp:182
bool is_cancelled() const noexcept
Definition cancellation.hpp:188

Inside a Coroutine

while (!tok.is_cancelled()) {
co_await aria::async::sleep_for(100ms);
tok.throw_if_cancelled(); // throws OperationCancelled
do_work();
}
}
void throw_if_cancelled() const
Throw OperationCancelled if cancelled. Call at safe await points.
Definition cancellation.hpp:193

Executor

Executors abstract scheduling — where coroutines run.

// Typically provided by the platform adapter
aria::async::IExecutor& ui; // main/UI thread
aria::async::IExecutor& worker; // background thread pool
Abstract executor interface — schedules a callable to run "somewhere".
Definition executor.hpp:36

Schedule On

Hop between executors:

aria::async::Task<UserProfile> load_profile(int uid) {
co_await aria::async::schedule_on(worker); // jump to background
auto data = fetch_from_db(uid); // blocking OK here
co_await aria::async::schedule_on(ui); // hop back to UI
co_return data;
}
auto schedule_on(IExecutor &exec)
Schedule a coroutine to resume on the given executor.
Definition executor.hpp:396

AsyncCommand<R, Args...>

An async command exposes three reactive properties that the UI can bind to:

Property Type Meaning
is_executing Property<bool> True while any invocation is in flight
last_error Property<std::optional<Error>> Most recent error, nullopt when OK
last_result Property<std::optional<R>> Most recent successful result (R ≠ void)

Basic Usage

[](std::string query) -> aria::async::Task<SearchResult> {
co_await aria::async::schedule_on(worker);
co_return perform_search(query);
}
};
// Trigger from UI
search.execute("hello");
// Bind in UI
search.is_executing.bind([](bool running) {
spinner.set_visible(running);
});
Definition async_command.hpp:462
void execute(Args... args)
Fire-and-forget.
Definition async_command.hpp:558
Property< bool > & is_executing
Definition async_command.hpp:585

Cancellable Action

Accept a CancellationToken as the first parameter:

co_await aria::async::schedule_on(worker);
tok.throw_if_cancelled();
co_return heavy_load(id);
}
};

Concurrency Policies

// Parallel (default): multiple invocations run concurrently
// LatestOnly: new execute() cancels in-flight work (search-as-you-type)
// DropIfRunning: ignore execute() while busy (prevent double-submit)
@ DropIfRunning
silently ignore new invocations while busy
Definition async_command.hpp:125
@ Parallel
default — all invocations run concurrently
Definition async_command.hpp:123
@ LatestOnly
cancel any in-flight invocations before starting
Definition async_command.hpp:124

Inside a ViewModel

class SearchVm : public aria::binding::ViewModel {
public:
: search(ui, worker,
[this](std::string q) -> aria::async::Task<Result> {
co_await aria::async::schedule_on(worker);
co_return do_search(q);
})
{
scope_.attach(*this);
}
aria::Property<std::string> query{""};
aria::async::AsyncCommand<Result, std::string> search;
private:
aria::binding::ViewModelScope scope_;
};
Base class for view models.
Definition view_model.hpp:32
Definition signal.hpp:12
Definition validation_key.hpp:110

ViewModelScope

Ties CoroutineScope to a ViewModel's lifetime. Destroying the VM cancels and joins all in-flight coroutines.

class PollingVm : public aria::binding::ViewModel {
public:
PollingVm() { scope_.attach(*this); }
void start() {
while (!tok.is_cancelled()) {
co_await aria::async::sleep_for(1s);
refresh();
}
});
}
private:
aria::binding::ViewModelScope scope_;
};

When PollingVm is destroyed, scope_ calls cancel_and_join() (default 5 s timeout). Stuck coroutines are reported as leaks.


Combinators

when_all

Wait for all tasks to complete:

auto [users, posts, comments] = co_await aria::async::when_all(
load_users(),
load_posts(),
load_comments()
);
}
auto when_all(Task< Ts >... tasks)
Definition when_all.hpp:192

when_any

Complete when the first task finishes (others are cancelled):

co_return co_await aria::async::when_any(
fetch_from_primary(),
fetch_from_backup()
);
}
auto when_any(std::vector< Task< T > > tasks)
Definition when_all.hpp:389

with_timeout

Abort if a task exceeds the deadline:

aria::async::Task<Data> fetch_with_deadline() {
co_return co_await aria::async::with_timeout(
slow_fetch(),
std::chrono::seconds(5)
);
}
auto with_timeout(IDelayedScheduler &timer, std::chrono::milliseconds duration, Factory factory, OnTimeout on_timeout=OnTimeout::Cancel) -> Task< detail::timeout_factory_value_t< Factory > >
Definition timeout.hpp:376

Quick Reference

Type Purpose Produces Value
Task<T> Lazy coroutine Yes (T)
AsyncCommand<R, Args...> Three-state async action Via last_result
CoroutineScope Launch + cancel coroutines No
ViewModelScope Scope tied to VM lifetime No
CancellationToken Cooperative cancellation check No
CancellationSource Cancel producer No
IExecutor Where to run No

See Also