blob: 7baf54408d0d59646eb8a5623be1d2c5ba536bb7 [file] [log] [blame]
// Copyright 2018 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/media/codec_impl/codec_admission_control.h>
#include <stdint.h>
#include <zircon/assert.h>
#include <mutex>
void CodecAdmissionControl::TryAddCodec(bool multi_instance,
fit::function<void(std::unique_ptr<CodecAdmission>)>
continue_after_previously_started_channel_closes_done) {
PostAfterPreviouslyStartedClosesDone(
[this, multi_instance,
callback = std::move(continue_after_previously_started_channel_closes_done)] {
callback(TryAddCodecInternal(multi_instance));
});
}
void CodecAdmissionControl::PostAfterPreviouslyStartedClosesDone(fit::closure to_run) {
std::lock_guard<std::mutex> lock(lock_);
while (true) {
zx_port_packet_t packet;
if (close_port_.wait(zx::deadline_after(zx::sec(0)), &packet) != ZX_OK) {
break;
}
auto it = codecs_to_check_for_close_.find(packet.key);
if (it == codecs_to_check_for_close_.end())
continue;
it->second->SetCodecIsClosingLocked();
codecs_to_check_for_close_.erase(it);
}
CleanOutPreviousClosures();
auto deferred_action = fit::defer_callback([this, to_run = std::move(to_run)]() mutable {
zx_status_t result = async::PostTask(shared_fidl_dispatcher_, std::move(to_run));
ZX_ASSERT(result == ZX_OK);
});
auto new_callback = std::make_shared<fit::deferred_callback>(std::move(deferred_action));
// Every existing close should hold a reference to this deferred callback so it'll run when
// they've all completed.
for (auto& handle : previous_closes_) {
auto locked = handle.lock();
if (locked) {
locked->AddClosureToReference(new_callback);
}
}
// If there are no valid previous_closes_s, then deferred_callback may be run at this point.
}
std::unique_ptr<CodecAdmission> CodecAdmissionControl::TryAddCodecInternal(bool multi_instance) {
std::lock_guard<std::mutex> lock(lock_);
if (multi_instance) {
if (single_instance_codec_count_ > 0) {
printf(
"CodecAdmissionControl::AddCodec(): we've already got a "
"single-instance codec\n");
return nullptr;
}
multi_instance_codec_count_++;
} else {
if (multi_instance_codec_count_ > 0 || single_instance_codec_count_ > 0) {
printf(
"CodecAdmissionControl::AddCodec(): we've already got an existing "
"codec. multi_instance_codec_count: %u single_instance_codec_count: "
"%u\n",
multi_instance_codec_count_, single_instance_codec_count_);
return nullptr;
}
single_instance_codec_count_++;
}
// private constructor so have to explicitly new, since friending
// std::make_unique<> would allow any class to create one.
return std::unique_ptr<CodecAdmission>(new CodecAdmission(this, multi_instance));
}
CodecAdmissionControl::CodecAdmissionControl(async_dispatcher_t* shared_fidl_dispatcher)
: shared_fidl_dispatcher_(shared_fidl_dispatcher),
single_instance_codec_count_(0),
multi_instance_codec_count_(0) {
zx_status_t status = zx::port::create(0, &close_port_);
ZX_ASSERT(status == ZX_OK);
ZX_DEBUG_ASSERT(shared_fidl_dispatcher_);
}
void CodecAdmissionControl::RemoveCodec(bool multi_instance, uint64_t port_key) {
std::lock_guard<std::mutex> lock(lock_);
// Else bug in caller.
if (multi_instance) {
ZX_DEBUG_ASSERT(multi_instance_codec_count_ > 0);
multi_instance_codec_count_--;
} else {
ZX_DEBUG_ASSERT(single_instance_codec_count_ == 1);
single_instance_codec_count_--;
}
if (port_key != 0) {
codecs_to_check_for_close_.erase(port_key);
}
}
std::shared_ptr<CodecAdmissionControl::PreviousCloseHandle>
CodecAdmissionControl::OnCodecIsClosing() {
CleanOutPreviousClosures();
auto new_close_handle = std::make_shared<PreviousCloseHandle>();
previous_closes_.push_back(new_close_handle);
return new_close_handle;
}
void CodecAdmissionControl::CleanOutPreviousClosures() {
previous_closes_.erase(std::remove_if(previous_closes_.begin(), previous_closes_.end(),
[](auto& ref) { return ref.expired(); }),
previous_closes_.end());
}
CodecAdmission::~CodecAdmission() {
codec_admission_control_->RemoveCodec(multi_instance_, port_key_);
}
CodecAdmission::CodecAdmission(CodecAdmissionControl* codec_admission_control, bool multi_instance)
: codec_admission_control_(codec_admission_control), multi_instance_(multi_instance) {
ZX_DEBUG_ASSERT(codec_admission_control_);
}
void CodecAdmission::SetChannelToWaitOn(const zx::channel& channel) {
ZX_DEBUG_ASSERT(channel);
ZX_DEBUG_ASSERT(!port_key_);
std::lock_guard<std::mutex> lock(codec_admission_control_->lock_);
// It should be impossible for next_port_key_ to wrap around within a
// reasonable timespan (see logic for koids).
ZX_DEBUG_ASSERT(codec_admission_control_->next_port_key_ != 0);
port_key_ = codec_admission_control_->next_port_key_++;
codec_admission_control_->codecs_to_check_for_close_[port_key_] = this;
zx_status_t status = channel.wait_async(codec_admission_control_->close_port_, port_key_,
ZX_CHANNEL_PEER_CLOSED, 0);
ZX_ASSERT(status == ZX_OK);
}