blob: 5e0a6d2ef222322de6f54d884f658430016d6921 [file] [log] [blame]
// Copyright 2020 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 "src/camera/bin/usb_device/sysmem_allocator.h"
#include <lib/async-loop/default.h>
#include <lib/async/cpp/task.h>
#include <lib/fit/defer.h>
#include <lib/fpromise/bridge.h>
#include <lib/fpromise/scope.h>
#include <lib/syslog/cpp/macros.h>
namespace camera {
namespace {
constexpr uint32_t kNamePriority = 30; // Higher than Scenic but below the maximum.
// Returns a promise that completes after calling |WaitForBuffersAllocated| on the provided
// BufferCollection.
//
// The |collection| is consumed by this operation and will be closed upon both success and failure.
fpromise::promise<BufferCollectionWithLifetime, zx_status_t> WaitForBuffersAllocated(
fuchsia::sysmem::BufferCollectionPtr collection) {
// Move the bridge completer into a shared_ptr so that we can share the completer between the
// FIDL error handler and the WaitForBuffersAllocated callback.
fpromise::bridge<BufferCollectionWithLifetime, zx_status_t> bridge;
auto completer = std::make_shared<fpromise::completer<BufferCollectionWithLifetime, zx_status_t>>(
std::move(bridge.completer));
std::weak_ptr<fpromise::completer<BufferCollectionWithLifetime, zx_status_t>> weak_completer(
completer);
collection.set_error_handler([completer](zx_status_t status) {
// After calling SetConstraints, allocation may fail. This results in WaitForBuffersAllocated
// returning NO_MEMORY followed by channel closure. Because the client may observe these in
// either order, treat channel closure as if it were NO_MEMORY.
FX_CHECK(status != ZX_OK);
completer->complete_error(status == ZX_ERR_PEER_CLOSED ? ZX_ERR_NO_MEMORY : status);
});
zx::eventpair deallocation_complete_client, deallocation_complete_server;
zx::eventpair::create(/*options=*/0, &deallocation_complete_client,
&deallocation_complete_server);
collection->AttachLifetimeTracking(std::move(deallocation_complete_server), 0);
collection->WaitForBuffersAllocated(
[weak_completer, deallocation_complete = std::move(deallocation_complete_client)](
zx_status_t status, fuchsia::sysmem::BufferCollectionInfo_2 buffers) mutable {
auto completer = weak_completer.lock();
if (completer) {
if (status == ZX_OK) {
BufferCollectionWithLifetime collection_lifetime;
collection_lifetime.buffers = std::move(buffers);
collection_lifetime.deallocation_complete = std::move(deallocation_complete);
completer->complete_ok(std::move(collection_lifetime));
} else {
completer->complete_error(status);
}
}
});
return bridge.consumer.promise().inspect(
[collection = std::move(collection)](
const fpromise::result<BufferCollectionWithLifetime, zx_status_t>& result) mutable {
if (collection) {
collection->Close();
collection = nullptr;
}
});
}
} // namespace
SysmemAllocator::SysmemAllocator(fuchsia::sysmem::AllocatorHandle allocator)
: allocator_(allocator.Bind()) {}
fpromise::promise<BufferCollectionWithLifetime, zx_status_t> SysmemAllocator::BindSharedCollection(
fuchsia::sysmem::BufferCollectionTokenHandle token,
fuchsia::sysmem::BufferCollectionConstraints constraints, std::string name) {
TRACE_DURATION("camera", "SysmemAllocator::BindSharedCollection");
// We expect sysmem to have free space, so bind the provided token now.
fuchsia::sysmem::BufferCollectionPtr collection;
allocator_->BindSharedCollection(std::move(token), collection.NewRequest());
collection->SetName(kNamePriority, std::move(name));
collection->SetConstraints(true, constraints);
return WaitForBuffersAllocated(std::move(collection)).wrap_with(scope_);
}
} // namespace camera