Choosing a Marker
This content is for v1. Switch to the latest version for up-to-date documentation.
此内容尚不支持你的语言。
The bridge uses BRIDGE_* markers to decide how C++ functions are exposed to Dart. Choosing the wrong one can cause blocking, deadlocks, or performance issues. This page is a cheat sheet plus decision flowchart.
Overview
Section titled “Overview”| Marker | Execution Thread | C++ Return Type | Dart Return Type | Can Block | Can Call DartFn |
|---|---|---|---|---|---|
BRIDGE_SYNC |
io_context | T |
T |
❌ | ❌ (deadlock) |
BRIDGE_ASYNC |
io_context coroutine | async_simple::coro::Lazy<T> |
Future<T> |
❌ | ✅ (co_await) |
BRIDGE_NORMAL |
thread_pool | T |
Future<T> |
✅ | ✅ (syncAwait) |
| Stream | io_context | void + StreamSink<T> |
Stream<T> |
❌ | Depends on implementation |
Core principles:
- Never block the io_context thread
- DartFn cannot be used with
BRIDGE_SYNC
1. BRIDGE_SYNC — Synchronous Calls
Section titled “1. BRIDGE_SYNC — Synchronous Calls”The C++ function executes synchronously on the bridge’s io thread, and the result is returned to Dart immediately.
BRIDGE_SYNCstd::int32_t bridge_version() { return 42; }Suitable for:
- Pure computation, getters, constant reads
- Microsecond-level operations (usually < 1 μs)
- No file access, network, locks, or sleep
Not suitable for:
- Blocking calls
- Calling DartFn (permanent deadlock, because the Dart reply requires the io thread)
2. BRIDGE_ASYNC — Asynchronous Coroutines
Section titled “2. BRIDGE_ASYNC — Asynchronous Coroutines”The C++ function returns async_simple::coro::Lazy<T> and runs as a coroutine on the io thread; when it hits co_await, it suspends without occupying the thread.
BRIDGE_ASYNCasync_simple::coro::Lazy<std::int32_t> add(std::int32_t a, std::int32_t b) { co_return a + b;}
BRIDGE_ASYNCasync_simple::coro::Lazy<std::string> fetch_url(std::string url) { // You can co_await channels, sleep, DartFn, or spawn_blocking. auto result = co_await co::oneshot::recv(); co_return result;}Suitable for:
- Asynchronous IO
- Waiting for other coroutines / channels / Dart callbacks
- Composing multiple asynchronous operations
Not suitable for:
- Blocking operations (use
BRIDGE_NORMALorspawn_blocking)
3. BRIDGE_NORMAL — Normal Functions
Section titled “3. BRIDGE_NORMAL — Normal Functions”The C++ function is an ordinary function (it does not return Lazy); the bridge automatically dispatches it to the thread_pool. On the Dart side it is still Future<T>.
BRIDGE_NORMALstd::string sleep_greeting(std::string name) { std::this_thread::sleep_for(std::chrono::milliseconds(100)); return "Hello, " + name;}Suitable for:
- File IO, synchronous network IO libraries
- CPU-intensive computation
- Any operation that blocks or takes more than microseconds
Notes:
- The function can block internally because it runs on the thread pool, not the io thread
- You can still call DartFn via
async_simple::coro::syncAwait(dcb::spawn(...))
4. Stream — Streams
Section titled “4. Stream — Streams”A function is exported as a Dart Stream<T> when it carries an export marker
(BRIDGE_SYNC / BRIDGE_ASYNC / BRIDGE_NORMAL) and takes a required dcb::StreamSink<T>
parameter. The export marker is the gate: a StreamSink parameter alone does not export the
function (the generator warns and skips it). For plain void stream functions use
BRIDGE_NORMAL; the sink parameter makes it a stream regardless of the marker’s usual
scheduling, and the generated Dart API returns Stream<T>.
BRIDGE_NORMALvoid ticks(dcb::StreamSink<std::int32_t> sink, std::int32_t count) { for (std::int32_t i = 0; i < count; ++i) { sink.add(i); } sink.end();}Notes:
- The function may return immediately; keep the sink and call
add()later from any thread sink.error(msg)sends an error event;sink.end()closes the stream normally- Cancelling the subscription only stops Dart-side reception; the C++ side continues running
and later
add()calls are silently dropped std::optional<dcb::StreamSink<T>>makes the stream optional: the generated Dart signature takes aStreamController<T>?input parameter instead of returning aStream<T>; use it onBRIDGE_SYNC/BRIDGE_ASYNC/BRIDGE_NORMALfunctions (there is no separate stream marker; sync events are delivered after the FFI call returns)
See Streams for complete examples (required and optional streams, cancellation, threading, errors).
5. DartFn — Reverse Dart Closure Calls
Section titled “5. DartFn — Reverse Dart Closure Calls”dcb::DartFn<Ret(Args...)> represents a Dart closure. It is not a function marker itself, but a parameter type, and must be paired with BRIDGE_ASYNC or BRIDGE_NORMAL.
Asynchronous Calls (Recommended)
Section titled “Asynchronous Calls (Recommended)”Call it with co_await inside a BRIDGE_ASYNC coroutine; the io thread actually suspends.
BRIDGE_ASYNCasync_simple::coro::Lazy<std::string> greet( dcb::DartFn<std::string(std::string)> callback, std::string name) { auto reply = co_await callback(name); co_return "Dart said: " + reply;}Persistent Callbacks
Section titled “Persistent Callbacks”Use the BRIDGE_PERSIST marker so the closure is not automatically unregistered after the call returns; it can be stored and invoked repeatedly.
BRIDGE_SYNCBRIDGE_PERSISTbool register_callback(dcb::DartFn<std::string(std::string)> callback);
BRIDGE_NORMALstd::string invoke_callback(std::string name);Prohibited
Section titled “Prohibited”// ❌ DeadlockBRIDGE_SYNCstd::string bad(dcb::DartFn<std::string(std::string)> callback);Decision Flow
Section titled “Decision Flow”Need to return a Stream? → Yes: export marker (typically BRIDGE_NORMAL) + dcb::StreamSink<T> parameter → Optional: std::optional<dcb::StreamSink<T>> parameter on a sync/async/normal function
Need to call a Dart closure? → Yes: BRIDGE_ASYNC + DartFn (co_await) → Or: BRIDGE_NORMAL + syncAwait(dcb::spawn(fn(args)))
Will the function block / take time / do file IO? → Yes: BRIDGE_NORMAL
Is the function asynchronous, needing co_await / channel / sleep? → Yes: BRIDGE_ASYNC
Is it just pure computation / getter / microsecond-level operation? → Yes: BRIDGE_SYNCCommon Mistakes
Section titled “Common Mistakes”| Mistake | Consequence |
|---|---|
Blocking inside BRIDGE_SYNC |
io thread stalls, the entire bridge becomes unresponsive |
BRIDGE_SYNC + DartFn |
Permanent deadlock |
Blocking inside BRIDGE_ASYNC |
Same as blocking inside BRIDGE_SYNC |
Writing co_await inside BRIDGE_NORMAL |
Compile error, because it is not a coroutine |