| // 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, Message 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(Message 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 |