• 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 #ifndef BASE_ANDROID_PRE_FREEZE_BACKGROUND_MEMORY_TRIMMER_H_
6 #define BASE_ANDROID_PRE_FREEZE_BACKGROUND_MEMORY_TRIMMER_H_
7 
8 #include <deque>
9 
10 #include "base/compiler_specific.h"
11 #include "base/debug/proc_maps_linux.h"
12 #include "base/feature_list.h"
13 #include "base/functional/callback.h"
14 #include "base/memory/post_delayed_memory_reduction_task.h"
15 #include "base/no_destructor.h"
16 #include "base/task/delayed_task_handle.h"
17 #include "base/task/sequenced_task_runner.h"
18 #include "base/timer/timer.h"
19 
20 namespace base::android {
21 class MemoryPurgeManagerAndroid;
22 
23 BASE_EXPORT BASE_DECLARE_FEATURE(kOnPreFreezeMemoryTrim);
24 BASE_EXPORT BASE_DECLARE_FEATURE(kShouldFreezeSelf);
25 
26 // Starting from Android U, apps are frozen shortly after being backgrounded
27 // (with some exceptions). This causes some background tasks for reclaiming
28 // resources in Chrome to not be run until Chrome is foregrounded again (which
29 // completely defeats their purpose).
30 //
31 // To try to avoid this problem, we use the |PostDelayedBackgroundTask| found
32 // below. Prior to Android U, this will simply post the task in the background
33 // with the given delay. From Android U onwards, this will post the task in the
34 // background with the given delay, but will run it sooner if we are about to
35 // be frozen.
36 class BASE_EXPORT PreFreezeBackgroundMemoryTrimmer {
37  public:
38   static PreFreezeBackgroundMemoryTrimmer& Instance();
39   ~PreFreezeBackgroundMemoryTrimmer() = delete;
40 
41   // Posts a delayed task. On versions of Android starting from U, may run the
42   // task sooner if we are backgrounded. In this case, we run the task on the
43   // correct task runner, ignoring the given delay.
PostDelayedBackgroundTask(scoped_refptr<base::SequencedTaskRunner> task_runner,const base::Location & from_here,OnceCallback<void (void)> task,base::TimeDelta delay)44   static void PostDelayedBackgroundTask(
45       scoped_refptr<base::SequencedTaskRunner> task_runner,
46       const base::Location& from_here,
47       OnceCallback<void(void)> task,
48       base::TimeDelta delay) LOCKS_EXCLUDED(lock_) {
49     PostDelayedBackgroundTask(
50         task_runner, from_here,
51         BindOnce(
52             [](OnceClosure task,
53                MemoryReductionTaskContext called_from_prefreeze) {
54               std::move(task).Run();
55             },
56             std::move(task)),
57         delay);
58   }
59   static void PostDelayedBackgroundTask(
60       scoped_refptr<base::SequencedTaskRunner> task_runner,
61       const base::Location& from_here,
62       OnceCallback<void(MemoryReductionTaskContext)> task,
63       base::TimeDelta delay) LOCKS_EXCLUDED(lock_);
64 
65   class PreFreezeMetric {
66    public:
67     virtual ~PreFreezeMetric();
68 
69     // |Measure| should return an amount of memory in bytes, or nullopt if
70     // unable to record the metric for any reason. It is called underneath a
71     // lock, so it should be fast enough to avoid delays (the same lock is held
72     // when unregistering metrics).
73     virtual std::optional<uint64_t> Measure() const = 0;
74 
name()75     const std::string& name() const LIFETIME_BOUND { return name_; }
76 
77    protected:
78     friend class PreFreezeBackgroundMemoryTrimmer;
79     explicit PreFreezeMetric(const std::string& name);
80 
81    private:
82     const std::string name_;
83   };
84 
85   // Registers a new metric to record before and after PreFreeze. Callers are
86   // responsible for making sure that the same metric is not registered
87   // multiple times.
88   //
89   // See |PreFreezeMetric| for details on the metric itself.
90   //
91   // Each time |OnPreFreeze| is run, |metric->Measure()| will be called twice:
92   // - Once directly before any tasks are run; and
93   // - Once two seconds after the first time it was called.
94   //
95   // As an example, calling RegisterMemoryMetric(PrivateMemoryFootprintMetric)
96   // in the Browser process would cause the following metrics to be recorded 2
97   // seconds after the next time |OnPreFreeze| is run:
98   // - "Memory.PreFreeze2.Browser.PrivateMemoryFootprint.Before"
99   // - "Memory.PreFreeze2.Browser.PrivateMemoryFootprint.After"
100   // - "Memory.PreFreeze2.Browser.PrivateMemoryFootprint.Diff"
101   //
102   // See "Memory.PreFreeze2.{process_type}.{name}.{suffix}" for details on the
103   // exact metrics.
104   static void RegisterMemoryMetric(const PreFreezeMetric* metric)
105       LOCKS_EXCLUDED(Instance().lock_);
106 
107   static void UnregisterMemoryMetric(const PreFreezeMetric* metric)
108       LOCKS_EXCLUDED(Instance().lock_);
109 
110   static bool SelfCompactionIsSupported();
111 
112   // Compacts the memory for the process, and returns the number of bytes
113   // processed on success.
114   static std::optional<int64_t> CompactSelf();
115 
116   static void SetSupportsModernTrimForTesting(bool is_supported);
117   static void ClearMetricsForTesting() LOCKS_EXCLUDED(lock_);
118   size_t GetNumberOfPendingBackgroundTasksForTesting() const
119       LOCKS_EXCLUDED(lock_);
120   size_t GetNumberOfKnownMetricsForTesting() const LOCKS_EXCLUDED(lock_);
121   size_t GetNumberOfValuesBeforeForTesting() const LOCKS_EXCLUDED(lock_);
122   bool DidRegisterTasksForTesting() const;
123 
OnPreFreezeForTesting()124   static void OnPreFreezeForTesting() LOCKS_EXCLUDED(lock_) { OnPreFreeze(); }
125 
126   static std::optional<int64_t> CompactRegion(debug::MappedMemoryRegion region);
127 
128   // Called when Chrome is about to be frozen. Runs as many delayed tasks as
129   // possible immediately, before we are frozen.
130   static void OnPreFreeze() LOCKS_EXCLUDED(lock_);
131 
132   static void OnSelfFreeze() LOCKS_EXCLUDED(lock_);
133 
134   static bool SupportsModernTrim();
135   static bool ShouldUseModernTrim();
136   static bool IsTrimMemoryBackgroundCritical();
137 
138  private:
139   friend class base::NoDestructor<PreFreezeBackgroundMemoryTrimmer>;
140   friend jboolean JNI_MemoryPurgeManager_IsOnPreFreezeMemoryTrimEnabled(
141       JNIEnv* env);
142   friend class base::android::MemoryPurgeManagerAndroid;
143   friend class base::OneShotDelayedBackgroundTimer;
144   friend class PreFreezeBackgroundMemoryTrimmerTest;
145   friend class PreFreezeSelfCompactionTest;
146 
147   // We use our own implementation here, based on |PostCancelableDelayedTask|,
148   // rather than relying on something like |base::OneShotTimer|, since
149   // |base::OneShotTimer| doesn't support things like immediately running our
150   // task from a different sequence, and some |base::OneShotTimer|
151   // functionality (e.g. |FireNow|) only works with the default task runner.
152   class BackgroundTask final {
153    public:
154     static std::unique_ptr<BackgroundTask> Create(
155         scoped_refptr<base::SequencedTaskRunner> task_runner,
156         const base::Location& from_here,
157         OnceCallback<void(MemoryReductionTaskContext)> task,
158         base::TimeDelta delay);
159 
160     explicit BackgroundTask(
161         scoped_refptr<base::SequencedTaskRunner> task_runner);
162     ~BackgroundTask();
163 
164     static void RunNow(std::unique_ptr<BackgroundTask> background_task);
165 
166     void Run(MemoryReductionTaskContext from_pre_freeze);
167 
168     void CancelTask();
169 
170    private:
171     friend class PreFreezeBackgroundMemoryTrimmer;
172     void Start(const Location& from_here,
173                TimeDelta delay,
174                OnceCallback<void(MemoryReductionTaskContext)> task);
175     void StartInternal(const Location& from_here,
176                        TimeDelta delay,
177                        OnceClosure task);
178     scoped_refptr<base::SequencedTaskRunner> task_runner_;
179     base::DelayedTaskHandle task_handle_;
180 
181     OnceCallback<void(MemoryReductionTaskContext)> task_;
182   };
183 
184   PreFreezeBackgroundMemoryTrimmer();
185 
186   static std::optional<int64_t> CompactMemory(
187       std::vector<debug::MappedMemoryRegion> regions);
188 
189   void RegisterMemoryMetricInternal(const PreFreezeMetric* metric)
190       EXCLUSIVE_LOCKS_REQUIRED(lock_);
191 
192   void UnregisterMemoryMetricInternal(const PreFreezeMetric* metric)
193       EXCLUSIVE_LOCKS_REQUIRED(lock_);
194   static void UnregisterBackgroundTask(BackgroundTask*) LOCKS_EXCLUDED(lock_);
195 
196   void UnregisterBackgroundTaskInternal(BackgroundTask*) LOCKS_EXCLUDED(lock_);
197 
198   static void RegisterPrivateMemoryFootprintMetric() LOCKS_EXCLUDED(lock_);
199   void RegisterPrivateMemoryFootprintMetricInternal() LOCKS_EXCLUDED(lock_);
200 
201   void PostDelayedBackgroundTaskInternal(
202       scoped_refptr<base::SequencedTaskRunner> task_runner,
203       const base::Location& from_here,
204       OnceCallback<void(MemoryReductionTaskContext)> task,
205       base::TimeDelta delay) LOCKS_EXCLUDED(lock_);
206   void PostDelayedBackgroundTaskModern(
207       scoped_refptr<base::SequencedTaskRunner> task_runner,
208       const base::Location& from_here,
209       OnceCallback<void(MemoryReductionTaskContext)> task,
210       base::TimeDelta delay) LOCKS_EXCLUDED(lock_);
211   BackgroundTask* PostDelayedBackgroundTaskModernHelper(
212       scoped_refptr<base::SequencedTaskRunner> task_runner,
213       const base::Location& from_here,
214       OnceCallback<void(MemoryReductionTaskContext)> task,
215       base::TimeDelta delay) EXCLUSIVE_LOCKS_REQUIRED(lock_);
216 
217   void OnPreFreezeInternal() LOCKS_EXCLUDED(lock_);
218 
219   void PostMetricsTasksIfModern() EXCLUSIVE_LOCKS_REQUIRED(lock_);
220   void PostMetricsTask() EXCLUSIVE_LOCKS_REQUIRED(lock_);
221   void RecordMetrics() LOCKS_EXCLUDED(lock_);
222 
223   mutable base::Lock lock_;
224   std::deque<std::unique_ptr<BackgroundTask>> background_tasks_
225       GUARDED_BY(lock_);
226   std::vector<const PreFreezeMetric*> metrics_ GUARDED_BY(lock_);
227   // When a metrics task is posted (see |RecordMetrics|), the values of each
228   // metric before any tasks are run are saved here. The "i"th entry corresponds
229   // to the "i"th entry in |metrics_|. When there is no pending metrics task,
230   // |values_before_| should be empty.
231   std::vector<std::optional<uint64_t>> values_before_ GUARDED_BY(lock_);
232   bool supports_modern_trim_;
233 };
234 
235 }  // namespace base::android
236 
237 #endif  // BASE_ANDROID_PRE_FREEZE_BACKGROUND_MEMORY_TRIMMER_H_
238