Prerequisites
This tutorial builds on the getting started tutorials.
Overview
The full example code for this tutorial is located at //examples/fidl/cpp/server_async_completer.
In the Echo
implementation from the initial server tutorial, the
server code responded to EchoString
requests using the completer:
void EchoString(EchoStringRequest& request, EchoStringCompleter::Sync& completer) override {
// Call |Reply| to reply synchronously with the request value.
completer.Reply({{.response = request.value()}});
}
Notice that the type of the completer ends with ::Sync
. Sync completers must
be used before the handler returns. Enforcing this allows optimizations since
the bookkeeping metadata for making a reply can be stack allocated.
Respond asynchronously
In many cases responding synchronously is infeasible. For example, the reply may
need results from other asynchronous calls. To respond asynchronously, we must
obtain an async completer from the sync completer, using ToAsync
:
EchoStringCompleter::Async async_completer = completer.ToAsync();
The resulting async_completer
exposes the same API as completer
, but can be
moved away to be used at a later time.
In the example code, the server makes the reply inside a delayed task, emulating a scenario where the server must execute a long running task before sending a response:
void EchoString(EchoStringRequest& request, EchoStringCompleter::Sync& completer) override {
async::PostDelayedTask(
dispatcher_,
[value = request.value(), completer = completer.ToAsync()]() mutable {
completer.Reply({{.response = value}});
},
zx::duration(ZX_SEC(1)));
}
The server won't start handling any new requests until returning from the
current handler method. After returning from EchoString
, the server will
monitor the endpoint for new FIDL messages, while the reply is scheduled one
second into the future. This means if the client sent multiple EchoString
requests in quick succession, we may have just as many concurrent async delayed
tasks in flight.
Respond asynchronously in servers speaking wire domain objects
You would use the same ToAsync
operation when the server speaks
wire domain objects, but pay extra attention to object lifetimes.
In particular, the request views provided to method handlers do not own the
request message. If an asynchronous task needs to use the request parameters
after the EchoString
method returns, we need to copy relevant fields to an
owned type:
class EchoImpl : public fidl::WireServer<fuchsia_examples::Echo> {
public:
void EchoString(EchoStringRequestView request, EchoStringCompleter::Sync& completer) override {
// Copy the contents of |request->value| (a fidl::StringView) to a string.
std::string value_owned{request->value.get()};
async::PostDelayedTask(
dispatcher_,
[value = value_owned, completer = completer.ToAsync()]() mutable {
completer.Reply(fidl::StringView::FromExternal(value));
},
zx::duration(ZX_SEC(1)));
}
// ...
};
For further information on the memory ownership, refer to Memory ownership of wire domain objects.
Run the example
In order for the client and server to communicate using the Echo
protocol,
component framework must route the fuchsia.examples.Echo
capability from the
server to the client. For this tutorial, a
realm
component is
provided to declare the appropriate capabilities and routes.
Configure your build to include the provided package that includes the echo realm, server, and client:
fx set core.x64 --with //examples/fidl/cpp/server_async_completer:echo-cpp-async
Build the Fuchsia image:
fx build
Run the
echo_realm
component. This creates the client and server component instances and routes the capabilities:ffx component run /core/ffx-laboratory:echo_realm fuchsia-pkg://fuchsia.com/echo-cpp-async#meta/echo_realm.cm
Start the
echo_client
instance:ffx component start /core/ffx-laboratory:echo_realm/echo_client
The server component starts when the client attempts to connect to the Echo
protocol. You should see output similar to the following in the device logs
(ffx log
). The leftmost column is the timestamp:
[21611.962][echo_server][I] Running C++ echo server with natural types
[21611.965][echo_server][I] Incoming connection for fuchsia.examples.Echo
[21612.998][echo_client][I] (Natural types) got response: hello
[21613.999][echo_client][I] (Natural types) got response: hello
[21614.000][echo_client][I] (Natural types) got event: hello
[21615.002][echo_client][I] (Wire types) got response: hello
[21615.003][echo_client][I] (Natural types) got event: hello
[21615.003][echo_server][I] Client disconnected
Note that it takes about one second between
Incoming connection for fuchsia.examples.Echo
and
(Natural types) got response: hello
, because the server is programmed to
asynchronously delay the response by one second.
Terminate the realm component to stop execution and clean up the component instances:
ffx component destroy /core/ffx-laboratory:echo_realm