blob: 736406908c5c8acd23f6ab703feeb4b77febfc45 [file] [log] [blame]
// 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