| // 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 <zircon/assert.h> |
| |
| namespace fidl { |
| |
| namespace internal { |
| |
| void AsyncTransaction::Dispatch(std::shared_ptr<AsyncBinding>&& binding, fidl_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); |
| dispatch_fn_(owned_binding_->interface_, 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_) { |
| auto* binding = owned_binding_.get(); |
| binding->keep_alive_ = std::move(owned_binding_); |
| } |
| } |
| |
| void AsyncTransaction::Reply(fidl::Message msg) { |
| 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; |
| |
| if (msg.bytes().actual() < sizeof(fidl_message_header_t)) { |
| // TODO(42086): Propagate this error back up to the user. |
| binding->Close(std::move(binding), ZX_ERR_INVALID_ARGS); |
| return; |
| } |
| auto hdr = reinterpret_cast<fidl_message_header_t*>(msg.bytes().data()); |
| hdr->txid = txid; |
| auto status = binding->channel()->write(0, msg.bytes().data(), msg.bytes().actual(), |
| msg.handles().data(), msg.handles().actual()); |
| if (status != ZX_OK) |
| binding->Close(std::move(binding), status); |
| // release ownership on handles, which have been consumed by channel write. |
| msg.ClearHandlesUnsafe(); |
| } |
| |
| void AsyncTransaction::EnableNextDispatch() { |
| if (!owned_binding_) |
| return; // Has no effect if the Transaction does not own the binding. |
| auto* binding = owned_binding_.get(); |
| unowned_binding_ = owned_binding_; // Preserve a weak reference to the binding. |
| binding->keep_alive_ = std::move(owned_binding_); |
| if ((*resume_status_ = binding->EnableNextDispatch()) == ZX_OK) |
| *binding_released_ = true; |
| } |
| |
| void AsyncTransaction::Close(zx_status_t epitaph) { |
| if (!owned_binding_) { |
| if (auto binding = unowned_binding_.lock()) |
| binding->Close(std::move(binding), epitaph); |
| return; |
| } |
| *resume_status_ = ZX_ERR_CANCELED; // OnUnbind() will run after Dispatch() returns. |
| // Close() will not be able to cancel the wait. Restore the internal reference. |
| owned_binding_->keep_alive_ = owned_binding_; |
| owned_binding_->Close(std::move(owned_binding_), epitaph); |
| } |
| |
| 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 = owned_binding_.get(); |
| binding->keep_alive_ = std::move(owned_binding_); |
| return std::make_unique<AsyncTransaction>(std::move(*this)); |
| } |
| |
| } // namespace internal |
| |
| } // namespace fidl |