Built-in Runtime
This content is for v1. Switch to the latest version for up-to-date documentation.
此内容尚不支持你的语言。
dart_cpp_bridge includes a built-in asio + async-simple runtime on the C++ side. By default, it starts automatically when DartCppBridge.init() is called from Dart; business code can write async_simple::coro::Lazy<T> coroutines directly without creating its own io_context or Executor. This chapter introduces the foundational tools and how to use them directly.
Libraries Ready to Use
Section titled “Libraries Ready to Use”The bridge already pulls in and initializes these for you via FetchContent:
- Asio standalone —
asio::io_contextsingle-threaded event loop +asio::thread_poolblocking thread pool - async-simple —
async_simple::coro::Lazy<T>coroutines andasync_simple::Executorscheduling model - moodycamel::ConcurrentQueue — the lock-free queue underlying
co::mpsc::unbounded<T>(concurrentqueue.h)
Common headers:
#include "dart_cpp_bridge/runtime.hpp" // Runtime, spawn, spawn_blocking#include "dart_cpp_bridge/asio_executor.hpp" // AsioExecutor#include "dart_cpp_bridge/channel.hpp" // co::oneshot / co::mpsc#include "async_simple/coro/Lazy.h" // Lazy<T>#include "async_simple/coro/Sleep.h" // sleepRuntime Singleton
Section titled “Runtime Singleton”dcb::Runtime is a process-wide singleton that holds:
asio::io_context— single-threaded event loop (io thread)dcb::AsioExecutor— async-simple’sExecutorimplementation that schedules coroutines back onto theio_contextasio::thread_pool— blocking worker pool (default 4 threads, adjustable viaset_pool_threads())
#include "dart_cpp_bridge/runtime.hpp"
// Manual start/stop (normally done automatically by Dart-side init; C++ unit tests need to call it manually)dcb::Runtime::instance().start();dcb::Runtime::instance().stop();Main interfaces:
| Interface | Purpose |
|---|---|
start() / stop() |
Start/stop the io thread and thread pool |
running() |
Whether it has started |
io() |
Get asio::io_context& |
pool() |
Get asio::thread_pool& |
executor() |
Get dcb::AsioExecutor* |
spawn_on_asio(factory) |
Post a Lazy factory from a non-coroutine context to the io thread to start |
co_await Directly in Business Coroutines
Section titled “co_await Directly in Business Coroutines”Business functions invoked by wire dispatch already run as Lazy<T> on the io thread, so you can use these tools directly:
async_simple::coro::Lazy<std::string> my_api(std::string input) { // Non-blocking sleep, backed by asio::steady_timer co_await async_simple::coro::sleep(std::chrono::milliseconds(100));
// Hand blocking work off to the thread pool auto result = co_await dcb::spawn_blocking([&] { return heavyComputation(input); });
co_return result;}spawn / spawn_detached
Section titled “spawn / spawn_detached”When you are not in a coroutine context (e.g., a normal function or callback) and want to post a Lazy to the io thread for execution:
#include "async_simple/coro/SyncAwait.h"
// Start and wait for the result (do not call on the io thread, or it will deadlock)auto result = async_simple::coro::syncAwait( dcb::spawn(my_coroutine()));
// Start and discard the result (fire-and-forget)dcb::spawn_detached(my_coroutine());dcb::spawn(lazy) returns a RescheduleLazy<T> already bound to the Runtime executor. It supports:
syncAwait(...)— block the current thread until completion.start(callback)— custom completion callbackspawn_detached(...)— start directly, ignoring results and exceptions
spawn_blocking
Section titled “spawn_blocking”Run blocking tasks on the thread_pool without blocking the io thread:
async_simple::coro::Lazy<int> compute(int n) { auto result = co_await dcb::spawn_blocking([n] { // Runs on a pool thread; can sleep or do synchronous IO int sum = 0; for (int i = 1; i <= n; ++i) sum += i; return sum; });
// Exceptions are caught on the pool thread and rethrown at the co_await site co_return result;}Asynchronous sleep
Section titled “Asynchronous sleep”async_simple::coro::sleep(dur) is non-blocking in a coroutine bound to AsioExecutor: AsioExecutor overrides async-simple’s schedule(Func, Duration) and uses asio::steady_timer, so it does not occupy a thread.
If the coroutine chain is bound to a cancellation signal (Lazy::setLazyLocal),
the sleep is also cancellable: emitting SignalType::Terminate cancels the
timer and the co_await sleep(...) throws async_simple::SignalException.
#include "async_simple/coro/Sleep.h"
async_simple::coro::Lazy<std::string> delayed_echo(std::string msg) { co_await async_simple::coro::sleep(std::chrono::seconds(1)); co_return msg;}Cross-Coroutine Communication: channel
Section titled “Cross-Coroutine Communication: channel”dart_cpp_bridge/channel.hpp provides two Tokio-style channel types for passing data between coroutines or across threads. co::mpsc::unbounded<T> uses moodycamel::ConcurrentQueue as its lock-free queue underneath.
oneshot — one-shot request/response
Section titled “oneshot — one-shot request/response”auto [tx, rx] = co::oneshot::channel<std::string>();
// Send from any threadtx.send("hello");
// Receive in a coroutineauto value = co_await rx.recv(); // std::optional<std::string>if (value) { /* ... */ }mpsc — multi-producer, single-consumer
Section titled “mpsc — multi-producer, single-consumer”auto [tx, rx] = co::mpsc::unbounded<int>();
// Send from any thread / multiple producerstx.send(1);tx.send(2);
// Receive in a coroutinewhile (auto v = co_await rx.recv()) { // process v}Sender is thread-safe and send() never blocks. Receiver::recv() must not be called concurrently.
Creating a Standalone Asio Runtime
Section titled “Creating a Standalone Asio Runtime”By default dcb::Runtime is a process-wide singleton. If you need an independent event loop isolated from the main Runtime (for example, a dedicated worker thread), you can assemble one yourself:
#include "dart_cpp_bridge/asio_executor.hpp"#include <asio/io_context.hpp>#include <asio/executor_work_guard.hpp>
asio::io_context ioc;auto guard = std::make_unique<asio::executor_work_guard<asio::io_context::executor_type>>( ioc.get_executor());auto ex = std::make_unique<dcb::AsioExecutor>(ioc);
std::thread t([&] { ex->set_io_thread_id(std::this_thread::get_id()); ioc.run();});
// You can then run coroutines on this standalone executor:// my_coroutine().via(ex.get()).start([](auto&&) {});For the full implementation, see examples/multi_runtime_demo/worker_runtime.hpp.
Threading Rules
Section titled “Threading Rules”Full Examples
Section titled “Full Examples”examples/base_demo— basic sync / async / stream / DartFnexamples/multi_runtime_demo— standalone AsioExecutor runtime + channelexamples/foreign_runtime_demo— non-asio runtime integration viaForeignExecutor