blob: ead841be057dde926bbfd71f5f47981362d0822e [file] [log] [blame]
// Copyright 2018 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.
#include "lib/fidl/cpp/internal/proxy_controller.h"
#include <utility>
#include "lib/fidl/cpp/internal/logging.h"
namespace fidl {
namespace internal {
namespace {
constexpr uint32_t kUserspaceTxidMask = 0x7FFFFFFF;
} // namespace
ProxyController::ProxyController() : reader_(this), next_txid_(1) {}
ProxyController::~ProxyController() = default;
ProxyController::ProxyController(ProxyController&& other)
: reader_(this), handlers_(std::move(other.handlers_)), next_txid_(other.next_txid_) {
reader_.TakeChannelAndErrorHandlerFrom(&other.reader());
other.Reset();
}
ProxyController& ProxyController::operator=(ProxyController&& other) {
if (this != &other) {
reader_.TakeChannelAndErrorHandlerFrom(&other.reader());
handlers_ = std::move(other.handlers_);
next_txid_ = other.next_txid_;
other.Reset();
}
return *this;
}
zx_status_t ProxyController::Send(const fidl_type_t* type, HLCPPOutgoingMessage message,
std::unique_ptr<SingleUseMessageHandler> response_handler) {
zx_txid_t txid = 0;
if (response_handler) {
txid = next_txid_++ & kUserspaceTxidMask;
while (!txid || handlers_.find(txid) != handlers_.end())
txid = next_txid_++ & kUserspaceTxidMask;
message.set_txid(txid);
}
const char* error_msg = nullptr;
zx_status_t status = message.Validate(type, &error_msg);
if (status != ZX_OK) {
FIDL_REPORT_ENCODING_ERROR(message, type, error_msg);
return status;
}
status = message.Write(reader_.channel().get(), 0);
if (status != ZX_OK) {
// Channel closure always races with any channel write that's been started but not yet
// completed, so ZX_ERR_PEER_CLOSED is expected to occur sometimes under normal operation.
if (status != ZX_ERR_PEER_CLOSED) {
FIDL_REPORT_CHANNEL_WRITING_ERROR(message, type, status);
}
return status;
}
if (response_handler)
handlers_.emplace(txid, std::move(response_handler));
return ZX_OK;
}
void ProxyController::Reset() {
reader_.Reset();
ClearPendingHandlers();
}
zx_status_t ProxyController::OnMessage(HLCPPIncomingMessage message) {
zx_txid_t txid = message.txid();
if (!txid) {
if (!proxy_)
return ZX_ERR_NOT_SUPPORTED;
return proxy_->Dispatch_(std::move(message));
}
auto it = handlers_.find(txid);
if (it == handlers_.end())
return ZX_ERR_NOT_FOUND;
std::unique_ptr<SingleUseMessageHandler> handler = std::move(it->second);
handlers_.erase(it);
return (*handler)(std::move(message));
}
void ProxyController::OnChannelGone() { ClearPendingHandlers(); }
void ProxyController::ClearPendingHandlers() {
// Avoid reentrancy problems by first copying the handlers map.
auto doomed = std::move(handlers_);
next_txid_ = 1;
doomed.clear();
}
} // namespace internal
} // namespace fidl