blob: da1434c8b657cf1e8dacc4873b8072b741eb2215 [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_INCLUDE_LIB_FIDL_LLCPP_SYNC_CALL_H_
#define LIB_FIDL_LLCPP_INCLUDE_LIB_FIDL_LLCPP_SYNC_CALL_H_
#include <lib/fidl/llcpp/async_binding.h>
#include <lib/fidl/llcpp/internal/arrow.h>
#include <lib/fidl/llcpp/internal/endpoints.h>
#include <lib/fidl/llcpp/internal/server_details.h>
#include <lib/fidl/llcpp/message_storage.h>
#include <lib/fidl/llcpp/traits.h>
#include <cstddef>
#include <cstdint>
#include <cstdlib>
#include <memory>
namespace fidl {
//
// The logic here for calculating buffer size needs to be kept in sync with the
// one defined in tools/fidl/lib/fidlgen_cpp/protocol.go
//
// Helper to calculate the maximum possible message size for a FIDL type,
// clamped at the Zircon channel packet size.
template <typename FidlType, const MessageDirection Direction>
constexpr uint32_t MaxSizeInChannel() {
return internal::ClampedMessageSize<FidlType, Direction>();
}
// Helper to calculate a safe buffer size for use in caller-allocating flavors
// to call |Method| from a synchronous client, assuming the size of each message
// (request/response) is clamped at the Zircon channel packet size.
//
// |Method| is a method marker that looks like
// |fuchsia_mylib::SomeProtocol::SomeMethod|.
//
// This could be used as part of determining an optimum initial size for a FIDL
// arena or buffer span.
template <typename Method>
constexpr uint32_t SyncClientMethodBufferSizeInChannel() {
// TODO(fxbug.dev/85843): We should be able optimize this to just the max of
// the send/receive size, once Zircon channel calls guarantee that the
// send/receive buffers can overlap.
uint32_t size = 0;
static_assert(IsFidlTransactionalMessage<internal::TransactionalRequest<Method>>::value,
"|Method| must be a FIDL method marker type");
size += MaxSizeInChannel<internal::TransactionalRequest<Method>, MessageDirection::kSending>();
// If it's a two-way method, count the response as well.
if constexpr (IsFidlTransactionalMessage<internal::TransactionalResponse<Method>>::value) {
size +=
MaxSizeInChannel<internal::TransactionalResponse<Method>, MessageDirection::kReceiving>();
}
return size;
}
// Helper to calculate a safe buffer size for use in caller-allocating flavors
// to call |Method| from an asynchronous client, assuming the size of each
// message (request) is clamped at the Zircon channel packet size.
//
// An asynchronous client handles responses asynchronously, and from one place
// in the event loop. Therefore, only the request portion of a two-way call need
// to be factored in buffer size calculations.
//
// |Method| is a method marker that looks like
// |fuchsia_mylib::SomeProtocol::SomeMethod|.
//
// This could be used as part of determining an optimum initial size for a FIDL
// arena or buffer span.
template <typename Method>
constexpr uint32_t AsyncClientMethodBufferSizeInChannel() {
static_assert(IsFidlTransactionalMessage<internal::TransactionalRequest<Method>>::value,
"|Method| must be a FIDL method marker type");
return MaxSizeInChannel<internal::TransactionalRequest<Method>, MessageDirection::kSending>();
}
// Helper to calculate a safe buffer size for use in caller-allocating flavors
// to reply to |Method| from a server, assuming the size of each message is
// clamped at the Zircon channel packet size.
//
// |Method| is a method marker that looks like
// |fuchsia_mylib::SomeProtocol::SomeMethod|.
//
// This could be used as part of determining an optimum initial size for a FIDL
// arena or buffer span.
template <typename Method>
constexpr uint32_t ServerReplyBufferSizeInChannel() {
static_assert(IsFidlTransactionalMessage<internal::TransactionalResponse<Method>>::value,
"|Method| must be a FIDL method marker type");
return MaxSizeInChannel<internal::TransactionalResponse<Method>, MessageDirection::kSending>();
}
// Helper to calculate a safe buffer size for use in caller-allocating flavors
// to send a |Method| event, assuming the size of each message is clamped at
// the Zircon channel packet size.
//
// |Method| is a method marker that looks like
// |fuchsia_mylib::SomeProtocol::SomeMethod|.
//
// This could be used as part of determining an optimum initial size for a FIDL
// arena or buffer span.
template <typename Method>
constexpr uint32_t EventReplyBufferSizeInChannel() {
static_assert(IsFidlTransactionalMessage<internal::TransactionalEvent<Method>>::value,
"|Method| must be a FIDL method marker type");
return MaxSizeInChannel<internal::TransactionalEvent<Method>, MessageDirection::kSending>();
}
namespace internal {
// |CallerAllocatingImpl| provides a |Type| which is the corresponding
// caller-allocating messaging implementation given a |ManagedImpl| with managed
// memory allocation. For example,
//
// CallerAllocatingImpl<WireSyncClientImpl, FidlProtocol>::Type
//
// should be
//
// WireSyncBufferClientImpl<FidlProtocol>
//
// It is used to easily derive the caller-allocating messaging type given a
// regular messaging type.
template <template <typename FidlProtocol> class ManagedImpl, typename FidlProtocol>
struct CallerAllocatingImpl;
// Associate |WireSyncClientImpl| (managed) and |WireSyncBufferClientImpl|
// (caller-allocating).
template <typename FidlProtocol>
struct CallerAllocatingImpl<WireSyncClientImpl, FidlProtocol> {
using Type = WireSyncBufferClientImpl<FidlProtocol>;
};
// Associate |WireWeakEventSender| (managed) and |WireWeakBufferEventSender|
// (caller-allocating).
template <typename FidlProtocol>
struct CallerAllocatingImpl<WireWeakEventSender, FidlProtocol> {
using Type = WireWeakBufferEventSender<FidlProtocol>;
};
// Associate |WireEventSender| (managed) and |WireBufferEventSender|
// (caller-allocating).
template <typename FidlProtocol>
struct CallerAllocatingImpl<WireEventSender, FidlProtocol> {
using Type = WireBufferEventSender<FidlProtocol>;
};
// A veneer interface object for client/server messaging implementations that
// operate on a borrowed client/server endpoint, and where the implementation
// automatically manages the buffer for message encoding/decoding. Those
// implementations should inherit from this class following CRTP. Example uses
// of this veneer:
//
// * Making synchronous one-way or two-way calls.
// * Sending events.
//
// |Derived| implementations must not add any state, only behavior.
template <typename Derived>
struct SyncEndpointManagedVeneer {
public:
explicit SyncEndpointManagedVeneer(fidl::internal::AnyUnownedTransport transport)
: transport_(transport) {}
// Returns a pointer to the concrete messaging implementation.
Derived* operator->() && {
// Required for the static_cast in to work: we are aliasing the base class
// into |Derived|.
static_assert(sizeof(Derived) == sizeof(SyncEndpointManagedVeneer),
"Derived implementations must not add any state");
return static_cast<Derived*>(this);
}
protected:
// Used by implementations to access the transport, hence prefixed with an
// underscore to avoid the unlikely event of a name collision.
fidl::internal::AnyUnownedTransport _transport() const { return transport_; }
private:
fidl::internal::AnyUnownedTransport transport_;
};
// A veneer interface object for client/server messaging implementations that
// operate on a borrowed client/server endpoint, and where the caller provides
// the buffer for message encoding/decoding. Those implementations should
// inherit from this class following CRTP. Example uses of this veneer:
//
// * Making synchronous one-way or two-way calls.
// * Sending events.
//
// Compared to |SyncEndpointManagedVeneer|, this class additionally stores an
// allocator, such that subclasses maybe use it during encoding/decoding.
//
// |Derived| implementations must not add any state, only behavior.
template <typename Derived>
struct SyncEndpointBufferVeneer {
explicit SyncEndpointBufferVeneer(fidl::internal::AnyUnownedTransport transport,
AnyBufferAllocator&& allocator)
: transport_(transport), allocator_(std::move(allocator)) {}
// Returns a pointer to the concrete messaging implementation.
Derived* operator->() {
// Required for the static_cast in to work: we are aliasing the base class
// into |Derived|.
static_assert(sizeof(Derived) == sizeof(SyncEndpointBufferVeneer),
"Derived implementations must not add any state");
return static_cast<Derived*>(this);
}
protected:
// Used by implementations to access the transport, hence prefixed with an
// underscore to avoid the unlikely event of a name collision.
fidl::internal::AnyUnownedTransport _transport() const { return transport_; }
// Used by implementations to access the allocator, hence prefixed with an
// underscore to avoid the unlikely event of a name collision.
AnyBufferAllocator& _allocator() { return allocator_; }
private:
fidl::internal::AnyUnownedTransport transport_;
AnyBufferAllocator allocator_;
};
// A veneer interface object for client/server messaging implementations that
// operate on a borrowed client/server endpoint. This class exposes both
// managed and caller-allocating flavors, and delegates to
// |SyncEndpointManagedVeneer| and |SyncEndpointBufferVeneer| respectively.
//
// |SyncImpl| should be the template messaging class,
// e.g. |WireSyncClientImpl| (without passing template parameters).
// |FidlProtocol| should be the protocol marker.
//
// It must not outlive the borrowed endpoint.
template <template <typename FidlProtocol> class SyncImpl, typename FidlProtocol>
class SyncEndpointVeneer final {
private:
using CallerAllocatingImpl =
typename ::fidl::internal::CallerAllocatingImpl<SyncImpl, FidlProtocol>::Type;
public:
explicit SyncEndpointVeneer(fidl::internal::AnyUnownedTransport transport)
: transport_(std::move(transport)) {}
// Returns a veneer object for the concrete messaging implementation.
internal::SyncEndpointManagedVeneer<SyncImpl<FidlProtocol>> operator->() && {
return internal::SyncEndpointManagedVeneer<SyncImpl<FidlProtocol>>(transport_);
}
// Returns a veneer object which exposes the caller-allocating API, using
// the provided |resource| to allocate buffers necessary for each call.
// The requests and responses (if applicable) will live on those buffers.
//
// Examples of supported memory resources are:
//
// * |fidl::BufferSpan|, referencing a range of bytes.
// * |fidl::AnyArena&|, referencing an arena.
// * Any type for which there is a |MakeAnyBufferAllocator| specialization.
// See |AnyBufferAllocator|.
//
// The returned object borrows from this object, hence must not outlive
// the current object.
//
// The returned object may be briefly persisted for use over multiple calls:
//
// fidl::Arena my_arena;
// auto buffered = fidl::WireCall(client_end).buffer(my_arena);
// fidl::WireUnownedResult foo = buffered->FooMethod();
// fidl::WireUnownedResult bar = buffered->BarMethod();
// ...
//
// In this situation, those calls will all use the initially provided memory
// resource (`my_arena`) to allocate their message buffers. The memory
// resource won't be reset/overwritten across calls. This means it's possible
// to access the result from |FooMethod| after making another |BarMethod|
// call. Note that if a |BufferSpan| is provided as the memory resource,
// sharing memory resource in this manner may eventually exhaust the capacity
// of the buffer span since it represents a single fixed size buffer. To reuse
// (overwrite) the underlying buffer across multiple calls, obtain a new
// caller-allocating veneer object for each call:
//
// fidl::BufferSpan span(some_large_buffer, size);
// auto client = fidl::WireCall(client_end);
// client.buffer(span)->FooMethod();
// client.buffer(span)->BarMethod();
//
template <typename MemoryResource>
auto buffer(MemoryResource&& resource) {
return SyncEndpointBufferVeneer<CallerAllocatingImpl>{
transport_, MakeAnyBufferAllocator(std::forward<MemoryResource>(resource))};
}
private:
fidl::internal::AnyUnownedTransport transport_;
};
template <template <typename FidlProtocol> class SyncImpl, typename FidlProtocol>
class WeakEventSenderVeneer final {
private:
using CallerAllocatingImpl =
typename ::fidl::internal::CallerAllocatingImpl<SyncImpl, FidlProtocol>::Type;
public:
explicit WeakEventSenderVeneer(std::weak_ptr<::fidl::internal::AsyncServerBinding> binding)
: binding_(std::move(binding)) {}
// Returns a veneer object for sending events with managed memory allocation.
Arrow<SyncImpl<FidlProtocol>> operator->() { return Arrow<SyncImpl<FidlProtocol>>(binding_); }
// Returns a veneer object which exposes the caller-allocating API, using
// the provided |resource| to allocate buffers necessary for each event.
// See documentation on |SyncEndpointVeneer::buffer| for detailed behavior.
template <typename MemoryResource>
auto buffer(MemoryResource&& resource) {
return Arrow<CallerAllocatingImpl>{
binding_, MakeAnyBufferAllocator(std::forward<MemoryResource>(resource))};
}
private:
std::weak_ptr<::fidl::internal::AsyncServerBinding> binding_;
};
} // namespace internal
// A buffer holding data inline, sized specifically for |FidlMethod| and for use
// with synchronous client methods. It can be used to provide request/response
// buffers when using the caller-allocating flavor. For example:
//
// // All space used for the |Foo| call is allocated from |buffer|.
// fidl::SyncClientBuffer<MyProtocol::Foo> buffer;
// fidl::WireUnownedResult result = fidl::WireCall<MyProtocol>(channel)
// .buffer(buffer.view())
// ->Foo(args);
//
template <typename FidlMethod>
using SyncClientBuffer =
internal::InlineMessageBuffer<SyncClientMethodBufferSizeInChannel<FidlMethod>()>;
// A buffer holding data inline, sized specifically for |FidlMethod| and for use
// with asynchronous client methods. It can be used to provide request buffers
// when using the caller-allocating flavor. For example:
//
// // All space used for the |Foo| call is allocated from |buffer|.
// fidl::AsyncClientBuffer<MyProtocol::Foo> buffer;
// fidl::WireClient client(...);
// client.buffer(buffer.view())->Foo(args);
//
template <typename FidlMethod>
using AsyncClientBuffer =
internal::InlineMessageBuffer<AsyncClientMethodBufferSizeInChannel<FidlMethod>()>;
// A buffer holding data inline, sized specifically for |FidlMethod| and for
// server-side use. It can be used to provide response buffers when using
// the caller-allocating flavor. For example:
//
// void Foo(Args args, FooCompleter::Sync& completer) {
// // All space used for the |Foo| reply is allocated from |buffer|.
// fidl::ServerBuffer<MyProtocol::Foo> buffer;
// completer.buffer(buffer.view()).Reply(args);
// }
//
template <typename FidlMethod>
using ServerBuffer = internal::InlineMessageBuffer<ServerReplyBufferSizeInChannel<FidlMethod>()>;
// A buffer holding data inline, sized specifically for |FidlMethod| and for
// server-side use. It can be used to provide event buffers when using
// the caller-allocating flavor. For example:
//
// void Foo(Args args, FooCompleter::Sync& completer) {
// // All space used for the |Foo| reply is allocated from |buffer|.
// fidl::EventBuffer<MyProtocol::Foo> buffer;
// fidl::WireSendEvent(binding).buffer(buffer.view())->OnEvent(args);
// }
//
template <typename FidlMethod>
using EventBuffer = internal::InlineMessageBuffer<EventReplyBufferSizeInChannel<FidlMethod>()>;
} // namespace fidl
#endif // LIB_FIDL_LLCPP_INCLUDE_LIB_FIDL_LLCPP_SYNC_CALL_H_