blob: 32f9e12ac55678a9d82751fc2fbeeffa7f5fa655 [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.
#ifndef SRC_MEDIA_LIB_CODEC_IMPL_INCLUDE_LIB_MEDIA_CODEC_IMPL_CODEC_ADMISSION_CONTROL_H_
#define SRC_MEDIA_LIB_CODEC_IMPL_INCLUDE_LIB_MEDIA_CODEC_IMPL_CODEC_ADMISSION_CONTROL_H_
#include <lib/async/cpp/task.h>
#include <lib/fit/defer.h>
#include <lib/fit/function.h>
#include <lib/zx/channel.h>
#include <lib/zx/port.h>
#include <zircon/compiler.h>
#include <mutex>
#include <unordered_map>
#include <vector>
#include <fbl/macros.h>
// Controls how many Codec instances are concurrently served by this process.
//
// There's a limit of 1 for single-instance decoders, but arbitrarily-many
// multi-instance decoders can be used if there's no single-instance decoder.
class CodecAdmission;
class CodecAdmissionControl {
public:
// Create with a dispatcher to post async calls to the shared fidl thread.
explicit CodecAdmissionControl(async_dispatcher_t* shared_fidl_dispatcher);
// Get a move-only CodecAdmission as a move-only ticket that allows creation
// of a CodecImpl.
//
// TODO(dustingreen): std::optional<> instead when C++17.
void TryAddCodec(bool multi_instance, fit::function<void(std::unique_ptr<CodecAdmission>)>
continue_after_previously_started_channel_closes_done);
// Anything posted here will run after any previously-posted items here or via
// TryAddCodec().
//
// Run the posted closure after all previously-started closes are done being
// processed, and after all previously-queued closures via this method are
// done.
void PostAfterPreviouslyStartedClosesDone(fit::closure to_run);
private:
friend class CodecAdmission;
// This keeps a set of deferred callbacks alive until all references to it are released.
class PreviousCloseHandle {
public:
void AddClosureToReference(std::shared_ptr<fit::deferred_callback> input_defer) {
defer_list_.push_back(input_defer);
}
private:
std::vector<std::shared_ptr<fit::deferred_callback>> defer_list_;
};
// This is called after all previous closes are done.
std::unique_ptr<CodecAdmission> TryAddCodecInternal(bool multi_instance);
std::shared_ptr<PreviousCloseHandle> OnCodecIsClosing() __TA_REQUIRES(lock_);
void RemoveCodec(bool multi_instance, uint64_t key_value);
void CleanOutPreviousClosures() __TA_REQUIRES(lock_);
void CheckForClosedChannels() __TA_REQUIRES(lock_);
async_dispatcher_t* shared_fidl_dispatcher_ = nullptr;
std::mutex lock_;
uint32_t single_instance_codec_count_ __TA_GUARDED(lock_) = 0;
uint32_t multi_instance_codec_count_ __TA_GUARDED(lock_) = 0;
std::vector<std::weak_ptr<PreviousCloseHandle>> previous_closes_ __TA_GUARDED(lock_);
// We need to generate unique keys instead of using the CodecAdmission pointer, because if the
// CodecAdmission is closed not in response to a PEER_CLOSED then we may leave a port message
// queued up once the channel actually closes. Then a new CodecAdmission could be allocated at the
// same address and we don't want the old port message interpreted as going to the new
// CodecAdmission.
uint64_t next_port_key_ __TA_GUARDED(lock_) = 1;
std::unordered_map</*port_key*/ uint64_t, CodecAdmission*> codecs_to_check_for_close_
__TA_GUARDED(lock_);
zx::port close_port_ __TA_GUARDED(lock_);
DISALLOW_COPY_ASSIGN_AND_MOVE(CodecAdmissionControl);
};
class CodecAdmission {
public:
CodecAdmission(CodecAdmission&& from) = delete;
CodecAdmission& operator=(CodecAdmission&& from) = delete;
CodecAdmission(const CodecAdmission& from) = delete;
CodecAdmission& operator=(const CodecAdmission& from) = delete;
// Sets the channel CodecAdmissionControl should check for ZX_CHANNEL_PEER_CLOSED - if that's
// received, the CodecAdmission is assumed to be closing soon.
void SetChannelToWaitOn(const zx::channel& channel);
// Tell the codec admission control that this codec will be closing soon. When the class is
// destroyed |close_handle_| will be destructed and that allows pending callbacks to run.
void SetCodecIsClosing() {
std::lock_guard<std::mutex> lock(codec_admission_control_->lock_);
SetCodecIsClosingLocked();
}
~CodecAdmission();
private:
friend class CodecAdmissionControl;
CodecAdmission(CodecAdmissionControl* codec_admission_control, bool multi_instance);
void SetCodecIsClosingLocked() __TA_NO_THREAD_SAFETY_ANALYSIS {
if (!close_handle_)
close_handle_ = codec_admission_control_->OnCodecIsClosing();
}
CodecAdmissionControl* codec_admission_control_ = nullptr;
bool multi_instance_;
std::shared_ptr<CodecAdmissionControl::PreviousCloseHandle> close_handle_;
uint64_t port_key_ = 0;
};
#endif // SRC_MEDIA_LIB_CODEC_IMPL_INCLUDE_LIB_MEDIA_CODEC_IMPL_CODEC_ADMISSION_CONTROL_H_