| // Copyright 2022 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "third_party/blink/renderer/modules/webcodecs/codec_pressure_manager.h" |
| |
| #include "base/task/sequenced_task_runner.h" |
| #include "build/build_config.h" |
| #include "third_party/blink/renderer/modules/webcodecs/codec_pressure_gauge.h" |
| #include "third_party/blink/renderer/platform/heap/cross_thread_persistent.h" |
| #include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h" |
| #include "third_party/blink/renderer/platform/wtf/cross_thread_copier_base.h" |
| #include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h" |
| #include "third_party/blink/renderer/platform/wtf/functional.h" |
| |
| namespace blink { |
| |
| CodecPressureManager::CodecPressureManager( |
| ReclaimableCodec::CodecType codec_type, |
| scoped_refptr<base::SequencedTaskRunner> task_runner) |
| : codec_type_(codec_type) { |
| auto pressure_threshold_changed_cb = |
| [](CrossThreadWeakPersistent<CodecPressureManager> self, |
| scoped_refptr<base::SequencedTaskRunner> task_runner, |
| bool global_pressure_exceeded) { |
| // Accessing |self| is not thread safe. Even if it is thread unsafe, |
| // checking for |!self| can definitively tell us if the object has |
| // already been GC'ed, so we can exit early. Otherwise, we always post |
| // to |task_runner|, where it will be safe to use |self|, since it's |
| // in the same sequence that created |self|. |
| if (!self) |
| return; |
| |
| // Always post this change, to guarantee ordering if this callback |
| // is run from different threads. |
| DCHECK(task_runner); |
| PostCrossThreadTask( |
| *task_runner, FROM_HERE, |
| CrossThreadBindOnce( |
| &CodecPressureManager::OnGlobalPressureThresholdChanged, self, |
| global_pressure_exceeded)); |
| }; |
| |
| CodecPressureGauge::RegistrationResult result = |
| GetCodecPressureGauge().RegisterPressureCallback( |
| ConvertToBaseRepeatingCallback(CrossThreadBindRepeating( |
| pressure_threshold_changed_cb, |
| WrapCrossThreadWeakPersistent(this), task_runner))); |
| |
| pressure_callback_id_ = result.first; |
| global_pressure_exceeded_ = result.second; |
| } |
| |
| CodecPressureGauge& CodecPressureManager::GetCodecPressureGauge() { |
| return CodecPressureGauge::GetInstance(codec_type_); |
| } |
| |
| void CodecPressureManager::AddCodec(ReclaimableCodec* codec) { |
| DCHECK(manager_registered_); |
| DCHECK(codec->is_applying_codec_pressure()); |
| |
| DCHECK(!codecs_with_pressure_.Contains(codec)); |
| codecs_with_pressure_.insert(codec); |
| |
| ++local_codec_pressure_; |
| GetCodecPressureGauge().Increment(); |
| |
| codec->SetGlobalPressureExceededFlag(global_pressure_exceeded_); |
| } |
| |
| void CodecPressureManager::RemoveCodec(ReclaimableCodec* codec) { |
| DCHECK(manager_registered_); |
| |
| DCHECK(codecs_with_pressure_.Contains(codec)); |
| codecs_with_pressure_.erase(codec); |
| |
| DCHECK(local_codec_pressure_); |
| --local_codec_pressure_; |
| GetCodecPressureGauge().Decrement(); |
| |
| // |codec| is responsible for clearing its own global pressure exceeded flag. |
| } |
| |
| void CodecPressureManager::OnCodecDisposed(ReclaimableCodec* codec) { |
| DCHECK(codec->is_applying_codec_pressure()); |
| |
| if (!manager_registered_) { |
| // |this|'s pre-finalizer (UnregisterManager()) could have been called |
| // before leftover ReclaimableCodec's pre-finalizers were called. |
| // This shouldn't happen often, but it might if |this| and codecs are |
| // prefinalized in the same GC run. |
| DCHECK_EQ(local_codec_pressure_, 0u); |
| return; |
| } |
| |
| // The GC should have removed |codec| from |codecs_with_pressure_|. |
| DCHECK(!codecs_with_pressure_.Contains(codec)); |
| |
| DCHECK(local_codec_pressure_); |
| --local_codec_pressure_; |
| GetCodecPressureGauge().Decrement(); |
| } |
| |
| void CodecPressureManager::OnGlobalPressureThresholdChanged( |
| bool pressure_threshold_exceeded) { |
| DCHECK_NE(global_pressure_exceeded_, pressure_threshold_exceeded); |
| global_pressure_exceeded_ = pressure_threshold_exceeded; |
| |
| for (auto codec : codecs_with_pressure_) |
| codec->SetGlobalPressureExceededFlag(global_pressure_exceeded_); |
| } |
| |
| void CodecPressureManager::UnregisterManager() { |
| if (!manager_registered_) |
| return; |
| |
| GetCodecPressureGauge().UnregisterPressureCallback(pressure_callback_id_, |
| local_codec_pressure_); |
| codecs_with_pressure_.clear(); |
| |
| local_codec_pressure_ = 0u; |
| |
| manager_registered_ = false; |
| } |
| |
| void CodecPressureManager::Trace(Visitor* visitor) const { |
| visitor->Trace(codecs_with_pressure_); |
| } |
| |
| } // namespace blink |