• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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, &regions)) {
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