| // 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. |
| |
| #include <lib/fidl/llcpp/async_binding.h> |
| #include <lib/fidl/llcpp/async_transaction.h> |
| #include <lib/fidl/llcpp/message.h> |
| #include <lib/fidl/llcpp/server.h> |
| #include <zircon/assert.h> |
| |
| namespace fidl { |
| |
| namespace internal { |
| |
| std::optional<UnbindInfo> AsyncTransaction::Dispatch(std::shared_ptr<AsyncBinding>&& binding, |
| fidl_incoming_msg_t* msg) { |
| ZX_ASSERT(!owned_binding_); |
| ZX_ASSERT(!moved_); |
| bool moved = false; |
| moved_ = &moved; |
| // Take ownership of the internal (dispatcher) reference to the AsyncBinding. Until code executed |
| // in this scope releases ownership, no other thread may access the binding via keep_alive_. |
| owned_binding_ = std::move(binding); |
| // Avoid static_pointer_cast for now since it results in atomic inc/dec. |
| auto* binding_raw = static_cast<AnyAsyncServerBinding*>(owned_binding_.get()); |
| fidl::DispatchResult dispatch_result = binding_raw->interface_->dispatch_message(msg, this); |
| if (moved) |
| return {}; // Return if `this` is no longer valid. |
| moved_ = nullptr; |
| // Transfer ownership of the binding back to the dispatcher if we still have it. |
| if (owned_binding_) |
| binding_raw->keep_alive_ = std::move(owned_binding_); |
| |
| switch (dispatch_result) { |
| case fidl::DispatchResult::kFound: |
| // Propagate any error that happened during the message handling. |
| return unbind_info_; |
| case fidl::DispatchResult::kNotFound: |
| // The message was not recognized by the |dispatch_fn_|. |
| return UnbindInfo{UnbindInfo::kUnexpectedMessage, ZX_ERR_NOT_SUPPORTED}; |
| } |
| } |
| |
| zx_status_t AsyncTransaction::Reply(fidl::OutgoingMessage* message) { |
| ZX_ASSERT(txid_ != 0); |
| auto txid = txid_; |
| txid_ = 0; |
| |
| // Get a strong reference to the binding. Avoid unnecessarily copying the reference if |
| // owned_binding_ is valid. On error, the reference will be consumed by Close(). |
| std::shared_ptr<AsyncBinding> tmp = owned_binding_ ? nullptr : unowned_binding_.lock(); |
| auto& binding = owned_binding_ ? owned_binding_ : tmp; |
| if (!binding) |
| return ZX_ERR_CANCELED; |
| |
| message->set_txid(txid); |
| message->Write(binding->channel()); |
| return message->status(); |
| } |
| |
| void AsyncTransaction::EnableNextDispatch() { |
| if (!owned_binding_) |
| return; // Has no effect if the Transaction does not own the binding. |
| auto* binding_raw = static_cast<AnyAsyncServerBinding*>(owned_binding_.get()); |
| unowned_binding_ = owned_binding_; // Preserve a weak reference to the binding. |
| binding_raw->keep_alive_ = std::move(owned_binding_); |
| if (binding_raw->EnableNextDispatch() == ZX_OK) { |
| *binding_released_ = true; |
| } else { |
| unbind_info_ = {UnbindInfo::kUnbind, ZX_OK}; |
| } |
| } |
| |
| void AsyncTransaction::Close(zx_status_t epitaph) { |
| if (!owned_binding_) { |
| if (auto binding = unowned_binding_.lock()) { |
| auto* binding_raw = static_cast<AnyAsyncServerBinding*>(binding.get()); |
| binding_raw->Close(std::move(binding), epitaph); |
| } |
| return; |
| } |
| unbind_info_ = {UnbindInfo::kClose, epitaph}; // OnUnbind() will run after Dispatch() returns. |
| // Return ownership of the binding to the dispatcher. |
| auto* binding_raw = static_cast<AnyAsyncServerBinding*>(owned_binding_.get()); |
| binding_raw->keep_alive_ = std::move(owned_binding_); |
| } |
| |
| void AsyncTransaction::InternalError(UnbindInfo error) { |
| if (!owned_binding_) { |
| if (auto binding = unowned_binding_.lock()) { |
| auto* binding_raw = static_cast<AnyAsyncServerBinding*>(binding.get()); |
| binding_raw->InternalError(std::move(binding), error); |
| } |
| return; |
| } |
| unbind_info_ = error; // OnUnbind() will run after Dispatch() returns. |
| // Return ownership of the binding to the dispatcher. |
| auto* binding_raw = static_cast<AnyAsyncServerBinding*>(owned_binding_.get()); |
| binding_raw->keep_alive_ = std::move(owned_binding_); |
| } |
| |
| std::unique_ptr<Transaction> AsyncTransaction::TakeOwnership() { |
| ZX_ASSERT(owned_binding_); |
| ZX_ASSERT(moved_); |
| *moved_ = true; |
| moved_ = nullptr; // This should only ever be called once. |
| unowned_binding_ = owned_binding_; // Preserve a weak reference to the binding. |
| auto* binding_raw = static_cast<AnyAsyncServerBinding*>(owned_binding_.get()); |
| binding_raw->keep_alive_ = std::move(owned_binding_); |
| return std::make_unique<AsyncTransaction>(std::move(*this)); |
| } |
| |
| bool AsyncTransaction::IsUnbound() { |
| // The channel is unbound if this transaction neither owns the binding nor can get a strong |
| // reference to it. |
| return !owned_binding_ && !unowned_binding_.lock(); |
| } |
| |
| } // namespace internal |
| |
| } // namespace fidl |