1 // Copyright 2024 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/android/pre_freeze_background_memory_trimmer.h"
6
7 #include <sys/mman.h>
8 #include <sys/utsname.h>
9
10 #include <optional>
11 #include <string>
12
13 #include "base/android/build_info.h"
14 #include "base/android/pmf_utils.h"
15 #include "base/cancelable_callback.h"
16 #include "base/check.h"
17 #include "base/command_line.h"
18 #include "base/feature_list.h"
19 #include "base/functional/bind.h"
20 #include "base/logging.h"
21 #include "base/memory/page_size.h"
22 #include "base/metrics/histogram_functions.h"
23 #include "base/strings/strcat.h"
24 #include "base/task/sequenced_task_runner.h"
25 #include "base/task/thread_pool.h"
26 #include "base/task/thread_pool/thread_pool_instance.h"
27 #include "base/time/time.h"
28 #include "base/trace_event/base_tracing.h"
29
30 namespace base::android {
31 namespace {
32
33 // These values are logged to UMA. Entries should not be renumbered and
34 // numeric values should never be reused. Please keep in sync with
35 // "PreFreezeMetricsFailureType" in tools/metrics/histograms/enums.xml.
36 enum class MetricsFailure {
37 kAlreadyRunning,
38 kSizeMismatch,
39 kMaxValue = kSizeMismatch
40 };
41
42 // This constant is chosen arbitrarily, to allow time for the background tasks
43 // to finish running BEFORE collecting metrics.
44 const base::TimeDelta kDelayForMetrics = base::Seconds(2);
45
BytesToMiB(uint64_t v)46 uint64_t BytesToMiB(uint64_t v) {
47 return v / 1024 / 1024;
48 }
49
GetProcessType()50 const char* GetProcessType() {
51 CHECK(base::CommandLine::InitializedForCurrentProcess());
52 const std::string type =
53 base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII("type");
54 const char* process_type = type == "" ? "Browser"
55 : type == "renderer" ? "Renderer"
56 : type == "gpu-process" ? "GPU"
57 : type == "utility" ? "Utility"
58 : "Unknown";
59 return process_type;
60 }
61
GetMetricName(std::string_view name,std::string_view suffix)62 std::string GetMetricName(std::string_view name, std::string_view suffix) {
63 const char* process_type = GetProcessType();
64 return StrCat({"Memory.PreFreeze2.", process_type, ".", name, ".", suffix});
65 }
66
67 class PrivateMemoryFootprintMetric
68 : public PreFreezeBackgroundMemoryTrimmer::PreFreezeMetric {
69 public:
PrivateMemoryFootprintMetric()70 PrivateMemoryFootprintMetric()
71 : PreFreezeBackgroundMemoryTrimmer::PreFreezeMetric(
72 "PrivateMemoryFootprint") {}
Measure() const73 std::optional<uint64_t> Measure() const override {
74 return PmfUtils::GetPrivateMemoryFootprintForCurrentProcess();
75 }
76
77 ~PrivateMemoryFootprintMetric() override = default;
78
79 // Whether the metric has been registered with
80 // |PreFreezeBackgroundMemoryTrimmer| or not, which happens the first time a
81 // task is posted via |PreFreezeBackgroundMemoryTrimmer| or
82 // |OneShotDelayedBackgroundTimer|.
83 static bool did_register_;
84 };
85
86 bool PrivateMemoryFootprintMetric::did_register_ = false;
87
MaybeRecordMetric(std::optional<uint64_t> value_bytes,std::string_view metric_name,std::string_view suffix)88 void MaybeRecordMetric(std::optional<uint64_t> value_bytes,
89 std::string_view metric_name,
90 std::string_view suffix) {
91 // Skip recording the metric if we failed to get the PMF.
92 if (!value_bytes.has_value()) {
93 return;
94 }
95
96 UmaHistogramMemoryMB(GetMetricName(metric_name, suffix),
97 static_cast<int>(BytesToMiB(value_bytes.value())));
98 }
99
Diff(std::optional<uint64_t> before,std::optional<uint64_t> after)100 std::optional<uint64_t> Diff(std::optional<uint64_t> before,
101 std::optional<uint64_t> after) {
102 if (!before.has_value() || !before.has_value()) {
103 return std::nullopt;
104 }
105
106 const uint64_t before_value = before.value();
107 const uint64_t after_value = after.value();
108
109 return after_value < before_value ? before_value - after_value : 0;
110 }
111
IsMadvisePageoutSupported()112 bool IsMadvisePageoutSupported() {
113 static bool supported = []() -> bool {
114 #if defined(MADV_PAGEOUT)
115 // To determine if MADV_PAGEOUT is supported we will try calling it with an
116 // invalid memory area.
117 // madvise(2) first checks the mode first, returning -EINVAL if it's
118 // unknown. Next, it will always return 0 for a zero length VMA before
119 // validating if it's mapped.
120 // So, in this case, we can test for support with any page aligned address
121 // with a zero length.
122 int res =
123 madvise(reinterpret_cast<void*>(base::GetPageSize()), 0, MADV_PAGEOUT);
124 if (res < 0 && errno == -EINVAL)
125 return false;
126 PLOG_IF(ERROR, res < 0) << "Unexpected return from madvise";
127 if (res == 0)
128 return true;
129 #endif
130 return false;
131 }();
132 return supported;
133 }
134
135 } // namespace
136
137 BASE_FEATURE(kOnPreFreezeMemoryTrim,
138 "OnPreFreezeMemoryTrim",
139 FEATURE_ENABLED_BY_DEFAULT);
140
141 BASE_FEATURE(kIsTrimMemoryBackgroundCritical,
142 "IsTrimMemoryBackgroundCritical",
143 FEATURE_ENABLED_BY_DEFAULT);
144
145 BASE_FEATURE(kShouldFreezeSelf,
146 "ShouldFreezeSelf",
147 FEATURE_DISABLED_BY_DEFAULT);
148
PreFreezeBackgroundMemoryTrimmer()149 PreFreezeBackgroundMemoryTrimmer::PreFreezeBackgroundMemoryTrimmer()
150 : supports_modern_trim_(BuildInfo::GetInstance()->sdk_int() >=
151 SDK_VERSION_U) {}
152
153 // static
Instance()154 PreFreezeBackgroundMemoryTrimmer& PreFreezeBackgroundMemoryTrimmer::Instance() {
155 static base::NoDestructor<PreFreezeBackgroundMemoryTrimmer> instance;
156 return *instance;
157 }
158
RecordMetrics()159 void PreFreezeBackgroundMemoryTrimmer::RecordMetrics() {
160 // We check that the command line is available here because we use it to
161 // determine the current process, which is used for the names of metrics
162 // below.
163 CHECK(base::CommandLine::InitializedForCurrentProcess());
164 base::AutoLock locker(lock_);
165 if (metrics_.size() != values_before_.size()) {
166 UmaHistogramEnumeration("Memory.PreFreeze2.RecordMetricsFailureType",
167 MetricsFailure::kSizeMismatch);
168 values_before_.clear();
169 return;
170 }
171
172 for (size_t i = 0; i < metrics_.size(); i++) {
173 const auto metric = metrics_[i];
174 const std::optional<uint64_t> value_before = values_before_[i];
175
176 std::optional<uint64_t> value_after = metric->Measure();
177
178 MaybeRecordMetric(value_before, metric->name(), "Before");
179 MaybeRecordMetric(value_after, metric->name(), "After");
180 MaybeRecordMetric(Diff(value_before, value_after), metric->name(), "Diff");
181 }
182
183 values_before_.clear();
184 }
185
PostMetricsTask()186 void PreFreezeBackgroundMemoryTrimmer::PostMetricsTask() {
187 // PreFreeze is only for Android U and greater, so no need to record metrics
188 // for older versions.
189 if (!SupportsModernTrim()) {
190 return;
191 }
192
193 // We need the process type to record the metrics below, which we get from
194 // the command line. We cannot post the task below if the thread pool is not
195 // initialized yet.
196 if (!base::CommandLine::InitializedForCurrentProcess() ||
197 !base::ThreadPoolInstance::Get()) {
198 return;
199 }
200
201 // The |RecordMetrics| task resets the |values_before_| after it uses them.
202 // That task is posted with a 2 second delay from when |OnPreFreeze| is run.
203 //
204 // From the time that Chrome is backgrounded until Android delivers the signal
205 // to run PreFreeze always takes at least 10 seconds.
206 //
207 // Therefore, even if we:
208 // - Post |RecordMetrics|
209 // - and then immediately return to foreground and immediately back to
210 // background.
211 // We still will have to wait at least 10 seconds before we get the PreFreeze
212 // signal again, by which time the original RecordMetrics task will have
213 // already finished.
214 if (values_before_.size() > 0) {
215 UmaHistogramEnumeration("Memory.PreFreeze2.RecordMetricsFailureType",
216 MetricsFailure::kAlreadyRunning);
217 return;
218 }
219 for (const auto& metric : metrics_) {
220 values_before_.push_back(metric->Measure());
221 }
222
223 // The posted task will be more likely to survive background killing in
224 // experiments that change the memory trimming behavior. Run as USER_BLOCKING
225 // to reduce this sample imbalance in experiment groups. Normally tasks
226 // collecting metrics should use BEST_EFFORT, but when running in background a
227 // number of subtle effects may influence the real delay of those tasks. The
228 // USER_BLOCKING will allow to estimate the number of better-survived tasks
229 // more precisely.
230 base::ThreadPool::PostDelayedTask(
231 FROM_HERE, {base::TaskPriority::USER_BLOCKING, MayBlock()},
232 base::BindOnce(&PreFreezeBackgroundMemoryTrimmer::RecordMetrics,
233 base::Unretained(this)),
234 kDelayForMetrics);
235 }
236
237 // static
PostDelayedBackgroundTask(scoped_refptr<base::SequencedTaskRunner> task_runner,const base::Location & from_here,OnceCallback<void (MemoryReductionTaskContext)> task,base::TimeDelta delay)238 void PreFreezeBackgroundMemoryTrimmer::PostDelayedBackgroundTask(
239 scoped_refptr<base::SequencedTaskRunner> task_runner,
240 const base::Location& from_here,
241 OnceCallback<void(MemoryReductionTaskContext)> task,
242 base::TimeDelta delay) {
243 // Preserve previous behaviour on versions before Android U.
244 if (!SupportsModernTrim()) {
245 task_runner->PostDelayedTask(
246 from_here,
247 BindOnce(std::move(task), MemoryReductionTaskContext::kDelayExpired),
248 delay);
249 return;
250 }
251
252 Instance().PostDelayedBackgroundTaskInternal(task_runner, from_here,
253 std::move(task), delay);
254 }
255
PostDelayedBackgroundTaskInternal(scoped_refptr<base::SequencedTaskRunner> task_runner,const base::Location & from_here,OnceCallback<void (MemoryReductionTaskContext)> task,base::TimeDelta delay)256 void PreFreezeBackgroundMemoryTrimmer::PostDelayedBackgroundTaskInternal(
257 scoped_refptr<base::SequencedTaskRunner> task_runner,
258 const base::Location& from_here,
259 OnceCallback<void(MemoryReductionTaskContext)> task,
260 base::TimeDelta delay) {
261 DCHECK(SupportsModernTrim());
262
263 RegisterPrivateMemoryFootprintMetric();
264
265 if (!base::FeatureList::IsEnabled(kOnPreFreezeMemoryTrim)) {
266 task_runner->PostDelayedTask(
267 from_here,
268 BindOnce(std::move(task), MemoryReductionTaskContext::kDelayExpired),
269 delay);
270 return;
271 }
272
273 PostDelayedBackgroundTaskModern(task_runner, from_here, std::move(task),
274 delay);
275 }
276
PostDelayedBackgroundTaskModern(scoped_refptr<base::SequencedTaskRunner> task_runner,const base::Location & from_here,OnceCallback<void (MemoryReductionTaskContext)> task,base::TimeDelta delay)277 void PreFreezeBackgroundMemoryTrimmer::PostDelayedBackgroundTaskModern(
278 scoped_refptr<base::SequencedTaskRunner> task_runner,
279 const base::Location& from_here,
280 OnceCallback<void(MemoryReductionTaskContext)> task,
281 base::TimeDelta delay) {
282 // We create a cancellable delayed task (below), which must be done on the
283 // same TaskRunner that will run the task eventually, so we may need to
284 // repost this on the correct TaskRunner.
285 if (!task_runner->RunsTasksInCurrentSequence()) {
286 // |base::Unretained(this)| is safe here because we never destroy |this|.
287 task_runner->PostTask(
288 FROM_HERE,
289 base::BindOnce(
290 &PreFreezeBackgroundMemoryTrimmer::PostDelayedBackgroundTaskModern,
291 base::Unretained(this), task_runner, from_here, std::move(task),
292 delay));
293 return;
294 }
295
296 base::AutoLock locker(lock_);
297 PostDelayedBackgroundTaskModernHelper(std::move(task_runner), from_here,
298 std::move(task), delay);
299 }
300
301 PreFreezeBackgroundMemoryTrimmer::BackgroundTask*
PostDelayedBackgroundTaskModernHelper(scoped_refptr<SequencedTaskRunner> task_runner,const Location & from_here,OnceCallback<void (MemoryReductionTaskContext)> task,TimeDelta delay)302 PreFreezeBackgroundMemoryTrimmer::PostDelayedBackgroundTaskModernHelper(
303 scoped_refptr<SequencedTaskRunner> task_runner,
304 const Location& from_here,
305 OnceCallback<void(MemoryReductionTaskContext)> task,
306 TimeDelta delay) {
307 std::unique_ptr<BackgroundTask> background_task =
308 BackgroundTask::Create(task_runner, from_here, std::move(task), delay);
309 auto* ptr = background_task.get();
310 background_tasks_.push_back(std::move(background_task));
311 return ptr;
312 }
313
314 // static
RegisterMemoryMetric(const PreFreezeMetric * metric)315 void PreFreezeBackgroundMemoryTrimmer::RegisterMemoryMetric(
316 const PreFreezeMetric* metric) {
317 base::AutoLock locker(Instance().lock_);
318 Instance().RegisterMemoryMetricInternal(metric);
319 }
320
RegisterMemoryMetricInternal(const PreFreezeMetric * metric)321 void PreFreezeBackgroundMemoryTrimmer::RegisterMemoryMetricInternal(
322 const PreFreezeMetric* metric) {
323 metrics_.push_back(metric);
324 // If we are in the middle of recording metrics when we register this, add
325 // a nullopt at the end so that metrics recording doesn't fail for all
326 // metrics, just this one.
327 if (values_before_.size() > 0) {
328 values_before_.push_back(std::nullopt);
329 }
330 }
331
332 // static
UnregisterMemoryMetric(const PreFreezeMetric * metric)333 void PreFreezeBackgroundMemoryTrimmer::UnregisterMemoryMetric(
334 const PreFreezeMetric* metric) {
335 base::AutoLock locker(Instance().lock_);
336 Instance().UnregisterMemoryMetricInternal(metric);
337 }
338
UnregisterMemoryMetricInternal(const PreFreezeMetric * metric)339 void PreFreezeBackgroundMemoryTrimmer::UnregisterMemoryMetricInternal(
340 const PreFreezeMetric* metric) {
341 auto it = std::find(metrics_.begin(), metrics_.end(), metric);
342 CHECK(it != metrics_.end());
343 const long index = it - metrics_.begin();
344 if (values_before_.size() > 0) {
345 CHECK_EQ(values_before_.size(), metrics_.size());
346 values_before_.erase(values_before_.begin() + index);
347 }
348 metrics_.erase(metrics_.begin() + index);
349 }
350
351 // static
SelfCompactionIsSupported()352 bool PreFreezeBackgroundMemoryTrimmer::SelfCompactionIsSupported() {
353 return IsMadvisePageoutSupported();
354 }
355
356 // static
CompactSelf()357 std::optional<int64_t> PreFreezeBackgroundMemoryTrimmer::CompactSelf() {
358 // MADV_PAGEOUT was only added in Linux 5.4, so do nothing in earlier
359 // versions.
360 if (!SelfCompactionIsSupported()) {
361 return std::nullopt;
362 }
363
364 std::vector<debug::MappedMemoryRegion> regions;
365
366 std::string proc_maps;
367 if (!debug::ReadProcMaps(&proc_maps) || !ParseProcMaps(proc_maps, ®ions)) {
368 return std::nullopt;
369 }
370
371 if (regions.size() == 0) {
372 return std::nullopt;
373 }
374
375 // TODO(crbug.com/344547190): This may run for a long time. Add a way to
376 // cancel this part-way through if we return to the foreground while this is
377 // running.
378 return CompactMemory(std::move(regions));
379 }
380
381 // static
CompactRegion(debug::MappedMemoryRegion region)382 std::optional<int64_t> PreFreezeBackgroundMemoryTrimmer::CompactRegion(
383 debug::MappedMemoryRegion region) {
384 #if defined(MADV_PAGEOUT)
385 // Skip file-backed regions
386 if (region.inode != 0 || region.dev_major != 0) {
387 return 0;
388 }
389 // Skip shared regions
390 if ((region.permissions & debug::MappedMemoryRegion::Permission::PRIVATE) ==
391 0) {
392 return 0;
393 }
394
395 TRACE_EVENT1("base", __PRETTY_FUNCTION__, "size", region.end - region.start);
396
397 int error = madvise(reinterpret_cast<void*>(region.start),
398 region.end - region.start, MADV_PAGEOUT);
399
400 if (error < 0) {
401 // We may fail on some regions, such as [vvar], or a locked region. It's
402 // not worth it to try to filter these all out, so we just skip them, and
403 // rely on metrics to verify that this is working correctly for most
404 // regions.
405 //
406 // EINVAL could be [vvar] or a locked region. ENOMEM would be a moved or
407 // unmapped region.
408 if (errno != EINVAL && errno != ENOMEM) {
409 PLOG(ERROR) << "Unexpected error from madvise.";
410 return std::nullopt;
411 }
412 return 0;
413 }
414
415 return region.end - region.start;
416 #else
417 return std::nullopt;
418 #endif
419 }
420
421 // static
CompactMemory(std::vector<debug::MappedMemoryRegion> regions)422 std::optional<int64_t> PreFreezeBackgroundMemoryTrimmer::CompactMemory(
423 std::vector<debug::MappedMemoryRegion> regions) {
424 TRACE_EVENT1("base", __PRETTY_FUNCTION__, "count", regions.size());
425 int64_t total_bytes_processed = 0;
426 for (const auto& region : regions) {
427 const auto bytes_processed = CompactRegion(region);
428 if (!bytes_processed) {
429 return std::nullopt;
430 }
431 total_bytes_processed += bytes_processed.value();
432 }
433 return total_bytes_processed;
434 }
435
PostMetricsTasksIfModern()436 void PreFreezeBackgroundMemoryTrimmer::PostMetricsTasksIfModern() {
437 if (!SupportsModernTrim()) {
438 return;
439 }
440 PostMetricsTask();
441 }
442
443 // static
OnSelfFreeze()444 void PreFreezeBackgroundMemoryTrimmer::OnSelfFreeze() {
445 // TODO
446 }
447
448 // static
OnPreFreeze()449 void PreFreezeBackgroundMemoryTrimmer::OnPreFreeze() {
450 Instance().OnPreFreezeInternal();
451 }
452
OnPreFreezeInternal()453 void PreFreezeBackgroundMemoryTrimmer::OnPreFreezeInternal() {
454 base::AutoLock locker(lock_);
455 PostMetricsTasksIfModern();
456
457 if (!ShouldUseModernTrim()) {
458 return;
459 }
460
461 // We check |num_pending_tasks-- > 0| so that we have an upper limit on the
462 // number of tasks that we run.
463 // We check |!background_tasks_.empty()| so that we exit as soon as we have
464 // no more tasks to run.
465 //
466 // This handles both the case where we have tasks that post other tasks (we
467 // won't run endlessly because of the upper limit), and the case where tasks
468 // cancel other tasks (we exit as soon as the queue is empty).
469 //
470 // Note that the current implementation may run some tasks that were posted
471 // by earlier tasks, if some other tasks are also cancelled, but we
472 // stop eventually due to the upper limit.
473 size_t num_pending_tasks = background_tasks_.size();
474 while (num_pending_tasks-- > 0 && !background_tasks_.empty()) {
475 auto background_task = std::move(background_tasks_.front());
476 background_tasks_.pop_front();
477 // We release the lock here for two reasons:
478 // (1) To avoid holding it too long while running all the background tasks.
479 // (2) To prevent a deadlock if the |background_task| needs to acquire the
480 // lock (e.g. to post another task).
481 base::AutoUnlock unlocker(lock_);
482 BackgroundTask::RunNow(std::move(background_task));
483 }
484 }
485
486 // static
UnregisterBackgroundTask(BackgroundTask * task)487 void PreFreezeBackgroundMemoryTrimmer::UnregisterBackgroundTask(
488 BackgroundTask* task) {
489 return Instance().UnregisterBackgroundTaskInternal(task);
490 }
491
UnregisterBackgroundTaskInternal(BackgroundTask * timer)492 void PreFreezeBackgroundMemoryTrimmer::UnregisterBackgroundTaskInternal(
493 BackgroundTask* timer) {
494 base::AutoLock locker(lock_);
495 std::erase_if(background_tasks_, [&](auto& t) { return t.get() == timer; });
496 }
497
498 // static
RegisterPrivateMemoryFootprintMetric()499 void PreFreezeBackgroundMemoryTrimmer::RegisterPrivateMemoryFootprintMetric() {
500 base::AutoLock locker(Instance().lock_);
501 static base::NoDestructor<PrivateMemoryFootprintMetric> pmf_metric;
502 if (!PrivateMemoryFootprintMetric::did_register_) {
503 PrivateMemoryFootprintMetric::did_register_ = true;
504 Instance().RegisterMemoryMetricInternal(pmf_metric.get());
505 }
506 }
507
508 // static
SupportsModernTrim()509 bool PreFreezeBackgroundMemoryTrimmer::SupportsModernTrim() {
510 return Instance().supports_modern_trim_;
511 }
512
513 // static
ShouldUseModernTrim()514 bool PreFreezeBackgroundMemoryTrimmer::ShouldUseModernTrim() {
515 return SupportsModernTrim() &&
516 base::FeatureList::IsEnabled(kOnPreFreezeMemoryTrim);
517 }
518
519 // static
IsTrimMemoryBackgroundCritical()520 bool PreFreezeBackgroundMemoryTrimmer::IsTrimMemoryBackgroundCritical() {
521 return SupportsModernTrim() &&
522 base::FeatureList::IsEnabled(kIsTrimMemoryBackgroundCritical);
523 }
524
525 // static
SetSupportsModernTrimForTesting(bool is_supported)526 void PreFreezeBackgroundMemoryTrimmer::SetSupportsModernTrimForTesting(
527 bool is_supported) {
528 Instance().supports_modern_trim_ = is_supported;
529 }
530
531 // static
ClearMetricsForTesting()532 void PreFreezeBackgroundMemoryTrimmer::ClearMetricsForTesting() {
533 base::AutoLock locker(Instance().lock_);
534 Instance().metrics_.clear();
535 PrivateMemoryFootprintMetric::did_register_ = false;
536 }
537
DidRegisterTasksForTesting() const538 bool PreFreezeBackgroundMemoryTrimmer::DidRegisterTasksForTesting() const {
539 base::AutoLock locker(lock_);
540 return metrics_.size() != 0;
541 }
542
543 size_t
GetNumberOfPendingBackgroundTasksForTesting() const544 PreFreezeBackgroundMemoryTrimmer::GetNumberOfPendingBackgroundTasksForTesting()
545 const {
546 base::AutoLock locker(lock_);
547 return background_tasks_.size();
548 }
549
GetNumberOfKnownMetricsForTesting() const550 size_t PreFreezeBackgroundMemoryTrimmer::GetNumberOfKnownMetricsForTesting()
551 const {
552 base::AutoLock locker(lock_);
553 return metrics_.size();
554 }
555
GetNumberOfValuesBeforeForTesting() const556 size_t PreFreezeBackgroundMemoryTrimmer::GetNumberOfValuesBeforeForTesting()
557 const {
558 base::AutoLock locker(lock_);
559 return values_before_.size();
560 }
561
562 // static
RunNow(std::unique_ptr<PreFreezeBackgroundMemoryTrimmer::BackgroundTask> background_task)563 void PreFreezeBackgroundMemoryTrimmer::BackgroundTask::RunNow(
564 std::unique_ptr<PreFreezeBackgroundMemoryTrimmer::BackgroundTask>
565 background_task) {
566 if (!background_task->task_runner_->RunsTasksInCurrentSequence()) {
567 background_task->task_runner_->PostTask(
568 FROM_HERE,
569 base::BindOnce(&BackgroundTask::RunNow, std::move(background_task)));
570 return;
571 }
572
573 // We check that the task has not been run already. If it has, we do not run
574 // it again.
575 if (background_task->task_handle_.IsValid()) {
576 background_task->task_handle_.CancelTask();
577 } else {
578 return;
579 }
580
581 background_task->Run(MemoryReductionTaskContext::kProactive);
582 }
583
CancelTask()584 void PreFreezeBackgroundMemoryTrimmer::BackgroundTask::CancelTask() {
585 if (task_handle_.IsValid()) {
586 task_handle_.CancelTask();
587 PreFreezeBackgroundMemoryTrimmer::UnregisterBackgroundTask(this);
588 }
589 }
590
591 // static
592 std::unique_ptr<PreFreezeBackgroundMemoryTrimmer::BackgroundTask>
Create(scoped_refptr<base::SequencedTaskRunner> task_runner,const base::Location & from_here,OnceCallback<void (MemoryReductionTaskContext)> task,base::TimeDelta delay)593 PreFreezeBackgroundMemoryTrimmer::BackgroundTask::Create(
594 scoped_refptr<base::SequencedTaskRunner> task_runner,
595 const base::Location& from_here,
596 OnceCallback<void(MemoryReductionTaskContext)> task,
597 base::TimeDelta delay) {
598 DCHECK(task_runner->RunsTasksInCurrentSequence());
599 auto background_task = std::make_unique<BackgroundTask>(task_runner);
600 background_task->Start(from_here, delay, std::move(task));
601 return background_task;
602 }
603
BackgroundTask(scoped_refptr<base::SequencedTaskRunner> task_runner)604 PreFreezeBackgroundMemoryTrimmer::BackgroundTask::BackgroundTask(
605 scoped_refptr<base::SequencedTaskRunner> task_runner)
606 : task_runner_(task_runner) {}
607
608 PreFreezeBackgroundMemoryTrimmer::BackgroundTask::~BackgroundTask() = default;
609
Run(MemoryReductionTaskContext from_pre_freeze)610 void PreFreezeBackgroundMemoryTrimmer::BackgroundTask::Run(
611 MemoryReductionTaskContext from_pre_freeze) {
612 DCHECK(!task_handle_.IsValid());
613 std::move(task_).Run(from_pre_freeze);
614 }
615
Start(const base::Location & from_here,base::TimeDelta delay,OnceCallback<void (MemoryReductionTaskContext)> task)616 void PreFreezeBackgroundMemoryTrimmer::BackgroundTask::Start(
617 const base::Location& from_here,
618 base::TimeDelta delay,
619 OnceCallback<void(MemoryReductionTaskContext)> task) {
620 task_ = std::move(task);
621 task_handle_ = task_runner_->PostCancelableDelayedTask(
622 subtle::PostDelayedTaskPassKey(), from_here,
623 base::BindOnce(
624 [](BackgroundTask* p) {
625 p->Run(MemoryReductionTaskContext::kDelayExpired);
626 UnregisterBackgroundTask(p);
627 },
628 this),
629 delay);
630 }
631
PreFreezeMetric(const std::string & name)632 PreFreezeBackgroundMemoryTrimmer::PreFreezeMetric::PreFreezeMetric(
633 const std::string& name)
634 : name_(name) {}
635
636 PreFreezeBackgroundMemoryTrimmer::PreFreezeMetric::~PreFreezeMetric() = default;
637
638 } // namespace base::android
639