blob: 73691954bbb9dea6aca3eca4aa6440154953aae2 [file] [log] [blame]
// 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_TRANSACTION_H_
#define LIB_FIDL_LLCPP_TRANSACTION_H_
#include <lib/fidl/cpp/message.h>
#include <lib/fidl/llcpp/decoded_message.h>
#include <lib/fidl/llcpp/encoded_message.h>
#include <lib/fidl/llcpp/response_storage.h>
#include <lib/fidl/llcpp/traits.h>
#include <lib/fidl/transformer.h>
#include <lib/fidl/txn_header.h>
#include <zircon/assert.h>
#include <zircon/fidl.h>
#include <cstdlib>
#include <memory>
#include <type_traits>
#include <utility>
namespace fidl {
// An abstract transaction, encapsulating the logic of sending reply messages.
// The transaction type is polymorphic, to cater to a variety of transports and usage patterns.
// A basic transaction implementation using channels is defined in fidl-async, so most clients
// do not have to worry about implementing |fidl::Transaction|, and the higher-level APIs from
// fidl-async would suffice for a lot of use cases.
//
// Should greater flexibility over managing in-flight transactions and server lifecycle be desired,
// clients usually manually dispatch the FIDL messages. This is accomplished in the C bindings by
// creating a transaction type that is memory layout-compatible with |fidl_txn_t|, and passing that
// when calling the |foo_bar_try_dispatch| functions generated by the C bindings. Similarly here,
// clients can manually invoke the [Try]Dispatch functions generated by the low-level C++ bindings,
// passing in their own transaction type by inheriting from this class and implementing the
// pure virtual interfaces, e.g.
//
// // Imaginary use-case: suppose drivers send messages in their own ways
// class DriverRpcTransaction : public Transaction {
// public:
// // Implementation left-out
// DriverRpcTransaction(/* ...some args... */);
// ~DriverRpcTransaction();
// DriverRpcTransaction(DriverRpcTransaction&& other) noexcept
// : Transaction(std::move(other));
// DriverRpcTransaction& operator=(DriverRpcTransaction&& other) noexcept;
//
// protected:
// void Reply(fidl::Message msg) final { /* Send to another driver etc. */ }
// void Close(zx_status_t epitaph) final { /* Send epitaph and close down transport. */ }
//
// std::unique_ptr<Transaction> TakeOwnership() final {
// return std::make_unique<DriverRpcTransaction>(std::move(*this));
// }
//
// private:
// // Driver-specific state...
// };
//
// and then dispatch to the server e.g.
//
// fuchsia::device::driver::Interface* server_impl = /* construct/obtain server */;
// fidl_msg_t msg = /* read FIDL message */;
// DriverRpcTransaction txn(/* ...some args... */);
// fuchsia::device::driver::Dispatch(server_impl, &msg, &txn);
// /* Inspect |txn| */
//
// The LLCPP runtime provides strong invariants around each method in the interface. It is possible
// to implement a variety of dispatching behaviors by relying on them.
//
class Transaction {
public:
Transaction() = default;
// Move the contents of this transaction to heap, for an asynchronous reply.
// Called exactly once when a sync completer is converted to an async completer.
// Implementation may pause message dispatching here, in which case there will be
// at most one in-flight transaction.
// |Complete| and |Close| are never called on a transaction that has lost its ownership.
virtual std::unique_ptr<Transaction> TakeOwnership() = 0;
// Called at most once for a two-way FIDL method, to reply to a two-way call.
// Never called in case of a one-way call.
// Implementation must fill in the correct transaction ID.
virtual void Reply(fidl::Message message) = 0;
// Should send an epitaph and then close the underlying transport e.g. channel.
virtual void Close(zx_status_t epitaph) = 0;
// Resumes the asynchronous wait on the underlying channel. This allows at least one more
// dispatcher thread to enter the message handler for this binding in parallel.
virtual void EnableNextDispatch() {
ZX_ASSERT_MSG(false, "Transaction does not support explicitly resuming dispatch.");
}
// A transaction will only be destroyed after one of three actions happens to it:
// the Completer containing it is destroyed, the transaction is closed, or ownership is taken from
// the transaction.
// An async transaction is destroyed immediately after the request has been closed or the
// Completer containing it is destroyed.
// A synchronous transaction lives on the stack, and may provide status/errors to the binding
// dispatch loop.
// If the implementation paused message dispatching above, it may resume dispatching here.
virtual ~Transaction() = default;
};
// Manages the lifetime of a transaction and serves as the base class of FIDL method-specific
// completers. It ensures that no transaction will be silently dropped without replying or closing.
class CompleterBase {
public:
CompleterBase(const CompleterBase&) = delete;
CompleterBase& operator=(const CompleterBase&) = delete;
// Instructs the transaction to send an epitaph and then close the underlying transport.
// |status| may be an error status, or |ZX_OK|, which indicates normal (expected) closure.
void Close(zx_status_t status);
// Resumes the asynchronous wait on the underlying channel, enabling another dispatcher thread to
// enter the message handler for this binding. This must only be called from the scope of the
// message handler.
// TODO(madhaviyengar): Hide this from Completer::Async in a cleaner way.
virtual void EnableNextDispatch();
protected:
explicit CompleterBase(Transaction* transaction, bool owned, bool method_expects_reply)
: transaction_(transaction), owned_(owned), needs_to_reply_(method_expects_reply) {}
CompleterBase(CompleterBase&& other) noexcept;
CompleterBase& operator=(CompleterBase&& other) noexcept;
~CompleterBase();
// Encode and write |decoded_msg| as the reply.
template <typename FidlType>
void SendReply(DecodedMessage<FidlType> decoded_msg) {
static_assert(IsFidlMessage<FidlType>::value, "FIDL transactional message type required");
EncodeResult<FidlType> encode_result = fidl::Encode(std::move(decoded_msg));
if (encode_result.status != ZX_OK) {
Close(ZX_ERR_INTERNAL);
return;
}
SendReply(encode_result.message.ToAnyMessage());
}
// Move the contents of |transaction_| to heap and return it.
std::unique_ptr<Transaction> TakeOwnership();
private:
void SendReply(Message msg);
void EnsureHasTransaction();
void DropTransaction();
Transaction* transaction_;
bool owned_;
bool needs_to_reply_;
};
// Completers of a FIDL method call.
//
// |Base| is a FIDL method-specific sub-class of |CompleterBase| generated
// by the FIDL compiler which provides the necessary Reply() implementations.
// For one-way FIDL methods, |Base| is |CompleterBase| itself.
template <typename Base = CompleterBase>
struct Completer final {
static constexpr bool kExpectingReply = !std::is_same<Base, CompleterBase>::value;
// An asynchronous responder owns the underlying transaction.
// It may be stored in the server implementation for delayed reply. It is okay for the handler
// to return without replying when the Completer::Sync is converted to Completer::Async, e.g.
//
// virtual void MyMethod(Foo foo, Bar bar, MyMethodCompleter::Sync completer) {
// PerformLongOperation(foo, bar).then([completer = completer.ToAsync()] () {
// // Here the type of |completer| is |MyMethodCompleter::Async|.
// completer.Reply(...);
// });
// }
//
class Async final : public Base {
public:
Async() : Base() {}
explicit Async(std::unique_ptr<Transaction> owned_transaction)
: Base(owned_transaction.release(), true /*owned*/, kExpectingReply) {}
private:
// EnableNextDispatch() must only be invoked within the message handler. This is an attempt to
// enforce such behavior by restricting it to the fidl::Completer<T>::Sync variant.
void EnableNextDispatch() final {
ZX_ASSERT_MSG(false, "EnableNextDispatch() is only valid on a fidl::Completer<T>::Sync.");
}
};
// The server handler function will be given FooCompleter::Sync, an object tailor made for
// the specific method call. It only exposes methods to send the corresponding reply type
// given the request. The completer wraps a |fidl::Transaction|. The handler must interact
// with the sync responder in one way or another. If the handler forgets to e.g. reply the
// request, it will result in a run-time assert.
class Sync final : public Base {
public:
explicit Sync(Transaction* borrowed_transaction)
: Base(borrowed_transaction, false /*owned*/, kExpectingReply) {}
// Move a sync responder to its async counterpart, such that the reply could be
// issued asynchronously. This causes the transaction to be moved to heap.
Async ToAsync() { return Async(Base::TakeOwnership()); }
};
};
} // namespace fidl
#endif // LIB_FIDL_LLCPP_TRANSACTION_H_