| // 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 <atomic> |
| #include <cstdint> |
| #include <utility> |
| |
| #include "lib/fidl/cpp/internal/logging.h" |
| |
| namespace fidl { |
| namespace internal { |
| namespace { |
| |
| constexpr uint32_t kUserspaceTxidMask = 0x7FFFFFFF; |
| |
| // Enables client side error callbacks, which will eventually always be |
| // enabled. This should generally not be set, but exists to provide a |
| // mechanism to denylist test cases |
| // TODO(fxbug.dev/68206) Remove this. |
| static std::atomic<uint32_t> transitory_clientside_error_disable_count(0); |
| |
| } // 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; |
| } |
| |
| void 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_VALIDATING_ERROR(message, type, error_msg); |
| if (transitory_clientside_error_disable_count == 0) { |
| if (reader_.error_handler_ != nullptr) { |
| reader_.error_handler_(status); |
| } |
| reader_.Reset(); |
| } |
| return; |
| } |
| 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); |
| if (transitory_clientside_error_disable_count == 0) { |
| if (reader_.error_handler_ != nullptr) { |
| reader_.error_handler_(status); |
| } |
| reader_.Reset(); |
| } |
| } |
| return; |
| } |
| if (response_handler) |
| handlers_.emplace(txid, std::move(response_handler)); |
| } |
| |
| 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(); |
| } |
| |
| TransitoryProxyControllerClientSideErrorDisabler:: |
| TransitoryProxyControllerClientSideErrorDisabler() { |
| ++transitory_clientside_error_disable_count; |
| } |
| TransitoryProxyControllerClientSideErrorDisabler:: |
| ~TransitoryProxyControllerClientSideErrorDisabler() { |
| --transitory_clientside_error_disable_count; |
| } |
| |
| } // namespace internal |
| } // namespace fidl |