blob: 497736c061df6a03252091d0871b5a37f445e45f [file] [log] [blame]
// Copyright 2022 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// channel.h is the "entrypoint header" that should be included when using the
// channel transport with LLCPP.
#ifndef LIB_FIDL_LLCPP_CHANNEL_H_
#define LIB_FIDL_LLCPP_CHANNEL_H_
#include <lib/fidl/llcpp/client.h>
#include <lib/fidl/llcpp/internal/arrow.h>
#include <lib/fidl/llcpp/internal/transport_channel.h>
#include <lib/fidl/llcpp/server.h>
#include <lib/fidl/llcpp/sync_call.h>
#include <lib/fidl/llcpp/wire_messaging.h>
#include <lib/zx/status.h>
namespace fidl {
template <typename Protocol>
struct Endpoints {
fidl::ClientEnd<Protocol> client;
fidl::ServerEnd<Protocol> server;
};
// Creates a pair of Zircon channel endpoints speaking the |Protocol| protocol.
// Whenever interacting with LLCPP, using this method should be encouraged over
// |zx::channel::create|, because this method encodes the precise protocol type
// into its results at compile time.
//
// The return value is a result type wrapping the client and server endpoints.
// Given the following:
//
// auto endpoints = fidl::CreateEndpoints<MyProtocol>();
//
// The caller should first ensure that |endpoints.is_ok() == true|, after which
// the channel endpoints may be accessed in one of two ways:
//
// - Direct:
// endpoints->client;
// endpoints->server;
//
// - Structured Binding:
// auto [client_end, server_end] = std::move(endpoints.value());
template <typename Protocol>
zx::status<Endpoints<Protocol>> CreateEndpoints() {
zx::channel local, remote;
zx_status_t status = zx::channel::create(0, &local, &remote);
if (status != ZX_OK) {
return zx::error_status(status);
}
return zx::ok(Endpoints<Protocol>{
fidl::ClientEnd<Protocol>(std::move(local)),
fidl::ServerEnd<Protocol>(std::move(remote)),
});
}
// Creates a pair of Zircon channel endpoints speaking the |Protocol| protocol.
// Whenever interacting with LLCPP, using this method should be encouraged over
// |zx::channel::create|, because this method encodes the precise protocol type
// into its results at compile time.
//
// This overload of |CreateEndpoints| may lead to more concise code when the
// caller already has the client endpoint defined as an instance variable.
// It will replace the destination of |out_client| with a newly created client
// endpoint, and return the corresponding server endpoint in a |zx::status|:
//
// // |client_end_| is an instance variable.
// auto server_end = fidl::CreateEndpoints(&client_end_);
// if (server_end.is_ok()) { ... }
template <typename Protocol>
zx::status<fidl::ServerEnd<Protocol>> CreateEndpoints(fidl::ClientEnd<Protocol>* out_client) {
auto endpoints = fidl::CreateEndpoints<Protocol>();
if (!endpoints.is_ok()) {
return endpoints.take_error();
}
*out_client = fidl::ClientEnd<Protocol>(std::move(endpoints->client));
return zx::ok(fidl::ServerEnd<Protocol>(std::move(endpoints->server)));
}
// Creates a pair of Zircon channel endpoints speaking the |Protocol| protocol.
// Whenever interacting with LLCPP, using this method should be encouraged over
// |zx::channel::create|, because this method encodes the precise protocol type
// into its results at compile time.
//
// This overload of |CreateEndpoints| may lead to more concise code when the
// caller already has the server endpoint defined as an instance variable.
// It will replace the destination of |out_server| with a newly created server
// endpoint, and return the corresponding client endpoint in a |zx::status|:
//
// // |server_end_| is an instance variable.
// auto client_end = fidl::CreateEndpoints(&server_end_);
// if (client_end.is_ok()) { ... }
template <typename Protocol>
zx::status<fidl::ClientEnd<Protocol>> CreateEndpoints(fidl::ServerEnd<Protocol>* out_server) {
auto endpoints = fidl::CreateEndpoints<Protocol>();
if (!endpoints.is_ok()) {
return endpoints.take_error();
}
*out_server = fidl::ServerEnd<Protocol>(std::move(endpoints->server));
return zx::ok(fidl::ClientEnd<Protocol>(std::move(endpoints->client)));
}
// This class manages a server connection over a Zircon channel and its binding
// to an |async_dispatcher_t*|, which may be multi-threaded. See the detailed
// documentation on the |BindServer| APIs.
template <typename Protocol>
class ServerBindingRef : public internal::ServerBindingRefBase {
public:
~ServerBindingRef() = default;
ServerBindingRef(ServerBindingRef&&) noexcept = default;
ServerBindingRef& operator=(ServerBindingRef&&) noexcept = default;
ServerBindingRef(const ServerBindingRef&) = default;
ServerBindingRef& operator=(const ServerBindingRef&) = default;
// Triggers an asynchronous unbind operation. If specified, |on_unbound| will
// be asynchronously run on a dispatcher thread, passing in the endpoint and
// the unbind reason.
//
// On return, the dispatcher will stop monitoring messages on the endpoint,
// though handling of any already in-flight transactions will continue.
// Pending completers may be discarded.
//
// This may be called from any thread.
//
// WARNING: While it is safe to invoke Unbind() from any thread, it is unsafe to wait on the
// OnUnboundFn from a dispatcher thread, as that will likely deadlock.
using ServerBindingRefBase::Unbind;
// Triggers an asynchronous unbind operation. Eventually, the epitaph will be sent over the
// channel which will be subsequently closed. If specified, |on_unbound| will be invoked giving
// the unbind reason as an argument.
//
// This may be called from any thread.
void Close(zx_status_t epitaph) {
if (auto binding = ServerBindingRefBase::binding().lock())
binding->Close(std::move(binding), epitaph);
}
private:
// This is so that only |BindServerTypeErased| will be able to construct a
// new instance of |ServerBindingRef|.
friend internal::ServerBindingRefType<Protocol> internal::BindServerTypeErased<Protocol>(
async_dispatcher_t* dispatcher, fidl::internal::ServerEndType<Protocol> server_end,
internal::IncomingMessageDispatcher* interface, internal::AnyOnUnboundFn on_unbound);
explicit ServerBindingRef(std::weak_ptr<internal::AsyncServerBinding> internal_binding)
: ServerBindingRefBase(std::move(internal_binding)) {}
};
// |BindServer| starts handling message on |server_end| using implementation
// |impl|, on a potentially multi-threaded |dispatcher|. Multiple requests may
// be concurrently in-flight, and responded to synchronously or asynchronously.
//
// |ServerImpl| should implement the abstract base class
// |fidl::WireServer<library::MyProtocol>|, typically generated by the low-level
// C++ backend, corresponding to methods in the protocol |library.MyProtocol|.
//
// This function adds an asynchronous wait to the given |dispatcher| for new
// messages to arrive on |server_end|. When each message arrives, the
// corresponding method handler in |ServerImpl| is called on one of the
// threads of the |dispatcher|.
//
// ## Starting message dispatch
//
// On success, |BindServer| associates |impl| and |server_end| with the
// |dispatcher|, and begins handling messages that arrive on |server_end|. This
// association is called a "binding". The dispatcher owns the |server_end| while
// the binding is active.
//
// The returned |ServerBindingRef| is a reference to the binding; it does not
// own the binding. In particular, the binding is kept alive by the dispatcher
// even if the returned |ServerBindingRef| is dropped. If the binding reference
// is ignored, the server operates in a "self-managed" mode, where it will
// continue listening for messages until an error occurs or if the user tears
// down the connection using a |Completer|.
//
// It is a logic error to invoke |BindServer| on a dispatcher that is
// shutting down or already shut down. Doing so will result in a panic.
//
// If any other error occurs when creating the binding, the |on_unbound| handler
// will be invoked asynchronously with the reason. See the "Unbind" section
// for details on |on_unbound|.
//
// ## Stopping message dispatch
//
// ### Unbind
//
// |ServerBindingRef::Unbind| requests to explicitly disassociate the server
// |impl| and endpoint from the dispatcher, and to retrieve the |server_end|
// endpoint. Note that this is an asynchronous procedure.
//
// |Unbind| is guaranteed to return in a short and bounded amount of time. It
// does not depend on whether there are any in-flight requests. As such, the
// user may safely take locks around an |Unbind| call.
//
// After unbinding completes:
//
// - The |server_end| is detached from the dispatcher; no dispatcher threads
// will interact with it.
// - Calls on |Completer| objects from in-flight requests will have no effect.
// Failable operations will return |ZX_ERR_CANCELED|.
// - Subsequent calls made on the |ServerBindingRef| will be ignored. Failable
// operations will return |ZX_ERR_CANCELED|.
// - If |on_unbound| is not specified, the |server_end| is closed.
// - If |on_unbound| is specified, it will be called to signal the completion.
// Ownership of the |server_end| is transferred to this hook.
//
// |on_unbound| must be a callable of the following signature:
//
// // |impl| is the pointer to the server implementation.
// // |info| contains the reason for stopping the message dispatch.
// // |server_end| is the server channel endpoint.
// // |ProtocolType| is the type of the FIDL protocol.
// void OnUnbound(ServerImpl* impl,
// fidl::UnbindInfo info,
// fidl::ServerEnd<ProtocolType> server_end);
//
// More precisely, if there is a dispatcher thread waiting for incoming messages
// on the channel, it will stop monitoring the channel and detach itself from
// the binding. If there is a thread executing the method handler, the channel
// would be pulled from underneath it, such that it may no longer make any
// replies. When no thread has any active reference to the channel, the
// |on_unbound| callback will be invoked.
//
// |on_unbound| will be executed on a |dispatcher| thread, unless the user shuts
// down the |dispatcher| while there are active bindings associated with it. In
// that case, those bindings will be synchronously unbound, and the |on_unbound|
// callback would be executed on the thread invoking shutdown. |on_unbound|
// hooks must not acquire any locks that could be held during |dispatcher|
// shutdown.
//
// ### Close
//
// |ServerBindingRef::Close| has the same effects as |Unbind| except that it
// sends an epitaph message on the |server_end|.
//
// If specified, the |on_unbound| hook will execute after the epitaph has been
// sent.
//
// ## Server implementation ownership
//
// The server instance |impl| must remain alive while it is bound to the message
// dispatcher. Take special note of |Unbind|, as it returns before the unbind
// operation has completed. It is only safe to destroy the server instance
// within or after |on_unbound|.
//
// This overload borrows the server implementation by raw pointer. There are
// additional overloads of |BindServer| that either takes ownership of an
// implementation via |std::unique_ptr|, or shares the ownership via
// |std::shared_ptr|. Using either of those smart pointer overloads would
// automatically ensure memory safety.
//
// ## Error conditions
//
// The server implementation can call |Close| on the completer to indicate an
// application error during message handling.
//
// The connection will also be automatically closed by the dispatching logic in
// certain conditions:
//
// - If the client-end of the channel is closed (ZX_ERR_PEER_CLOSED).
// - If an error occurs when waiting on, reading from, or writing to the
// channel.
// - If decoding an incoming message fails or encoding an outgoing message
// fails.
// - If the message was not defined in the FIDL protocol.
//
// These error conditions lead to the unbinding of the connection. If
// |on_unbound| was specified, it would be called on a |dispatcher| thread with
// a failure reason, allowing the user to process the error.
//
// ## Message ordering
//
// By default, the message dispatch function for a binding will only ever be
// invoked by a single |dispatcher| thread at a time, even if the dispatcher has
// multiple threads. Messages will be dispatched in the order that they are
// received on the channel.
//
// A method handler may call |EnableNextDispatch| on their completer to allow
// another thread to begin dispatching the next message before the current
// method handler returns. Of course, this is only meaningful if the
// |dispatcher| has multiple threads.
//
// If a particular user does not care about ordering, they may invoke
// |EnableNextDispatch| immediately in the message handler. If you have such a
// use case, please file a bug, where we may consider instead providing a mode
// to automatically parallelize.
//
// ## Template Ergonomics
//
// This function is able to infer the type of |ServerImpl| and |OnUnbound| in
// most cases. The following code would compile without explicitly specializing
// |BindServer|:
//
// // Suppose |this| is a server implementation class |Foo|, that
// // implements the |Bar| FIDL protocol.
// fidl:ServerEnd<Bar> server_end = ...;
// fidl::BindServer(dispatcher, std::move(server_end), this,
// [](Foo*, fidl::UnbindInfo, fidl::ServerEnd<Bar>) { ... });
//
// TODO(fxbug.dev/66343): Consider using a "DidUnbind" virtual function
// in the server interface to replace the |on_unbound| handler lambda.
template <typename ServerImpl, typename OnUnbound = std::nullptr_t>
ServerBindingRef<typename ServerImpl::_EnclosingProtocol> BindServer(
async_dispatcher_t* dispatcher,
fidl::internal::ServerEndType<typename ServerImpl::_EnclosingProtocol> server_end,
ServerImpl* impl, OnUnbound&& on_unbound = nullptr) {
static_assert(std::is_same_v<typename ServerImpl::_EnclosingProtocol::Transport,
fidl::internal::ChannelTransport>);
return internal::BindServerImpl<ServerImpl>(
dispatcher, std::move(server_end), impl,
internal::UnboundThunk(std::move(impl), std::forward<OnUnbound>(on_unbound)));
}
// Overload of |BindServer| that takes ownership of the server as a |unique_ptr|.
// The pointer is destroyed on the same thread as the one calling |on_unbound|,
// and happens right after |on_unbound|. See |BindServer| for details.
template <typename ServerImpl, typename OnUnbound = std::nullptr_t>
ServerBindingRef<typename ServerImpl::_EnclosingProtocol> BindServer(
async_dispatcher_t* dispatcher,
fidl::internal::ServerEndType<typename ServerImpl::_EnclosingProtocol> server_end,
std::unique_ptr<ServerImpl>&& impl, OnUnbound&& on_unbound = nullptr) {
static_assert(std::is_same_v<typename ServerImpl::_EnclosingProtocol::Transport,
fidl::internal::ChannelTransport>);
ServerImpl* impl_raw = impl.get();
return internal::BindServerImpl<ServerImpl>(
dispatcher, std::move(server_end), impl_raw,
internal::UnboundThunk(std::move(impl), std::forward<OnUnbound>(on_unbound)));
}
// Overload of |BindServer| that shares ownership of the server via a |shared_ptr|.
// The pointer is destroyed on the same thread as the one calling |on_unbound|,
// and happens right after |on_unbound|. See |BindServer| for details.
template <typename ServerImpl, typename OnUnbound = std::nullptr_t>
ServerBindingRef<typename ServerImpl::_EnclosingProtocol> BindServer(
async_dispatcher_t* dispatcher,
fidl::internal::ServerEndType<typename ServerImpl::_EnclosingProtocol> server_end,
std::shared_ptr<ServerImpl> impl, OnUnbound&& on_unbound = nullptr) {
static_assert(std::is_same_v<typename ServerImpl::_EnclosingProtocol::Transport,
fidl::internal::ChannelTransport>);
ServerImpl* impl_raw = impl.get();
return internal::BindServerImpl<ServerImpl>(
dispatcher, std::move(server_end), impl_raw,
internal::UnboundThunk(std::move(impl), std::forward<OnUnbound>(on_unbound)));
}
// |fidl::WireSyncClient| owns a client endpoint and exposes synchronous FIDL
// calls. Prefer using this owning class over |fidl::WireCall| unless one has to
// interface with very low-level functionality (such as making a call over a raw
// zx_handle_t).
//
// Generated FIDL APIs are accessed by 'dereferencing' the client value:
//
// // Creates a sync client that speaks over |client_end|.
// fidl::WireSyncClient client(std::move(client_end));
//
// // Call the |Foo| method synchronously, obtaining the results from the
// // return value.
// fidl::WireResult result = client->Foo(args);
//
// |fidl::WireSyncClient| is suitable for code without access to an async
// dispatcher.
//
// ## Thread safety
//
// |WireSyncClient| is generally thread-safe with a few caveats:
//
// - Client objects can be safely sent between threads.
// - One may invoke many FIDL methods in parallel on the same client. However,
// FIDL method calls must be synchronized with operations that consume or
// mutate the client object itself:
//
// - Calling `Bind` or `TakeClientEnd`.
// - Assigning a new value to the |WireSyncClient| variable.
// - Moving the |WireSyncClient| to a different location.
// - Destroying the |WireSyncClient|.
//
// - There can be at most one `HandleOneEvent` call going on at the same time.
template <typename FidlProtocol>
class WireSyncClient {
public:
// Creates an uninitialized client that is not bound to a client endpoint.
//
// Prefer using the constructor overload that initializes the client
// atomically during construction. Use this default constructor only when the
// client must be constructed first before an endpoint could be obtained (for
// example, if the client is an instance variable).
//
// The client may be initialized later via |Bind|.
WireSyncClient() = default;
// Creates an initialized client. FIDL calls will be made on |client_end|.
//
// Similar to |fidl::WireClient|, the client endpoint must be valid.
//
// To just make a FIDL call uniformly on a client endpoint that may or may not
// be valid, use the |fidl::WireCall(client_end)| helper. We may extend
// |fidl::WireSyncClient<P>| with richer features hinging on having a valid
// endpoint in the future.
explicit WireSyncClient(::fidl::ClientEnd<FidlProtocol> client_end)
: client_end_(std::move(client_end)) {
ZX_ASSERT(is_valid());
}
~WireSyncClient() = default;
WireSyncClient(WireSyncClient&&) noexcept = default;
WireSyncClient& operator=(WireSyncClient&&) noexcept = default;
// Whether the client is initialized.
bool is_valid() const { return client_end_.is_valid(); }
explicit operator bool() const { return is_valid(); }
// Borrows the underlying client endpoint. The client must have been
// initialized.
const ::fidl::ClientEnd<FidlProtocol>& client_end() const {
ZX_ASSERT(is_valid());
return client_end_;
}
// Initializes the client with a |client_end|. FIDL calls will be made on this
// endpoint.
//
// It is not allowed to call |Bind| on an initialized client. To rebind a
// |WireSyncClient| to a different endpoint, simply replace the
// |WireSyncClient| variable with a new instance.
void Bind(::fidl::ClientEnd<FidlProtocol> client_end) {
ZX_ASSERT(!is_valid());
client_end_ = std::move(client_end);
ZX_ASSERT(is_valid());
}
// Extracts the underlying endpoint from the client. After this operation, the
// client goes back to an uninitialized state.
//
// It is not safe to invoke this method while there are ongoing FIDL calls.
::fidl::ClientEnd<FidlProtocol> TakeClientEnd() {
ZX_ASSERT(is_valid());
return std::move(client_end_);
}
// Returns a veneer object for making FIDL calls with managed memory.
internal::SyncEndpointManagedVeneer<internal::WireSyncClientImpl<FidlProtocol>> operator->()
const {
ZX_ASSERT(is_valid());
return internal::SyncEndpointManagedVeneer<internal::WireSyncClientImpl<FidlProtocol>>(
fidl::internal::MakeAnyUnownedTransport(client_end_.handle()));
}
// Returns a veneer object which exposes the caller-allocating API, using
// the provided |resource| to allocate buffers necessary for each call.
// See documentation on |SyncEndpointVeneer::buffer| for detailed behavior.
template <typename MemoryResource>
auto buffer(MemoryResource&& resource) const {
ZX_ASSERT(is_valid());
return internal::SyncEndpointBufferVeneer<internal::WireSyncBufferClientImpl<FidlProtocol>>{
internal::MakeAnyUnownedTransport(client_end_.handle()),
internal::MakeAnyBufferAllocator(std::forward<MemoryResource>(resource))};
}
// Handle all possible events defined in this protocol.
//
// Blocks to consume exactly one message from the channel, then call the corresponding virtual
// method defined in |event_handler|. If the message was unknown or malformed, returns an
// error without calling any virtual method.
::fidl::Status HandleOneEvent(fidl::WireSyncEventHandler<FidlProtocol>& event_handler) const {
return event_handler.HandleOneEvent(client_end());
}
private:
::fidl::ClientEnd<FidlProtocol> client_end_;
};
template <typename FidlProtocol>
WireSyncClient(fidl::ClientEnd<FidlProtocol>) -> WireSyncClient<FidlProtocol>;
// |WireCall| is used to make method calls directly on a |fidl::ClientEnd|
// without having to set up a client. Call it like:
//
// fidl::WireCall(client_end)->Method(args...);
//
template <typename FidlProtocol>
internal::SyncEndpointVeneer<internal::WireSyncClientImpl, FidlProtocol> WireCall(
const fidl::ClientEnd<FidlProtocol>& client_end) {
return internal::SyncEndpointVeneer<internal::WireSyncClientImpl, FidlProtocol>(
fidl::internal::MakeAnyUnownedTransport(client_end.borrow().handle()));
}
// |WireCall| is used to make method calls directly on a |fidl::ClientEnd|
// without having to set up a client. Call it like:
//
// fidl::WireCall(client_end)->Method(args...);
//
template <typename FidlProtocol>
internal::SyncEndpointVeneer<internal::WireSyncClientImpl, FidlProtocol> WireCall(
const fidl::UnownedClientEnd<FidlProtocol>& client_end) {
return internal::SyncEndpointVeneer<internal::WireSyncClientImpl, FidlProtocol>(
fidl::internal::MakeAnyUnownedTransport(client_end.handle()));
}
// Return an interface for sending FIDL events containing wire domain objects
// over the endpoint managed by |binding_ref|. Call it like:
//
// fidl::WireSendEvent(server_binding_ref)->FooEvent(args...);
//
template <typename FidlProtocol>
internal::WeakEventSenderVeneer<internal::WireWeakEventSender, FidlProtocol> WireSendEvent(
const ServerBindingRef<FidlProtocol>& binding_ref) {
return internal::WeakEventSenderVeneer<internal::WireWeakEventSender, FidlProtocol>(
internal::BorrowBinding(
static_cast<const fidl::internal::ServerBindingRefBase&>(binding_ref)));
}
// Return an interface for sending FIDL events containing wire domain objects
// over |server_end|. Call it like:
//
// fidl::WireSendEvent(server_end)->FooEvent(args...);
//
template <typename FidlProtocol>
internal::SyncEndpointVeneer<internal::WireEventSender, FidlProtocol> WireSendEvent(
const ServerEnd<FidlProtocol>& server_end) {
return internal::SyncEndpointVeneer<internal::WireEventSender, FidlProtocol>(
fidl::internal::MakeAnyUnownedTransport(server_end.channel()));
}
} // namespace fidl
#endif // LIB_FIDL_LLCPP_CHANNEL_H_