blob: f1ef27687a7224fe905347e6f30b60507e84502e [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 "buffer_collection.h"
#include <lib/ddk/trace/event.h>
#include <lib/sysmem-version/sysmem-version.h>
#include <zircon/compiler.h>
#include <zircon/errors.h>
#include <atomic>
#include "buffer_collection_token.h"
#include "fbl/ref_ptr.h"
#include "logical_buffer_collection.h"
#include "macros.h"
#include "node_properties.h"
#include "utils.h"
namespace sysmem_driver {
namespace {
// For max client vmo rights, we specify the RIGHT bits individually to avoid
// picking up any newly-added rights unintentionally. This is based on
// ZX_DEFAULT_VMO_RIGHTS, but with a few rights removed.
const uint32_t kMaxClientVmoRights =
// ZX_RIGHTS_BASIC, except ZX_RIGHT_INSPECT (at least for now).
ZX_RIGHT_TRANSFER | ZX_RIGHT_DUPLICATE | ZX_RIGHT_WAIT |
// ZX_RIGHTS_IO:
ZX_RIGHT_READ | ZX_RIGHT_WRITE |
// ZX_RIGHTS_PROPERTY allows a participant to set ZX_PROP_NAME for easier
// memory metrics. Nothing prevents participants from figting over the
// name, though the kernel should make each set/get atomic with respect to
// other set/get. This relies on ZX_RIGHTS_PROPERTY not implying anything
// that could be used as an attack vector between processes sharing a VMO.
ZX_RIGHTS_PROPERTY |
// We intentionally omit ZX_RIGHT_EXECUTE (indefinitely) and ZX_RIGHT_SIGNAL
// (at least for now).
//
// Remaining bits of ZX_DEFAULT_VMO_RIGHTS (as of this writing):
ZX_RIGHT_MAP;
} // namespace
// static
BufferCollection& BufferCollection::EmplaceInTree(
fbl::RefPtr<LogicalBufferCollection> logical_buffer_collection, BufferCollectionToken* token) {
// The token is passed in as a pointer because this method deletes token, but the caller must
// provide non-nullptr token.
ZX_DEBUG_ASSERT(token);
// This conversion from unique_ptr<> to RefPtr<> will go away once we move BufferCollection to
// LLCPP FIDL server binding.
fbl::RefPtr<Node> node(fbl::AdoptRef(new BufferCollection(logical_buffer_collection, *token)));
BufferCollection* buffer_collection_ptr = static_cast<BufferCollection*>(node.get());
// This also deletes token.
token->node_properties().SetNode(std::move(node));
return *buffer_collection_ptr;
}
BufferCollection::~BufferCollection() {
TRACE_DURATION("gfx", "BufferCollection::~BufferCollection", "this", this,
"logical_buffer_collection", &logical_buffer_collection());
}
void BufferCollection::CloseChannel(zx_status_t epitaph) {
// This essentially converts the OnUnboundFn semantic of getting called regardless of channel-fail
// vs. server-driven-fail into the more typical semantic where error_handler_ only gets called
// on channel-fail but not on server-driven-fail.
error_handler_ = {};
if (server_binding_)
server_binding_->Close(epitaph);
server_binding_ = {};
}
void BufferCollection::Bind(zx::channel channel) {
zx_info_handle_basic_t info;
zx_status_t status =
channel.get_info(ZX_INFO_HANDLE_BASIC, &info, sizeof(info), nullptr, nullptr);
if (status == ZX_OK) {
inspect_node_.CreateUint("channel_koid", info.koid, &properties_);
}
server_binding_ = fidl::BindServer(
logical_buffer_collection().parent_device()->dispatcher(), std::move(channel), this,
[this, this_ref = fbl::RefPtr<BufferCollection>(this)](
BufferCollection* collection, fidl::UnbindInfo info,
fidl::ServerEnd<fuchsia_sysmem::BufferCollection> channel) {
// We need to keep a refptr to this class, since the unbind happens asynchronously and
// can run after the parent closes a handle to this class.
if (error_handler_) {
zx_status_t status = info.status();
if (async_failure_result_ && info.reason() == fidl::Reason::kClose) {
// On kClose the error is always ZX_OK, so report the real error to
// LogicalBufferCollection if the close was caused by FailAsync or FailSync.
status = *async_failure_result_;
}
error_handler_(status);
}
// *this can be destroyed by ~this_ref here
});
}
void BufferCollection::Sync(SyncRequestView request, SyncCompleter::Sync& completer) {
// This isn't real churn. As a temporary measure, we need to count churn despite there not being
// any, since more real churn is coming soon, and we need to test the mitigation of that churn.
//
// TODO(fxbug.dev/33670): Remove this fake churn count once we're creating real churn from tests
// using new messages. Also consider making TableSet::CountChurn() private.
table_set_.CountChurn();
table_set_.MitigateChurn();
completer.Reply();
}
void BufferCollection::SetConstraintsAuxBuffers(
SetConstraintsAuxBuffersRequestView request,
SetConstraintsAuxBuffersCompleter::Sync& completer) {
table_set_.MitigateChurn();
if (is_set_constraints_aux_buffers_seen_) {
FailSync(FROM_HERE, completer, ZX_ERR_NOT_SUPPORTED,
"SetConstraintsAuxBuffers() can be called only once.");
return;
}
is_set_constraints_aux_buffers_seen_ = true;
if (is_done_) {
FailSync(FROM_HERE, completer, ZX_ERR_BAD_STATE,
"BufferCollectionToken::SetConstraintsAuxBuffers() when already is_done_");
return;
}
if (is_set_constraints_seen_) {
FailSync(FROM_HERE, completer, ZX_ERR_NOT_SUPPORTED,
"SetConstraintsAuxBuffers() after SetConstraints() causes failure.");
return;
}
ZX_DEBUG_ASSERT(!constraints_aux_buffers_);
constraints_aux_buffers_.emplace(table_set_, std::move(request->constraints));
// LogicalBufferCollection doesn't care about "clear aux buffers" constraints until the last
// SetConstraints(), so done for now.
}
void BufferCollection::SetConstraints(SetConstraintsRequestView request,
SetConstraintsCompleter::Sync& completer) {
TRACE_DURATION("gfx", "BufferCollection::SetConstraints", "this", this,
"logical_buffer_collection", &logical_buffer_collection());
table_set_.MitigateChurn();
std::optional<fuchsia_sysmem::wire::BufferCollectionConstraints> local_constraints(
std::move(request->constraints));
if (is_set_constraints_seen_) {
FailSync(FROM_HERE, completer, ZX_ERR_NOT_SUPPORTED, "2nd SetConstraints() causes failure.");
return;
}
is_set_constraints_seen_ = true;
if (is_done_) {
FailSync(FROM_HERE, completer, ZX_ERR_BAD_STATE,
"BufferCollectionToken::SetConstraints() when already is_done_");
// We're failing async - no need to try to fail sync.
return;
}
if (!request->has_constraints) {
// Not needed.
local_constraints.reset();
if (is_set_constraints_aux_buffers_seen_) {
// No constraints are fine, just not aux buffers constraints without main constraints, because
// I can't think of any reason why we'd need to support aux buffers constraints without main
// constraints, so disallow at least for now.
FailSync(FROM_HERE, completer, ZX_ERR_NOT_SUPPORTED,
"SetConstraintsAuxBuffers() && !has_constraints");
return;
}
}
ZX_DEBUG_ASSERT(!has_constraints());
// enforced above
ZX_DEBUG_ASSERT(!constraints_aux_buffers_ || local_constraints);
ZX_DEBUG_ASSERT(request->has_constraints == !!local_constraints);
{ // scope result
auto result = sysmem::V2CopyFromV1BufferCollectionConstraints(
table_set_.allocator(), local_constraints ? &local_constraints.value() : nullptr,
constraints_aux_buffers_ ? &(**constraints_aux_buffers_) : nullptr);
if (!result.is_ok()) {
FailSync(FROM_HERE, completer, ZX_ERR_INVALID_ARGS,
"V2CopyFromV1BufferCollectionConstraints() failed");
return;
}
ZX_DEBUG_ASSERT(!result.value().IsEmpty() || !local_constraints);
node_properties().SetBufferCollectionConstraints(TableHolder(table_set_, result.take_value()));
} // ~result
// No longer needed.
constraints_aux_buffers_.reset();
node_properties().LogInfo(FROM_HERE, "BufferCollection::SetConstraints()");
node_properties().LogConstraints(FROM_HERE);
// LogicalBufferCollection will ask for constraints when it needs them,
// possibly during this call if this is the last participant to report
// having initial constraints.
//
// The LogicalBufferCollection cares if this BufferCollection view has null
// constraints, but only later when it asks for the specific constraints.
logical_buffer_collection().OnSetConstraints();
// |this| may be gone at this point, if the allocation failed. Regardless,
// SetConstraints() worked, so ZX_OK.
}
void BufferCollection::WaitForBuffersAllocated(WaitForBuffersAllocatedRequestView request,
WaitForBuffersAllocatedCompleter::Sync& completer) {
TRACE_DURATION("gfx", "BufferCollection::WaitForBuffersAllocated", "this", this,
"logical_buffer_collection", &logical_buffer_collection());
table_set_.MitigateChurn();
if (is_done_) {
FailSync(FROM_HERE, completer, ZX_ERR_BAD_STATE,
"BufferCollectionToken::WaitForBuffersAllocated() when already is_done_");
return;
}
trace_async_id_t current_event_id = TRACE_NONCE();
TRACE_ASYNC_BEGIN("gfx", "BufferCollection::WaitForBuffersAllocated async", current_event_id,
"this", this, "logical_buffer_collection", &logical_buffer_collection());
pending_wait_for_buffers_allocated_.emplace_back(
std::make_pair(current_event_id, completer.ToAsync()));
// The allocation is a one-shot (once true, remains true) and may already be
// done, in which case this immediately completes txn.
MaybeCompleteWaitForBuffersAllocated();
}
void BufferCollection::CheckBuffersAllocated(CheckBuffersAllocatedRequestView request,
CheckBuffersAllocatedCompleter::Sync& completer) {
table_set_.MitigateChurn();
if (is_done_) {
FailSync(FROM_HERE, completer, ZX_ERR_BAD_STATE,
"BufferCollectionToken::CheckBuffersAllocated() when "
"already is_done_");
// We're failing async - no need to try to fail sync.
return;
}
if (!logical_allocation_result_) {
completer.Reply(ZX_ERR_UNAVAILABLE);
return;
}
// Buffer collection has either been allocated or failed.
completer.Reply(logical_allocation_result_->status);
}
void BufferCollection::GetAuxBuffers(GetAuxBuffersRequestView request,
GetAuxBuffersCompleter::Sync& completer) {
TRACE_DURATION("gfx", "BufferCollection::GetAuxBuffers", "this", this,
"logical_buffer_collection", &logical_buffer_collection());
table_set_.MitigateChurn();
if (!logical_allocation_result_) {
FailSync(FROM_HERE, completer, ZX_ERR_BAD_STATE,
"GetAuxBuffers() called before allocation complete.");
return;
}
if (logical_allocation_result_->status != ZX_OK) {
FailSync(FROM_HERE, completer, ZX_ERR_BAD_STATE,
"GetAuxBuffers() called after allocation failure.");
// We're failing async - no need to fail sync.
return;
}
ZX_DEBUG_ASSERT(logical_allocation_result_->buffer_collection_info);
auto v1_result =
CloneAuxBuffersResultForSendingV1(*logical_allocation_result_->buffer_collection_info);
if (!v1_result.is_ok()) {
// Close to avoid assert.
FailSync(FROM_HERE, completer, ZX_ERR_INTERNAL, "CloneAuxBuffersResultForSendingV1() failed.");
return;
}
auto v1 = v1_result.take_value();
completer.Reply(logical_allocation_result_->status, std::move(v1));
}
void BufferCollection::AttachToken(AttachTokenRequestView request,
AttachTokenCompleter::Sync& completer) {
TRACE_DURATION("gfx", "BufferCollection::AttachToken", "this", this, "logical_buffer_collection",
&logical_buffer_collection());
table_set_.MitigateChurn();
if (is_done_) {
// This is Close() followed by AttachToken(), which is not permitted and causes the
// BufferCollection to fail.
FailSync(FROM_HERE, completer, ZX_ERR_BAD_STATE,
"BufferCollection::AttachToken() attempted when is_done_");
return;
}
if (request->rights_attenuation_mask == 0) {
FailSync(FROM_HERE, completer, ZX_ERR_INVALID_ARGS,
"rights_attenuation_mask of 0 is forbidden");
return;
}
NodeProperties* new_node_properties = node_properties().NewChild(&logical_buffer_collection());
// These defaults can be overriden by Allocator.SetDebugClientInfo() called before
// BindSharedCollection().
if (!new_node_properties->client_debug_info().name.empty()) {
// This can be overriden via Allocator::SetDebugClientInfo(), but in case that's not called,
// this default may help clarify where the new token / BufferCollection channel came from.
new_node_properties->client_debug_info().name =
new_node_properties->client_debug_info().name + " then AttachToken()";
} else {
new_node_properties->client_debug_info().name = "from AttachToken()";
}
if (request->rights_attenuation_mask != ZX_RIGHT_SAME_RIGHTS) {
new_node_properties->rights_attenuation_mask() &= request->rights_attenuation_mask;
}
// All AttachToken() tokesn are ErrorPropagationMode::kDoNotPropagate from the start.
new_node_properties->error_propagation_mode() = ErrorPropagationMode::kDoNotPropagate;
logical_buffer_collection().CreateBufferCollectionToken(
shared_logical_buffer_collection(), new_node_properties, std::move(request->token_request));
}
void BufferCollection::AttachLifetimeTracking(AttachLifetimeTrackingRequestView request,
AttachLifetimeTrackingCompleter::Sync& completer) {
TRACE_DURATION("gfx", "BufferCollection::AttachLifetimeTracking", "this", this,
"logical_buffer_collection", &logical_buffer_collection());
table_set_.MitigateChurn();
if (is_done_) {
// This is Close() followed by AttachLifetimeTracking() which is not permitted and causes the
// BufferCollection to fail.
FailSync(FROM_HERE, completer, ZX_ERR_BAD_STATE,
"BufferCollection::AttachLifetimeTracking() attempted when is_done_");
return;
}
pending_lifetime_tracking_.emplace_back(
PendingLifetimeTracking{.server_end = std::move(request->server_end),
.buffers_remaining = request->buffers_remaining});
MaybeFlushPendingLifetimeTracking();
}
void BufferCollection::CloseSingleBuffer(CloseSingleBufferRequestView request,
CloseSingleBufferCompleter::Sync& completer) {
table_set_.MitigateChurn();
if (is_done_) {
FailSync(FROM_HERE, completer, ZX_ERR_BAD_STATE,
"BufferCollectionToken::CloseSingleBuffer() when already is_done_");
return;
}
// FailAsync() instead of returning a failure, mainly because FailAsync()
// prints a message that's more obvious than the generic _dispatch() failure
// would.
FailSync(FROM_HERE, completer, ZX_ERR_NOT_SUPPORTED, "CloseSingleBuffer() not yet implemented");
}
void BufferCollection::AllocateSingleBuffer(AllocateSingleBufferRequestView request,
AllocateSingleBufferCompleter::Sync& completer) {
table_set_.MitigateChurn();
if (is_done_) {
FailSync(FROM_HERE, completer, ZX_ERR_BAD_STATE,
"BufferCollectionToken::AllocateSingleBuffer() when already "
"is_done_");
return;
}
FailSync(FROM_HERE, completer, ZX_ERR_NOT_SUPPORTED,
"AllocateSingleBuffer() not yet implemented");
}
void BufferCollection::WaitForSingleBufferAllocated(
WaitForSingleBufferAllocatedRequestView request,
WaitForSingleBufferAllocatedCompleter::Sync& completer) {
table_set_.MitigateChurn();
if (is_done_) {
FailSync(FROM_HERE, completer, ZX_ERR_BAD_STATE,
"BufferCollectionToken::WaitForSingleBufferAllocated() when "
"already is_done_");
return;
}
FailSync(FROM_HERE, completer, ZX_ERR_NOT_SUPPORTED,
"WaitForSingleBufferAllocated() not yet implemented");
}
void BufferCollection::CheckSingleBufferAllocated(
CheckSingleBufferAllocatedRequestView request,
CheckSingleBufferAllocatedCompleter::Sync& completer) {
table_set_.MitigateChurn();
if (is_done_) {
FailSync(FROM_HERE, completer, ZX_ERR_BAD_STATE,
"BufferCollectionToken::CheckSingleBufferAllocated() when "
"already is_done_");
return;
}
FailSync(FROM_HERE, completer, ZX_ERR_NOT_SUPPORTED,
"CheckSingleBufferAllocated() not yet implemented");
}
void BufferCollection::Close(CloseRequestView request, CloseCompleter::Sync& completer) {
table_set_.MitigateChurn();
if (is_done_) {
FailSync(FROM_HERE, completer, ZX_ERR_BAD_STATE,
"BufferCollection::Close() when already closed.");
return;
}
// We still want to enforce that the client doesn't send any other messages
// between Close() and closing the channel, so we just set is_done_ here and
// do a FailAsync() if is_done_ is seen to be set while handling any other
// message.
is_done_ = true;
}
void BufferCollection::SetName(SetNameRequestView request, SetNameCompleter::Sync& completer) {
table_set_.MitigateChurn();
logical_buffer_collection().SetName(request->priority,
std::string(request->name.begin(), request->name.end()));
}
void BufferCollection::SetDebugClientInfo(SetDebugClientInfoRequestView request,
SetDebugClientInfoCompleter::Sync& completer) {
table_set_.MitigateChurn();
SetDebugClientInfoInternal(std::string(request->name.begin(), request->name.end()), request->id);
}
void BufferCollection::SetDebugClientInfoInternal(std::string name, uint64_t id) {
node_properties().client_debug_info().name = std::move(name);
node_properties().client_debug_info().id = id;
debug_id_property_ =
inspect_node_.CreateUint("debug_id", node_properties().client_debug_info().id);
debug_name_property_ =
inspect_node_.CreateString("debug_name", node_properties().client_debug_info().name);
}
void BufferCollection::FailAsync(Location location, zx_status_t status, const char* format, ...) {
va_list args;
va_start(args, format);
logical_buffer_collection().VLogClientError(location, &node_properties(), format, args);
va_end(args);
// Idempotent, so only close once.
if (!server_binding_)
return;
async_failure_result_ = status;
server_binding_->Close(status);
server_binding_ = {};
}
template <typename Completer>
void BufferCollection::FailSync(Location location, Completer& completer, zx_status_t status,
const char* format, ...) {
va_list args;
va_start(args, format);
logical_buffer_collection().VLogClientError(location, &node_properties(), format, args);
va_end(args);
completer.Close(status);
async_failure_result_ = status;
}
fpromise::result<fuchsia_sysmem2::wire::BufferCollectionInfo>
BufferCollection::CloneResultForSendingV2(
const fuchsia_sysmem2::wire::BufferCollectionInfo& buffer_collection_info) {
auto clone_result =
sysmem::V2CloneBufferCollectionInfo(table_set_.allocator(), buffer_collection_info,
GetClientVmoRights(), GetClientAuxVmoRights());
if (!clone_result.is_ok()) {
FailAsync(FROM_HERE, clone_result.error(),
"CloneResultForSendingV1() V2CloneBufferCollectionInfo() failed - status: %d",
clone_result.error());
return fpromise::error();
}
auto v2_b = clone_result.take_value();
ZX_DEBUG_ASSERT(has_constraints());
if (!constraints().has_usage() || !IsAnyUsage(constraints().usage())) {
// No VMO handles should be sent to the client in this case.
if (v2_b.has_buffers()) {
for (auto& vmo_buffer : v2_b.buffers()) {
if (vmo_buffer.has_vmo()) {
vmo_buffer.vmo().reset();
}
if (vmo_buffer.has_aux_vmo()) {
vmo_buffer.aux_vmo().reset();
}
}
}
}
return fpromise::ok(std::move(v2_b));
}
fpromise::result<fuchsia_sysmem::wire::BufferCollectionInfo2>
BufferCollection::CloneResultForSendingV1(
const fuchsia_sysmem2::wire::BufferCollectionInfo& buffer_collection_info) {
auto v2_result = CloneResultForSendingV2(buffer_collection_info);
if (!v2_result.is_ok()) {
// FailAsync() already called.
return fpromise::error();
}
auto v1_result = sysmem::V1MoveFromV2BufferCollectionInfo(v2_result.take_value());
if (!v1_result.is_ok()) {
FailAsync(FROM_HERE, ZX_ERR_INVALID_ARGS,
"CloneResultForSendingV1() V1MoveFromV2BufferCollectionInfo() failed");
return fpromise::error();
}
return v1_result;
}
fpromise::result<fuchsia_sysmem::wire::BufferCollectionInfo2>
BufferCollection::CloneAuxBuffersResultForSendingV1(
const fuchsia_sysmem2::wire::BufferCollectionInfo& buffer_collection_info) {
auto v2_result = CloneResultForSendingV2(buffer_collection_info);
if (!v2_result.is_ok()) {
// FailAsync() already called.
return fpromise::error();
}
auto v1_result = sysmem::V1AuxBuffersMoveFromV2BufferCollectionInfo(v2_result.take_value());
if (!v1_result.is_ok()) {
FailAsync(FROM_HERE, ZX_ERR_INVALID_ARGS,
"CloneResultForSendingV1() V1MoveFromV2BufferCollectionInfo() failed");
return fpromise::error();
}
return fpromise::ok(v1_result.take_value());
}
void BufferCollection::OnBuffersAllocated(const AllocationResult& allocation_result) {
ZX_DEBUG_ASSERT(!logical_allocation_result_);
ZX_DEBUG_ASSERT((allocation_result.status == ZX_OK) ==
!!allocation_result.buffer_collection_info);
node_properties().SetBuffersLogicallyAllocated();
logical_allocation_result_.emplace(allocation_result);
// Any that are pending are completed by this call or something called
// FailAsync(). It's fine for this method to ignore the fact that
// FailAsync() may have already been called. That's essentially the main
// reason we have FailAsync() instead of Fail().
MaybeCompleteWaitForBuffersAllocated();
// If there are any, they'll be flushed to LogicalBufferCollection now.
MaybeFlushPendingLifetimeTracking();
}
bool BufferCollection::has_constraints() { return node_properties().has_constraints(); }
const fuchsia_sysmem2::wire::BufferCollectionConstraints& BufferCollection::constraints() {
ZX_DEBUG_ASSERT(has_constraints());
return *node_properties().buffer_collection_constraints();
}
fuchsia_sysmem2::wire::BufferCollectionConstraints BufferCollection::CloneConstraints() {
ZX_DEBUG_ASSERT(has_constraints());
return sysmem::V2CloneBufferCollectionConstraints(table_set_.allocator(), constraints());
}
bool BufferCollection::is_done() const { return is_done_; }
BufferCollection::BufferCollection(
fbl::RefPtr<LogicalBufferCollection> logical_buffer_collection_param,
const BufferCollectionToken& token)
: Node(std::move(logical_buffer_collection_param), &token.node_properties()),
table_set_(logical_buffer_collection().table_set()) {
TRACE_DURATION("gfx", "BufferCollection::BufferCollection", "this", this,
"logical_buffer_collection", &this->logical_buffer_collection());
ZX_DEBUG_ASSERT(shared_logical_buffer_collection());
inspect_node_ =
this->logical_buffer_collection().inspect_node().CreateChild(CreateUniqueName("collection-"));
}
// This method is only meant to be called from GetClientVmoRights().
uint32_t BufferCollection::GetUsageBasedRightsAttenuation() {
// This method won't be called for participants without constraints.
ZX_DEBUG_ASSERT(has_constraints());
// We assume that read and map are both needed by all participants with any "usage". Only
// ZX_RIGHT_WRITE is controlled by usage.
// It's not this method's job to attenuate down to kMaxClientVmoRights, so
// let's not pretend like it is.
uint32_t result = std::numeric_limits<uint32_t>::max();
if (!constraints().has_usage() || !IsWriteUsage(constraints().usage())) {
result &= ~ZX_RIGHT_WRITE;
}
return result;
}
uint32_t BufferCollection::GetClientVmoRights() {
return
// max possible rights for a client to have
kMaxClientVmoRights &
// attenuate write if client doesn't need write
GetUsageBasedRightsAttenuation() &
// attenuate according to BufferCollectionToken.Duplicate() rights
// parameter so that initiator and participant can distribute the token
// and remove any unnecessary/unintended rights along the way.
node_properties().rights_attenuation_mask();
}
uint32_t BufferCollection::GetClientAuxVmoRights() {
// At least for now.
return GetClientVmoRights();
}
void BufferCollection::MaybeCompleteWaitForBuffersAllocated() {
if (!logical_allocation_result_) {
// Everything is ok so far, but allocation isn't done yet.
return;
}
while (!pending_wait_for_buffers_allocated_.empty()) {
auto [async_id, txn] = std::move(pending_wait_for_buffers_allocated_.front());
pending_wait_for_buffers_allocated_.pop_front();
fuchsia_sysmem::wire::BufferCollectionInfo2 v1;
if (logical_allocation_result_->status == ZX_OK) {
ZX_DEBUG_ASSERT(logical_allocation_result_->buffer_collection_info);
auto v1_result = CloneResultForSendingV1(*logical_allocation_result_->buffer_collection_info);
if (!v1_result.is_ok()) {
// FailAsync() already called.
return;
}
v1 = v1_result.take_value();
}
TRACE_ASYNC_END("gfx", "BufferCollection::WaitForBuffersAllocated async", async_id, "this",
this, "logical_buffer_collection", &logical_buffer_collection());
txn.Reply(logical_allocation_result_->status, std::move(v1));
fidl::Status reply_status = txn.result_of_reply();
if (!reply_status.ok()) {
FailAsync(FROM_HERE, reply_status.status(),
"fuchsia_sysmem_BufferCollectionWaitForBuffersAllocated_"
"reply failed - status: %s",
reply_status.FormatDescription().c_str());
return;
}
// ~txn
}
}
void BufferCollection::MaybeFlushPendingLifetimeTracking() {
if (!node_properties().buffers_logically_allocated()) {
return;
}
if (logical_allocation_result_->status != ZX_OK) {
// We close these immediately if logical allocation failed, regardless of
// the number of buffers potentially allocated in the overall
// LogicalBufferCollection. This is for behavior consistency between
// AttachToken() sub-trees and the root_.
pending_lifetime_tracking_.clear();
return;
}
for (auto& pending : pending_lifetime_tracking_) {
logical_buffer_collection().AttachLifetimeTracking(std::move(pending.server_end),
pending.buffers_remaining);
}
pending_lifetime_tracking_.clear();
}
bool BufferCollection::ReadyForAllocation() { return has_constraints(); }
void BufferCollection::Fail(zx_status_t epitaph) { CloseChannel(epitaph); }
BufferCollectionToken* BufferCollection::buffer_collection_token() { return nullptr; }
const BufferCollectionToken* BufferCollection::buffer_collection_token() const { return nullptr; }
BufferCollection* BufferCollection::buffer_collection() { return this; }
const BufferCollection* BufferCollection::buffer_collection() const { return this; }
OrphanedNode* BufferCollection::orphaned_node() { return nullptr; }
const OrphanedNode* BufferCollection::orphaned_node() const { return nullptr; }
bool BufferCollection::is_connected() const { return true; }
} // namespace sysmem_driver