File: reclaimable_codec.cc

package info (click to toggle)
chromium 138.0.7204.183-1
  • links: PTS, VCS
  • area: main
  • in suites: trixie
  • size: 6,071,908 kB
  • sloc: cpp: 34,937,088; ansic: 7,176,967; javascript: 4,110,704; python: 1,419,953; asm: 946,768; xml: 739,971; pascal: 187,324; sh: 89,623; perl: 88,663; objc: 79,944; sql: 50,304; cs: 41,786; fortran: 24,137; makefile: 21,806; php: 13,980; tcl: 13,166; yacc: 8,925; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (221 lines) | stat: -rw-r--r-- 7,645 bytes parent folder | download | duplicates (5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
// Copyright 2021 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/reclaimable_codec.h"

#include "base/location.h"
#include "base/time/default_tick_clock.h"
#include "third_party/blink/renderer/core/dom/dom_exception.h"
#include "third_party/blink/renderer/core/dom/quota_exceeded_error.h"
#include "third_party/blink/renderer/core/execution_context/execution_context.h"
#include "third_party/blink/renderer/modules/webcodecs/codec_pressure_manager.h"
#include "third_party/blink/renderer/modules/webcodecs/codec_pressure_manager_provider.h"
#include "third_party/blink/renderer/platform/heap/persistent.h"
#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
#include "third_party/blink/renderer/platform/wtf/functional.h"

namespace blink {

constexpr base::TimeDelta ReclaimableCodec::kInactivityReclamationThreshold;

ReclaimableCodec::ReclaimableCodec(CodecType type, ExecutionContext* context)
    : ExecutionContextLifecycleObserver(context),
      codec_type_(type),
      tick_clock_(base::DefaultTickClock::GetInstance()),
      inactivity_threshold_(kInactivityReclamationThreshold),
      last_activity_(tick_clock_->NowTicks()),
      activity_timer_(context->GetTaskRunner(TaskType::kInternalMedia),
                      this,
                      &ReclaimableCodec::OnActivityTimerFired) {
  DCHECK(context);
  // Do this last, it will immediately re-enter via OnLifecycleStateChanged().
  observer_handle_ = context->GetScheduler()->AddLifecycleObserver(
      FrameOrWorkerScheduler::ObserverType::kWorkerScheduler,
      WTF::BindRepeating(&ReclaimableCodec::OnLifecycleStateChanged,
                         WrapWeakPersistent(this)));
}

void ReclaimableCodec::Trace(Visitor* visitor) const {
  visitor->Trace(activity_timer_);
  ExecutionContextLifecycleObserver::Trace(visitor);
}

void ReclaimableCodec::ApplyCodecPressure() {
  if (is_applying_pressure_)
    return;

  is_applying_pressure_ = true;

  if (auto* pressure_manager = PressureManager())
    pressure_manager->AddCodec(this);
}

void ReclaimableCodec::ReleaseCodecPressure() {
  if (!is_applying_pressure_) {
    DCHECK(!activity_timer_.IsActive());
    return;
  }

  if (auto* pressure_manager = PressureManager()) {
    // If we fail to get |pressure_manager| here (say, because the
    // ExecutionContext is being destroyed), this is harmless. The
    // CodecPressureManager maintains its own local pressure count, and it will
    // properly decrement it from the global pressure count upon the manager's
    // disposal. The CodecPressureManager's WeakMember reference to |this| will
    // be cleared by the GC when |this| is disposed. The manager might still
    // call into SetGlobalPressureExceededFlag() before |this| is disposed, but
    // we will simply noop those calls.
    pressure_manager->RemoveCodec(this);
  }

  // We might still exceed global codec pressure at this point, but this codec
  // isn't contributing to it, and needs to reset its own flag.
  SetGlobalPressureExceededFlag(false);

  is_applying_pressure_ = false;
}

void ReclaimableCodec::Dispose() {
  if (!is_applying_pressure_)
    return;

  if (auto* pressure_manager = PressureManager())
    pressure_manager->OnCodecDisposed(this);
}

void ReclaimableCodec::SetGlobalPressureExceededFlag(
    bool global_pressure_exceeded) {
  if (!is_applying_pressure_) {
    // We should only hit this call because we failed to get the
    // PressureManager() in ReleaseCodecPressure(). See the note above.
    DCHECK(!PressureManager());
    return;
  }

  if (global_pressure_exceeded_ == global_pressure_exceeded)
    return;

  global_pressure_exceeded_ = global_pressure_exceeded;

  OnReclamationPreconditionsUpdated();
}

void ReclaimableCodec::OnLifecycleStateChanged(
    scheduler::SchedulingLifecycleState lifecycle_state) {
  DVLOG(5) << __func__
           << " lifecycle_state=" << static_cast<int>(lifecycle_state);
  bool is_backgrounded =
      lifecycle_state != scheduler::SchedulingLifecycleState::kNotThrottled;

  // Several life cycle states map to "backgrounded", but we only want to
  // observe the transition.
  if (is_backgrounded == is_backgrounded_)
    return;

  is_backgrounded_ = is_backgrounded;

  // Make sure we wait the full inactivity timer period before reclaiming a
  // newly backgrounded codec.
  if (is_backgrounded_)
    MarkCodecActive();

  OnReclamationPreconditionsUpdated();
}

void ReclaimableCodec::SimulateLifecycleStateForTesting(
    scheduler::SchedulingLifecycleState state) {
  OnLifecycleStateChanged(state);
}

void ReclaimableCodec::SimulateCodecReclaimedForTesting() {
  auto* message = "Codec reclaimed for testing.";
  if (RuntimeEnabledFeatures::QuotaExceededErrorUpdateEnabled()) {
    OnCodecReclaimed(MakeGarbageCollected<QuotaExceededError>(message));
  } else {
    OnCodecReclaimed(MakeGarbageCollected<DOMException>(
        DOMExceptionCode::kQuotaExceededError, message));
  }
}

void ReclaimableCodec::SimulateActivityTimerFiredForTesting() {
  OnActivityTimerFired(nullptr);
}

void ReclaimableCodec::MarkCodecActive() {
  last_activity_ = tick_clock_->NowTicks();
  last_tick_was_inactive_ = false;
}

void ReclaimableCodec::OnReclamationPreconditionsUpdated() {
  if (AreReclamationPreconditionsMet())
    StartIdleReclamationTimer();
  else
    StopIdleReclamationTimer();
}

bool ReclaimableCodec::AreReclamationPreconditionsMet() {
  // If |global_pressure_exceeded_| is true, so should |is_applying_pressure_|.
  DCHECK_EQ(global_pressure_exceeded_,
            global_pressure_exceeded_ && is_applying_pressure_);

  return is_applying_pressure_ && global_pressure_exceeded_ && is_backgrounded_;
}

void ReclaimableCodec::StartIdleReclamationTimer() {
  DCHECK(AreReclamationPreconditionsMet());

  if (activity_timer_.IsActive())
    return;

  DVLOG(5) << __func__ << " Starting timer.";
  activity_timer_.StartRepeating(inactivity_threshold_ / 2, FROM_HERE);
}

void ReclaimableCodec::StopIdleReclamationTimer() {
  DCHECK(!AreReclamationPreconditionsMet());

  activity_timer_.Stop();
}

void ReclaimableCodec::OnActivityTimerFired(TimerBase*) {
  DCHECK(AreReclamationPreconditionsMet());

  auto time_inactive = tick_clock_->NowTicks() - last_activity_;
  bool is_inactive = time_inactive >= inactivity_threshold_;

  // Do not immediately reclaim. Make sure the codec is inactive for 2 ticks.
  // Otherwise, tabs that were suspended could see their codecs reclaimed
  // immediately after being resumed.
  if (is_inactive && last_tick_was_inactive_) {
    activity_timer_.Stop();
    auto* message = "Codec reclaimed due to inactivity.";
    if (RuntimeEnabledFeatures::QuotaExceededErrorUpdateEnabled()) {
      OnCodecReclaimed(MakeGarbageCollected<QuotaExceededError>(message));
    } else {
      OnCodecReclaimed(MakeGarbageCollected<DOMException>(
          DOMExceptionCode::kQuotaExceededError, message));
    }
  }

  last_tick_was_inactive_ = time_inactive >= (inactivity_threshold_ / 2);
}

CodecPressureManager* ReclaimableCodec::PressureManager() {
  auto* execution_context = GetExecutionContext();

  if (!execution_context || execution_context->IsContextDestroyed())
    return nullptr;

  auto& manager_provider =
      CodecPressureManagerProvider::From(*execution_context);

  switch (codec_type_) {
    case CodecType::kDecoder:
      return manager_provider.GetDecoderPressureManager();
    case CodecType::kEncoder:
      return manager_provider.GetEncoderPressureManager();
  }
}

}  // namespace blink