blob: 4912ba1fefab59a58db1b32b5a289f6fda0f665d [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_SYNC_CALL_H_
#define LIB_FIDL_LLCPP_SYNC_CALL_H_
#include <lib/fidl/llcpp/coding.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 <cstddef>
#include <cstdint>
#include <cstdlib>
#include <memory>
namespace fidl {
namespace internal {
// Class representing the result of a one-way FIDL call.
// status() returns the encoding and transport level status.
// If status() is not ZX_OK, error() contains a human-readable string for debugging purposes.
class StatusAndError : public FromFailureMixin<StatusAndError> {
public:
StatusAndError() = default;
StatusAndError(zx_status_t status, const char* error) : status_(status), error_(error) {}
StatusAndError(const StatusAndError&) = delete;
StatusAndError& operator=(const StatusAndError&) = delete;
[[nodiscard]] zx_status_t status() const { return status_; }
[[nodiscard]] const char* error() const { return error_; }
[[nodiscard]] bool ok() const { return status_ == ZX_OK; }
protected:
StatusAndError(StatusAndError&&) = default;
StatusAndError& operator=(StatusAndError&&) = default;
// Initialize ourself from one of EncodeResult, DecodeResult, LinearizeResult, in the case of
// error hence there is no message.
template <typename SomeResult>
void SetFailure(SomeResult failure) {
ZX_DEBUG_ASSERT(failure.status != ZX_OK);
status_ = failure.status;
error_ = failure.error;
}
zx_status_t status_ = ZX_ERR_INTERNAL;
const char* error_ = nullptr;
};
// The base class for response-owning and non-owning SyncCalls.
//
// It is meant to support |OwnedSyncCallBase| and |UnownedSyncCallBase|.
template <typename ResponseType>
class SyncCallBase : private StatusAndError {
public:
SyncCallBase(const SyncCallBase&) = delete;
SyncCallBase& operator=(const SyncCallBase&) = delete;
using StatusAndError::error;
using StatusAndError::ok;
using StatusAndError::status;
// Convenience accessor for the FIDL response message pointer.
// The returned pointer is never null, unless the object is moved.
// Asserts that the call was successful.
ResponseType* Unwrap() {
ZX_ASSERT(status_ == ZX_OK);
return message_.message();
}
// Convenience accessor for the FIDL response message pointer.
// Asserts that the object was not moved.
// Asserts that the call was successful.
ResponseType& value() {
ZX_ASSERT(status_ == ZX_OK);
ZX_ASSERT(message_.message() != nullptr);
return *message_.message();
}
const ResponseType& value() const {
ZX_ASSERT(status_ == ZX_OK);
ZX_ASSERT(message_.message() != nullptr);
return *message_.message();
}
// Convenience accessor for the FIDL response message pointer.
// Asserts that the object was not moved.
// Asserts that the call was successful.
ResponseType* operator->() { return &value(); }
const ResponseType* operator->() const { return &value(); }
// Convenience accessor for the FIDL response message pointer.
// Asserts that the object was not moved.
// Asserts that the call was successful.
ResponseType& operator*() { return value(); }
const ResponseType& operator*() const { return value(); }
protected:
SyncCallBase() = default;
~SyncCallBase() = default;
SyncCallBase(SyncCallBase&& other) = default;
SyncCallBase& operator=(SyncCallBase&& other) = default;
using StatusAndError::error_;
using StatusAndError::SetFailure;
using StatusAndError::status_;
// Initialize ourself from the DecodeResult corresponding to the response.
void SetResult(fidl::DecodeResult<ResponseType> decode_result) {
StatusAndError::status_ = decode_result.status;
StatusAndError::error_ = decode_result.error;
message_ = std::move(decode_result.message);
ZX_ASSERT(StatusAndError::status_ != ZX_OK || message_.is_valid());
}
fidl::DecodedMessage<ResponseType>& decoded_message() { return message_; }
private:
fidl::DecodedMessage<ResponseType> message_;
};
// Base class representing the result of a two-way FIDL call, with ownership of the response buffer.
// It is always inherited by generated code performing the call. Do not instantiate manually.
// Types returned by the managed flavor will inherit from this class.
//
// Holds a |DecodedMessage<ResponseType>| in addition to providing status() and error().
// If status() is ZX_OK, Unwrap() returns a valid decoded message of type ResponseType.
// If status() is ZX_OK, value() returns a valid decoded message of type ResponseType.
// Otherwise, error() contains a human-readable string for debugging purposes.
//
// Note: this class does not add new members on top of |SyncCallBase|.
template <typename ResponseType>
class OwnedSyncCallBase : private SyncCallBase<ResponseType> {
using Super = SyncCallBase<ResponseType>;
using ResponseStorageType = ResponseStorage<ResponseType>;
public:
OwnedSyncCallBase(const OwnedSyncCallBase&) = delete;
OwnedSyncCallBase& operator=(const OwnedSyncCallBase&) = delete;
OwnedSyncCallBase(OwnedSyncCallBase&& other) {
if (this != &other) {
MoveImpl(std::move(other));
}
}
OwnedSyncCallBase& operator=(OwnedSyncCallBase&& other) {
if (this != &other) {
MoveImpl(std::move(other));
}
return *this;
}
using Super::error;
using Super::ok;
using Super::status;
using Super::Unwrap;
using Super::value;
using Super::operator->;
using Super::operator*;
protected:
OwnedSyncCallBase() = default;
~OwnedSyncCallBase() {
// Before handing over to the member and super class destructor, release the ownership
// decoded message has on the storage first, to prevent use-after-free.
Super::decoded_message().Reset(fidl::BytePart());
}
fidl::BytePart response_buffer() { return response_storage_.buffer(); }
using Super::SetFailure;
// Initialize ourself from the DecodeResult corresponding to the response.
// Invariant: the address of the response message must equal to that of our managed buffer.
void SetResult(fidl::DecodeResult<ResponseType> decode_result) {
ZX_ASSERT(decode_result.message.message() ==
reinterpret_cast<ResponseType*>(response_buffer().data()) ||
!decode_result.message.is_valid());
Super::SetResult(std::move(decode_result));
}
private:
ResponseStorageType response_storage_;
void MoveImpl(OwnedSyncCallBase&& other) {
Super::status_ = other.Super::status_;
Super::error_ = other.Super::error_;
Super::decoded_message().Reset(fidl::BytePart());
if constexpr (ResponseStorageType::kWillCopyBufferDuringMove) {
// Use the linearizer to update pointers and move handles, in the case of
// inlined response buffers.
if (other.Super::decoded_message().is_valid()) {
// If there are pointers, they need to be patched.
// Otherwise, we get away with a memcpy.
if constexpr (NeedsEncodeDecode<ResponseType>::value && ResponseType::HasPointer) {
auto result =
fidl::Linearize(other.Super::decoded_message().message(), response_buffer());
(void)other.Super::decoded_message().Release();
ZX_DEBUG_ASSERT(result.status == ZX_OK);
Super::decoded_message() = std::move(result.message);
} else {
auto other_bytes = other.Super::decoded_message().Release();
response_storage_ = std::move(other.response_storage_);
auto our_bytes = response_buffer();
our_bytes.set_actual(other_bytes.actual());
Super::decoded_message().Reset(std::move(our_bytes));
}
}
} else {
// If the response buffer is a unique_ptr, just move the unique_ptr.
response_storage_ = std::move(other.response_storage_);
Super::decoded_message() = std::move(other.Super::decoded_message());
}
}
};
// Class representing the result of a two-way FIDL call, without ownership of the response buffers.
// It is always inherited by generated code performing the call. Do not instantiate manually.
// Type returned by the caller-allocating flavor will inherit from this class.
//
// Holds a |DecodedMessage<ResponseType>| in addition to providing status() and error().
// If status() is ZX_OK, Unwrap() returns a valid decoded message of type ResponseType.
// If status() is ZX_OK, value() returns a valid decoded message of type ResponseType.
// Otherwise, error() contains a human-readable string for debugging purposes.
template <typename ResponseType>
using UnownedSyncCallBase = SyncCallBase<ResponseType>;
} // namespace internal
// An buffer holding data inline, sized specifically for |FidlType|.
// It can be used to allocate request/response buffers when using the caller-allocate or in-place
// flavor. For example:
//
// fidl::Buffer<mylib::FooRequest> request_buffer;
// fidl::Buffer<mylib::FooResponse> response_buffer;
// auto result = mylib::Call::Foo(channel, request_buffer.view(), args, response_buffer.view());
//
// Since the |Buffer| type is always used at client side, we can assume responses are processed in
// the |kSending| context, and requests are processed in the |kReceiving| context.
template <typename FidlType>
using Buffer =
internal::AlignedBuffer<internal::IsResponseType<FidlType>::value
? MaxSizeInChannel<FidlType, MessageDirection::kReceiving>()
: MaxSizeInChannel<FidlType, MessageDirection::kSending>()>;
} // namespace fidl
#endif // LIB_FIDL_LLCPP_SYNC_CALL_H_