blob: 5b196ea6f53bbadd6ea95410b017a606957341f4 [file] [log] [blame]
// Copyright 2021 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.
#ifndef SRC_LIB_FIDL_CPP_INCLUDE_LIB_FIDL_CPP_CLIENT_H_
#define SRC_LIB_FIDL_CPP_INCLUDE_LIB_FIDL_CPP_CLIENT_H_
#include <lib/fidl/cpp/internal/client_details.h>
#include <lib/fidl/cpp/internal/make_response_context.h>
#include <lib/fidl/cpp/unified_messaging.h>
#include <lib/fidl/llcpp/channel.h>
#include <lib/fidl/llcpp/client.h>
#include <lib/fidl/llcpp/internal/arrow.h>
namespace fidl {
//
// Note: when updating the documentation below, please make similar updates to
// the one in //zircon/system/ulib/fidl/include/lib/fidl/llcpp/client.h and
// //sdk/lib/fidl_driver/include/lib/fidl_driver/natural_client.h.
//
// The interface documentation on |fidl::Client| is largely identical to those
// on |fidl::WireClient|, after removing the "wire" portion from comments.
//
// The interface documentation on |fidl::SharedClient| is largely identical to
// those on |fidl::WireSharedClient|, after removing the "wire" portion from
// comments.
//
// |Client| is a client for sending and receiving FIDL wire and natural
// messages, that is bound to a single fixed thread. See |SharedClient| for a
// client that may be moved or cloned to a different thread.
//
// Generated FIDL APIs are accessed by 'dereferencing' the client value:
//
// // Creates a client that speaks over |client_end|, on the |my_dispatcher| dispatcher.
// fidl::Client client(std::move(client_end), my_dispatcher);
//
// // Call the |Foo| method asynchronously, passing in a callback that will be
// // invoked on a dispatcher thread when the server response arrives.
// auto status = client->Foo(args, [] (Result result) {});
//
// ## Lifecycle
//
// A client must be **bound** to an endpoint before it could be used. This
// association between the endpoint and the client is called a "binding".
// Binding a client to an endpoint starts the monitoring of incoming messages.
// Those messages are appropriately dispatched: to response callbacks, to event
// handlers, etc. FIDL methods (asynchronous or synchronous) may only be invoked
// on a bound client.
//
// Internally, a client is a lightweight reference to the binding, performing
// its duties indirectly through that object, as illustrated by the simplified
// diagram below:
//
// references makes
// client -------------> binding --------> FIDL call
//
// This means that the client _object_ and the binding have overlapping but
// slightly different lifetimes. For example, the binding may terminate in
// response to fatal communication errors, leaving the client object alive but
// unable to make any calls.
//
// To stop the monitoring of incoming messages, one may **teardown** the
// binding. When teardown is initiated, the client will not monitor new messages
// on the endpoint. Ongoing callbacks will be allowed to run to completion. When
// teardown is complete, further calls on the same client will fail. Unfulfilled
// response callbacks will be dropped.
//
// Destruction of a client object will initiate teardown.
//
// Teardown will also be initiated when the binding encounters a terminal error:
//
// - The server-end of the channel was closed.
// - An epitaph was received.
// - Decoding or encoding failed.
// - An invalid or unknown message was encountered.
// - Error waiting on, reading from, or writing to the channel.
//
// In this case, the user will be notified of the detailed error via the
// |on_fidl_error| method on the event handler.
//
// ## Thread safety
//
// |Client| provides an easier to use API in exchange of a more restrictive
// threading model:
//
// - There must only ever be one thread executing asynchronous operations for
// the provided |async_dispatcher_t|, termed "the dispatcher thread".
// - The client must be bound on the dispatcher thread.
// - The client must be destroyed on the dispatcher thread.
// - FIDL method calls may be made on other threads, but the response is always
// delivered on the dispatcher thread, as are event callbacks.
//
// The above rules are checked in debug builds at run-time. In short, the client
// is local to a thread.
//
// Note that FIDL method calls must be synchronized with operations that consume
// or mutate the |Client| itself:
//
// - Assigning a new value to the |Client| variable.
// - Moving the |Client| to a different location.
// - Destroying the |Client|.
//
// |Client| is suitable for systems with stronger sequential threading
// guarantees. It is intended to be used as a local variable with fixed
// lifetime, or as a member of a larger class where it is uniquely owned by
// instances of that class. Destroying the |Client| is guaranteed to stop
// message dispatch: since the client is destroyed on the dispatcher thread,
// there is no opportunity of parallel callbacks to user code, and
// use-after-free of user objects is naturally avoided during teardown.
//
// See |SharedClient| for a client that supports binding and destroying on
// arbitrary threads, at the expense of requiring two-phase shutdown.
template <typename Protocol>
class Client {
private:
using NaturalClientImpl = fidl::internal::NaturalClientImpl<Protocol>;
public:
// Create an initialized client which manages the binding of the client end of
// a channel to a dispatcher, as if that client had been default-constructed
// then later bound to that endpoint via |Bind|.
//
// It is a logic error to use a dispatcher that is shutting down or already
// shut down. Doing so will result in a panic.
//
// If any other error occurs during initialization, the
// |event_handler->on_fidl_error| handler will be invoked asynchronously with
// the reason, if specified.
template <typename AsyncEventHandler = fidl::AsyncEventHandler<Protocol>>
Client(fidl::ClientEnd<Protocol> client_end, async_dispatcher_t* dispatcher,
AsyncEventHandler* event_handler = nullptr) {
Bind(std::move(client_end), dispatcher, event_handler);
}
// Create an uninitialized client. The client may then be bound to an endpoint
// later via |Bind|.
//
// Prefer using the constructor overload that binds the client to a channel
// atomically during construction. Use this default constructor only when the
// client must be constructed first before a channel could be obtained (for
// example, if the client is an instance variable).
Client() = default;
// Returns if the |Client| is initialized.
bool is_valid() const { return controller_.is_valid(); }
explicit operator bool() const { return is_valid(); }
// The destructor of |Client| will initiate binding teardown.
//
// When the client destructs:
// - The channel will be closed.
// - Pointers obtained via |get| will be invalidated.
// - Binding teardown will happen, implying:
// * In-progress calls will be forgotten. Async callbacks will be dropped.
~Client() = default;
// |Client|s can be safely moved without affecting any in-flight FIDL
// method calls. Note that calling methods on a client should be serialized
// with respect to operations that consume the client, such as moving it or
// destroying it.
Client(Client&& other) noexcept { MoveImpl(std::move(other)); }
Client& operator=(Client&& other) noexcept {
if (this != &other) {
MoveImpl(std::move(other));
}
return *this;
}
// Initializes the client by binding the |client_end| endpoint to the
// dispatcher.
//
// It is a logic error to invoke |Bind| on a dispatcher that is shutting down
// or already shut down. Doing so will result in a panic.
//
// When other errors occur during binding, the |event_handler->on_fidl_error|
// handler will be asynchronously invoked with the reason, if specified.
//
// It is not allowed to call |Bind| on an initialized client. To rebind a
// |Client| to a different endpoint, simply replace the |Client|
// variable with a new instance.
void Bind(fidl::ClientEnd<Protocol> client_end, async_dispatcher_t* dispatcher,
fidl::AsyncEventHandler<Protocol>* event_handler = nullptr) {
controller_.Bind(fidl::internal::MakeAnyTransport(client_end.TakeChannel()), dispatcher,
internal::MakeAnyEventDispatcher(event_handler),
fidl::AnyTeardownObserver::Noop(),
fidl::internal::ThreadingPolicy::kCreateAndTeardownFromDispatcherThread);
natural_client_impl_.emplace(&controller_.get());
}
// Returns the interface for making outgoing FIDL calls using natural objects.
// The client must be initialized first.
//
// If the binding has been torn down, calls on the interface return error with
// status |ZX_ERR_CANCELED| and reason |fidl::Reason::kUnbind|.
//
// Persisting this pointer to a local variable is discouraged, since that
// results in unsafe borrows. Always prefer making calls directly via the
// |Client| reference-counting type.
const NaturalClientImpl* operator->() const { return &get(); }
const NaturalClientImpl& operator*() const { return get(); }
// Returns the interface for making outgoing FIDL calls using wire objects.
// The client must be initialized first.
//
// If the binding has been torn down, calls on the interface return error with
// status |ZX_ERR_CANCELED| and reason |fidl::Reason::kUnbind|.
//
// Persisting this pointer to a local variable is discouraged, since that
// results in unsafe borrows. Always prefer making calls directly via the
// |Client| reference-counting type.
auto wire() const {
return internal::Arrow<internal::WireWeakAsyncClientImpl<Protocol>>{&controller_.get()};
}
private:
const NaturalClientImpl& get() const { return natural_client_impl_.value(); }
Client(const Client& other) noexcept = delete;
Client& operator=(const Client& other) noexcept = delete;
void MoveImpl(Client&& other) noexcept {
controller_ = std::move(other.controller_);
// The move operation of |std::optional| does not clear the source optional.
// As such, we need to manually reset it to prevent two clients referencing
// the same |ClientBase|.
natural_client_impl_ = std::move(other.natural_client_impl_);
other.natural_client_impl_.reset();
}
internal::ClientController controller_;
cpp17::optional<NaturalClientImpl> natural_client_impl_;
};
template <typename Protocol, typename AsyncEventHandlerReference>
Client(fidl::ClientEnd<Protocol>, async_dispatcher_t*, AsyncEventHandlerReference&&)
-> Client<Protocol>;
template <typename Protocol>
Client(fidl::ClientEnd<Protocol>, async_dispatcher_t*) -> Client<Protocol>;
// |SharedClient| is a client for sending and receiving wire and natural
// messages. It is suitable for systems with less defined threading guarantees,
// by providing the building blocks to implement a two-phase asynchronous
// shutdown pattern.
//
// During teardown, |SharedClient| exposes a synchronization point beyond
// which it will not make any more upcalls to user code. The user may then
// arrange any objects that are the recipient of client callbacks to be
// destroyed after the synchronization point. As a result, when destroying an
// entire subsystem, the teardown of the client may be requested from an
// arbitrary thread, in parallel with any callbacks to user code, while
// avoiding use-after-free of user objects.
//
// In addition, |SharedClient| supports cloning multiple instances sharing
// the same underlying endpoint.
//
// ## Lifecycle
//
// See lifecycle notes on |Client| for general lifecycle information. Here
// we note the additional subtleties and two-phase shutdown features exclusive
// to |SharedClient|.
//
// Teardown of the binding is an asynchronous process, to account for the
// possibility of in-progress calls to user code. For example, the bindings
// runtime could be invoking a response callback from a dispatcher thread, while
// the user initiates teardown from an unrelated thread.
//
// There are a number of ways to monitor the completion of teardown:
//
// - Owned event handler: transfer the ownership of an event handler to the
// bindings as a |std::unique_ptr| when binding the client. After teardown is
// complete, the event handler will be destroyed. It is safe to destroy the
// user objects referenced by any client callbacks from within the event
// handler destructor.
//
// - Teardown observer: provide an instance of |fidl::AnyTeardownObserver| to
// the bindings. The observer will be notified when teardown is complete.
//
// See
// https://fuchsia.dev/fuchsia-src/development/languages/fidl/guides/llcpp-threading
// for detailed examples.
//
// A |SharedClient| may be |Clone|d, with the clone referencing the same
// endpoint. Automatic teardown occurs when the last clone bound to the
// endpoint is destructed.
//
// |AsyncTeardown| may be called on a |SharedClient| to explicitly initiate
// teardown.
//
// ## Thread safety
//
// FIDL method calls on this class are thread-safe. |AsyncTeardown| and |Clone|
// are also thread-safe, and may be invoked in parallel with FIDL method calls.
// However, those operations must be synchronized with operations that consume
// or mutate the |SharedClient| itself:
//
// - Assigning a new value to the |SharedClient| variable.
// - Moving the |SharedClient| to a different location.
// - Destroying the |SharedClient| variable.
//
// When teardown completes, the binding will notify the user from a |dispatcher|
// thread, unless the user shuts down the |dispatcher| while there are active
// clients associated with it. In that case, those clients will be synchronously
// torn down, and the notification (e.g. destroying the event handler) will
// happen on the thread invoking dispatcher shutdown.
template <typename Protocol>
class SharedClient final {
private:
using NaturalClientImpl = fidl::internal::NaturalClientImpl<Protocol>;
public:
// Creates an initialized |SharedClient| which manages the binding of the
// client end of a channel to a dispatcher.
//
// It is a logic error to use a dispatcher that is shutting down or already
// shut down. Doing so will result in a panic.
//
// If any other error occurs during initialization, the
// |event_handler->on_fidl_error| handler will be invoked asynchronously with
// the reason, if specified.
//
// |event_handler| will be destroyed when teardown completes.
template <typename AsyncEventHandler = fidl::AsyncEventHandler<Protocol>>
SharedClient(fidl::ClientEnd<Protocol> client_end, async_dispatcher_t* dispatcher,
std::unique_ptr<AsyncEventHandler> event_handler) {
Bind(std::move(client_end), dispatcher, std::move(event_handler));
}
// Creates a |SharedClient| that supports custom behavior on teardown
// completion via |teardown_observer|. Through helpers that return an
// |AnyTeardownObserver|, users may link the completion of teardown to the
// invocation of a callback or the lifecycle of related business objects. See
// for example |fidl::ObserveTeardown| and |fidl::ShareUntilTeardown|.
//
// This overload does not demand taking ownership of |event_handler| by
// |std::unique_ptr|, hence is suitable when the |event_handler| needs to be
// managed independently of the client lifetime.
//
// See |SharedClient| above for other behavior aspects of the constructor.
template <typename AsyncEventHandler = fidl::AsyncEventHandler<Protocol>>
SharedClient(fidl::ClientEnd<Protocol> client_end, async_dispatcher_t* dispatcher,
AsyncEventHandler* event_handler,
fidl::AnyTeardownObserver teardown_observer = fidl::AnyTeardownObserver::Noop()) {
Bind(std::move(client_end), dispatcher, event_handler, std::move(teardown_observer));
}
// Overload of |SharedClient| that omits the |event_handler|, to
// workaround C++ limitations on default arguments.
//
// See |SharedClient| above for other behavior aspects of the constructor.
template <typename AsyncEventHandler = fidl::AsyncEventHandler<Protocol>>
SharedClient(fidl::ClientEnd<Protocol> client_end, async_dispatcher_t* dispatcher,
fidl::AnyTeardownObserver teardown_observer = fidl::AnyTeardownObserver::Noop()) {
Bind(std::move(client_end), dispatcher, nullptr, std::move(teardown_observer));
}
// Creates an uninitialized |SharedClient|.
//
// Prefer using the constructor overload that binds the client to a channel
// atomically during construction. Use this default constructor only when the
// client must be constructed first before a channel could be obtained (for
// example, if the client is an instance variable).
SharedClient() = default;
// Returns if the |SharedClient| is initialized.
bool is_valid() const { return controller_.is_valid(); }
explicit operator bool() const { return is_valid(); }
// If the current |SharedClient| is the last instance controlling the
// current connection, the destructor of this |SharedClient| will trigger
// teardown.
//
// When the last |SharedClient| destructs:
// - The channel will be closed.
// - Pointers obtained via |get| will be invalidated.
// - Teardown will be initiated. See the **Lifecycle** section from the
// class documentation of |Client|.
//
// See also: |AsyncTeardown|.
~SharedClient() = default;
// |fidl::SharedClient|s can be safely moved without affecting any in-progress
// operations. Note that calling methods on a client should be serialized with
// respect to operations that consume the client, such as moving it or
// destroying it.
SharedClient(SharedClient&& other) noexcept = default;
SharedClient& operator=(SharedClient&& other) noexcept = default;
// Initializes the client by binding the |client_end| endpoint to the dispatcher.
//
// It is a logic error to invoke |Bind| on a dispatcher that is shutting down
// or already shut down. Doing so will result in a panic.
//
// It is not allowed to call |Bind| on an initialized client. To rebind a
// |SharedClient| to a different endpoint, simply replace the
// |SharedClient| variable with a new instance.
//
// When other error occurs during binding, the |event_handler->on_fidl_error|
// handler will be asynchronously invoked with the reason, if specified.
//
// |event_handler| will be destroyed when teardown completes.
void Bind(fidl::ClientEnd<Protocol> client_end, async_dispatcher_t* dispatcher,
std::unique_ptr<fidl::AsyncEventHandler<Protocol>> event_handler) {
auto event_handler_raw = event_handler.get();
Bind(std::move(client_end), dispatcher, event_handler_raw,
fidl::AnyTeardownObserver::ByOwning(std::move(event_handler)));
}
// Overload of |Bind| that supports custom behavior on teardown completion via
// |teardown_observer|. Through helpers that return an |AnyTeardownObserver|,
// users may link the completion of teardown to the invocation of a callback
// or the lifecycle of related business objects. See for example
// |fidl::ObserveTeardown| and |fidl::ShareUntilTeardown|.
//
// This overload does not demand taking ownership of |event_handler| by
// |std::unique_ptr|, hence is suitable when the |event_handler| needs to be
// managed independently of the client lifetime.
//
// See |Bind| above for other behavior aspects of the function.
void Bind(fidl::ClientEnd<Protocol> client_end, async_dispatcher_t* dispatcher,
fidl::AsyncEventHandler<Protocol>* event_handler,
fidl::AnyTeardownObserver teardown_observer = fidl::AnyTeardownObserver::Noop()) {
controller_.Bind(fidl::internal::MakeAnyTransport(client_end.TakeChannel()), dispatcher,
internal::MakeAnyEventDispatcher(event_handler), std::move(teardown_observer),
fidl::internal::ThreadingPolicy::kCreateAndTeardownFromAnyThread);
natural_client_impl_ = std::make_shared<NaturalClientImpl>(&controller_.get());
}
// Overload of |Bind| that omits the |event_handler|, to
// workaround C++ limitations on default arguments.
//
// See |Bind| above for other behavior aspects of the constructor.
void Bind(fidl::ClientEnd<Protocol> client_end, async_dispatcher_t* dispatcher,
fidl::AnyTeardownObserver teardown_observer = fidl::AnyTeardownObserver::Noop()) {
Bind(std::move(client_end), dispatcher, nullptr, std::move(teardown_observer));
}
// Initiates asynchronous teardown of the bindings. See the **Lifecycle**
// section from the class documentation.
//
// |Bind| must have been called before this.
//
// While it is safe to invoke |AsyncTeardown| from any thread, it is unsafe to
// wait for teardown to complete from a dispatcher thread, as that will likely
// deadlock.
void AsyncTeardown() { controller_.Unbind(); }
// Returns another |SharedClient| instance sharing the same channel.
//
// Prefer to |Clone| only when necessary e.g. extending the lifetime of a
// |SharedClient| to a different scope. Any clone will prevent the cleanup
// of the channel while the binding is alive.
SharedClient Clone() { return SharedClient(*this); }
// Returns the interface for making outgoing FIDL calls using natural objects.
// The client must be initialized first.
//
// If the binding has been torn down, calls on the interface return error with
// status |ZX_ERR_CANCELED| and reason |fidl::Reason::kUnbind|.
//
// Persisting this pointer to a local variable is discouraged, since that
// results in unsafe borrows. Always prefer making calls directly via the
// |Client| reference-counting type.
const NaturalClientImpl* operator->() const { return &get(); }
const NaturalClientImpl& operator*() const { return get(); }
// Returns the interface for making outgoing FIDL calls using wire objects.
// The client must be initialized first.
//
// If the binding has been torn down, calls on the interface return error with
// status |ZX_ERR_CANCELED| and reason |fidl::Reason::kUnbind|.
//
// Persisting this pointer to a local variable is discouraged, since that
// results in unsafe borrows. Always prefer making calls directly via the
// |Client| reference-counting type.
auto wire() const {
return internal::Arrow<internal::WireWeakAsyncClientImpl<Protocol>>{&controller_.get()};
}
private:
const NaturalClientImpl& get() const {
auto* impl = natural_client_impl_.get();
ZX_ASSERT(impl != nullptr);
return *impl;
}
SharedClient(const SharedClient& other) noexcept = default;
SharedClient& operator=(const SharedClient& other) noexcept = default;
internal::ClientController controller_;
std::shared_ptr<NaturalClientImpl> natural_client_impl_;
};
template <typename Protocol, typename AsyncEventHandlerReference>
SharedClient(fidl::ClientEnd<Protocol>, async_dispatcher_t*, AsyncEventHandlerReference&&,
fidl::AnyTeardownObserver) -> SharedClient<Protocol>;
template <typename Protocol, typename AsyncEventHandlerReference>
SharedClient(fidl::ClientEnd<Protocol>, async_dispatcher_t*, AsyncEventHandlerReference&&)
-> SharedClient<Protocol>;
template <typename Protocol>
SharedClient(fidl::ClientEnd<Protocol>, async_dispatcher_t*) -> SharedClient<Protocol>;
} // namespace fidl
#endif // SRC_LIB_FIDL_CPP_INCLUDE_LIB_FIDL_CPP_CLIENT_H_