blob: bbb2c490f0f22df3d9ed9acfd8676faf95af75e9 [file]
// Copyright 2019 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 LIB_FIDL_LLCPP_ASYNC_BINDING_H_
#define LIB_FIDL_LLCPP_ASYNC_BINDING_H_
#include <lib/async/dispatcher.h>
#include <lib/async/task.h>
#include <lib/async/wait.h>
#include <lib/fidl/epitaph.h>
#include <lib/fidl/llcpp/extract_resource_on_destruction.h>
#include <lib/fidl/llcpp/internal/client_details.h>
#include <lib/fidl/llcpp/internal/thread_checker.h>
#include <lib/fidl/llcpp/message.h>
#include <lib/fidl/llcpp/result.h>
#include <lib/fidl/llcpp/server_end.h>
#include <lib/fidl/llcpp/transaction.h>
#include <lib/fidl/llcpp/wire_messaging.h>
#include <lib/fit/function.h>
#include <lib/stdcompat/variant.h>
#include <lib/sync/completion.h>
#include <lib/zx/channel.h>
#include <zircon/fidl.h>
#include <mutex>
#include <optional>
namespace fidl {
// The return value of various Dispatch, TryDispatch, or
// |IncomingMessageDispatcher::dispatch_message| functions, which call into the
// appropriate server message handlers based on the method ordinal.
enum class __attribute__((enum_extensibility(closed))) DispatchResult {
// The FIDL method ordinal was not recognized by the dispatch function.
kNotFound = false,
// The FIDL method ordinal matched one of the handlers.
// Note that this does not necessarily mean the message was handled successfully.
// For example, the message could fail to decode.
kFound = true
};
namespace internal {
// |AsyncBinding| objects implement the common logic for registering waits
// on channels, and teardown. |AsyncBinding| itself composes |async_wait_t|
// which borrows the channel to wait for messages. The actual responsibilities
// of managing channel ownership falls on the various subclasses, which must
// ensure the channel is not destroyed while there are outstanding waits.
//
// |AsyncBinding| objects are always managed by a |std::shared_ptr|. Messaging
// APIs typically promote a corresponding |std::weak_ptr| briefly when they need
// to write to the transport, and gracefully report an *unbound* error if the
// binding has been destroyed.
class AsyncBinding : private async_wait_t {
public:
~AsyncBinding() __TA_EXCLUDES(lock_) = default;
void BeginFirstWait() __TA_EXCLUDES(lock_);
// Checks for the need to teardown and registers the next wait in one critical
// section:
//
// - If we are already in |Lifecycle::MustTeardown|, early return an error.
// - Otherwise, adds the next wait to the dispatcher, recording any error in
// |lifecycle_|.
//
// When used from the message handler, the message handler should immediately
// perform teardown when this method returns an error.
zx_status_t CheckForTeardownAndBeginNextWait() __TA_EXCLUDES(lock_);
void StartTeardown(std::shared_ptr<AsyncBinding>&& calling_ref) __TA_EXCLUDES(thread_checker_)
__TA_EXCLUDES(lock_) {
StartTeardownWithInfo(std::move(calling_ref), ::fidl::UnbindInfo::Unbind());
}
zx::unowned_channel channel() const { return zx::unowned_channel(handle()); }
zx_handle_t handle() const { return async_wait_t::object; }
protected:
AsyncBinding(async_dispatcher_t* dispatcher, const zx::unowned_channel& borrowed_channel,
ThreadingPolicy threading_policy);
static void OnMessage(async_dispatcher_t* dispatcher, async_wait_t* wait, zx_status_t status,
const zx_packet_signal_t* signal) {
static_cast<AsyncBinding*>(wait)->MessageHandler(status, signal);
}
// Common message handling entrypoint shared by both client and server bindings.
void MessageHandler(zx_status_t status, const zx_packet_signal_t* signal)
__TA_EXCLUDES(thread_checker_) __TA_EXCLUDES(lock_);
// Dispatches a generic incoming message.
//
// ## Message ownership
//
// The client async binding should invoke the matching response handler or
// event handler, if one is found. |msg| is then consumed, regardless of
// decoding error.
//
// The server async binding should invoke the matching request handler if
// one is found. |msg| is then consumed, regardless of decoding error.
//
// In other cases (e.g. unknown message, epitaph), |msg| is not consumed.
//
// The caller should simply ignore the |fidl::IncomingMessage| object once
// it is passed to this function, letting RAII clean up handles as needed.
//
// ## Return value
//
// If errors occur during dispatching, the function will return an
// |UnbindInfo| describing the error. Otherwise, it will return
// |std::nullopt|.
//
// If `*binding_released` is set, the calling code no longer has ownership of
// this |AsyncBinding| object and so must not access its state.
virtual std::optional<UnbindInfo> Dispatch(fidl::IncomingMessage& msg, bool* binding_released)
__TA_REQUIRES(thread_checker_) = 0;
// |StartTeardownWithInfo| attempts to post exactly one task to drive the
// teardown process. This enum reflects the result of posting the task.
enum class TeardownTaskPostingResult {
kOk,
// The binding is already tearing down, so we should not post another.
kRacedWithInProgressTeardown,
// Failed to post the task to the dispatcher. This is usually due to
// the dispatcher already shutting down.
//
// If the user shuts down the dispatcher when the binding is already
// established and monitoring incoming messages, then whichever thread
// that was monitoring incoming messages would drive the teardown
// process.
//
// If the user calls |BindServer| on a shut-down dispatcher, there is
// no available thread to drive the teardown process and report errors.
// We consider it a programming error, and panic right away. Note that
// this is inherently racy i.e. shutting down dispatchers while trying
// to also bind new channels to the same dispatcher, so we may want to
// reevaluate whether shutting down the dispatcher is an error whenever
// there is any active binding (fxbug.dev/NNNNN).
kDispatcherError,
};
// Initiates teardown with the provided |info| as reason.
TeardownTaskPostingResult StartTeardownWithInfo(std::shared_ptr<AsyncBinding>&& calling_ref,
UnbindInfo info) __TA_EXCLUDES(thread_checker_)
__TA_EXCLUDES(lock_);
async_dispatcher_t* dispatcher_ = nullptr;
// A circular reference that represents the dispatcher ownership of the
// |AsyncBinding|. When |lifecycle_| is |Lifecycle::Bound|, all mutations of
// |keep_alive_| must happen on a dispatcher thread.
std::shared_ptr<AsyncBinding> keep_alive_ = {};
private:
// Synchronously perform teardown in the context of a dispatcher thread with
// exclusive access of the internal binding reference.
//
// If |lifecycle_| is not yet in |MustTeardown|, |info| must be present to
// specify the teardown reason.
void PerformTeardown(cpp17::optional<UnbindInfo> info) __TA_REQUIRES(thread_checker_)
__TA_EXCLUDES(lock_);
// Override |FinishTeardown| to perform cleanup work at the final stage of
// binding teardown.
//
// An important guarantee of this function is up-call exclusion: there will be
// no parallel up-calls to user objects at the point of invocation.
//
// Proof that |AsyncBinding| upholds this property:
//
// The runtime arranges |AsyncBinding::MessageHandler| to be run when an
// incoming message arrives, where it would make up-calls to handle the
// message. There will be at most one pending handler registration at any
// time. |StartTeardownWithInfo| attempts to de-register this interest for a
// new message (`async_cancel_wait`). There are two possible outcomes:
//
// - If the cancellation succeeds, it follows that there no up-calls since the
// |MessageHandler| will no longer run.
//
// - If the cancellation fails, the |MessageHandler| may already be running,
// or has entered an imminent state where it is too late to cancel. In
// either case, |MessageHandler| will detect that teardown is in order when
// it is re-registering the wait, and will run the teardown task right away.
// There is no parallel up-calls because the |MessageHandler| itself is
// synchronously preoccupied with teardown.
//
// |FinishTeardown| will be invoked on a dispatcher thread if the dispatcher is
// running, and will be invoked on the thread that is calling shutdown if the
// dispatcher is shutting down.
virtual void FinishTeardown(std::shared_ptr<AsyncBinding>&& calling_ref, UnbindInfo info)
__TA_REQUIRES(thread_checker_) = 0;
// |thread_checker_| records the thread ID of constructing thread and checks
// that required operations run on that thread when the threading policy calls
// for it.
//
// |thread_checker_| is no-op in release builds, and may be completely
// optimized out.
[[no_unique_address]] ThreadChecker thread_checker_;
// A lock protecting the binding |lifecycle|.
std::mutex lock_;
// |Lifecycle| is a state machine that captures the lifecycle of a binding.
//
// A binding transitions through the states in their listed order, and may
// be allowed to skip forward certain states as noted below.
class Lifecycle {
public:
enum LifecycleState {
// The binding is created, but message dispatch has not started.
//
// A binding always starts in this state.
kCreated = 0,
// The first |async_wait_t| has been registered with the dispatcher
// i.e. the first wait has begun.
kBound,
// A fatal error happened or the user explicitly requested teardown.
// The binding must stop message processing at its earliest convenience.
kMustTeardown,
// The last stage of the binding before its destruction. The only
// allowed operation is to call |FinishTeardown| to notify the user.
kTorndown
};
// Transitions to the |kBound| state.
//
// One may only transition from |kCreated| to this state.
void TransitionToBound();
// Indicates that waits should no longer be added to the dispatcher.
//
// |info| contains the reason for teardown.
//
// One may transition to this state from |kCreated|, |kBound|, or
// |kMustTeardown|. When transitioning from |kMustTeardown| to itself, the
// previous |info| value is preserved. In other words, the earliest error is
// propagated to the user.
void TransitionToMustTeardown(fidl::UnbindInfo info);
// Transitions to the |kTorndown| state.
//
// One may only transition to this state from |kMustTeardown|.
//
// Returns the stored reason for teardown.
fidl::UnbindInfo TransitionToTorndown();
// Returns whether the binding _ever_ entered the |kBound| state.
bool DidBecomeBound() const { return did_enter_bound_; }
// Checks if the binding is in the specified |state|.
bool Is(LifecycleState state) { return state_ == state; }
// Returns the current state as an enumeration.
LifecycleState state() const { return state_; }
private:
LifecycleState state_ = kCreated;
bool did_enter_bound_ = false;
// The reason for teardown. Only valid when |state_| is |kMustTeardown|.
fidl::UnbindInfo info_ = {};
} lifecycle_ __TA_GUARDED(lock_) = {};
};
//
// Server binding specifics
//
class IncomingMessageDispatcher;
class AsyncTransaction;
// A generic callback type handling the completion of server unbinding.
// Note that the first parameter is a pointer to |IncomingMessageDispatcher|,
// which is the common base interface implemented by all server protocol
// message handling interfaces.
//
// The bindings runtime need to convert this pointer to the specific server
// implementation type before invoking the public unbinding completion callback
// that is |fidl::OnUnboundFn<ServerImpl>|.
using AnyOnUnboundFn = fit::callback<void(IncomingMessageDispatcher*, UnbindInfo, zx::channel)>;
// Base implementation shared by various specializations of
// |AsyncServerBinding<Protocol>|.
class AnyAsyncServerBinding : public AsyncBinding {
public:
std::optional<UnbindInfo> Dispatch(fidl::IncomingMessage& msg, bool* binding_released) override;
void Close(std::shared_ptr<AsyncBinding>&& calling_ref, zx_status_t epitaph) {
StartTeardownWithInfo(std::move(calling_ref), fidl::UnbindInfo::Close(epitaph));
}
protected:
AnyAsyncServerBinding(async_dispatcher_t* dispatcher, const zx::unowned_channel& borrowed_channel,
IncomingMessageDispatcher* interface)
: AsyncBinding(dispatcher, borrowed_channel,
ThreadingPolicy::kCreateAndTeardownFromAnyThread),
interface_(interface) {}
IncomingMessageDispatcher* interface() const { return interface_; }
private:
friend fidl::internal::AsyncTransaction;
IncomingMessageDispatcher* interface_ = nullptr;
};
// The async server binding for |Protocol|.
// Contains an event sender for that protocol, which directly owns the channel.
template <typename Protocol>
class AsyncServerBinding final : public AnyAsyncServerBinding {
struct ConstructionKey {};
public:
using EventSender = typename fidl::WireEventSender<Protocol>;
static std::shared_ptr<AsyncServerBinding> Create(async_dispatcher_t* dispatcher,
fidl::ServerEnd<Protocol>&& server_end,
IncomingMessageDispatcher* interface,
AnyOnUnboundFn&& on_unbound_fn) {
auto ret = std::make_shared<AsyncServerBinding>(dispatcher, std::move(server_end), interface,
std::move(on_unbound_fn), ConstructionKey{});
// We keep the binding alive until somebody decides to close the channel.
ret->keep_alive_ = ret;
return ret;
}
virtual ~AsyncServerBinding() = default;
const EventSender& event_sender() const { return event_sender_.get(); }
zx::unowned_channel channel() const { return zx::unowned_channel(event_sender_.get().channel()); }
// Do not construct this object outside of this class. This constructor takes
// a private type following the pass-key idiom.
AsyncServerBinding(async_dispatcher_t* dispatcher, fidl::ServerEnd<Protocol>&& server_end,
IncomingMessageDispatcher* interface, AnyOnUnboundFn&& on_unbound_fn,
ConstructionKey key)
: AnyAsyncServerBinding(dispatcher, server_end.channel().borrow(), interface),
event_sender_(EventSender(std::move(server_end))),
on_unbound_fn_(std::move(on_unbound_fn)) {}
private:
// Waits for all references to the binding to be released.
// Sends epitaph and invokes |on_unbound_fn_| as required.
void FinishTeardown(std::shared_ptr<AsyncBinding>&& calling_ref, UnbindInfo info) override {
// Stash required state after deleting the binding, since the binding
// will be destroyed as part of this function.
auto* the_interface = interface();
auto on_unbound_fn = std::move(on_unbound_fn_);
// Downcast to our class.
std::shared_ptr<AsyncServerBinding> server_binding =
std::static_pointer_cast<AsyncServerBinding>(calling_ref);
calling_ref.reset();
// Delete the calling reference.
// Wait for any transient references to be released.
DestroyAndExtract(std::move(server_binding), &AsyncServerBinding::event_sender_,
[&info, the_interface, &on_unbound_fn](EventSender event_sender) {
// `this` is no longer valid.
// If required, send the epitaph.
zx::channel channel = std::move(event_sender.channel());
if (info.reason() == Reason::kClose) {
info =
UnbindInfo::Close(fidl_epitaph_write(channel.get(), info.status()));
}
// Execute the unbound hook if specified.
if (on_unbound_fn)
on_unbound_fn(the_interface, info, std::move(channel));
});
}
// The channel is owned by AsyncServerBinding.
ExtractedOnDestruction<EventSender> event_sender_;
// The user callback to invoke after teardown has completed.
AnyOnUnboundFn on_unbound_fn_ = {};
};
//
// Client binding specifics
//
class ChannelRef;
class ClientBase;
// The async client binding. The client supports both synchronous and
// asynchronous calls. Because the channel lifetime must outlast the duration
// of any synchronous calls, and that synchronous calls do not yet support
// cancellation, the client binding does not own the channel directly.
// Rather, it co-owns the channel between itself and any in-flight sync
// calls, using shared pointers.
class AsyncClientBinding final : public AsyncBinding {
public:
static std::shared_ptr<AsyncClientBinding> Create(async_dispatcher_t* dispatcher,
std::shared_ptr<ChannelRef> channel,
std::shared_ptr<ClientBase> client,
AsyncEventHandler* event_handler,
AnyTeardownObserver&& teardown_observer,
ThreadingPolicy threading_policy);
virtual ~AsyncClientBinding() = default;
std::shared_ptr<ChannelRef> GetChannel() const { return channel_; }
private:
AsyncClientBinding(async_dispatcher_t* dispatcher, std::shared_ptr<ChannelRef> channel,
std::shared_ptr<ClientBase> client, AsyncEventHandler* event_handler,
AnyTeardownObserver&& teardown_observer, ThreadingPolicy threading_policy);
std::optional<UnbindInfo> Dispatch(fidl::IncomingMessage& msg, bool* binding_released) override;
void FinishTeardown(std::shared_ptr<AsyncBinding>&& calling_ref, UnbindInfo info) override;
std::shared_ptr<ChannelRef> channel_ = nullptr; // Strong reference to the channel.
std::shared_ptr<ClientBase> client_;
AsyncEventHandler* event_handler_;
AnyTeardownObserver teardown_observer_;
};
} // namespace internal
} // namespace fidl
#endif // LIB_FIDL_LLCPP_ASYNC_BINDING_H_