1 // Copyright 2013 The Chromium Authors. All rights reserved.
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/memory/memory_pressure_listener.h"
6 
7 #include "base/observer_list_threadsafe.h"
8 #include "base/threading/sequenced_task_runner_handle.h"
9 #include "base/trace_event/base_tracing.h"
10 
11 namespace base {
12 
13 namespace {
14 
15 // This class is thread safe and internally synchronized.
16 class MemoryPressureObserver {
17  public:
18   // There is at most one MemoryPressureObserver and it is never deleted.
19   ~MemoryPressureObserver() = delete;
20 
AddObserver(MemoryPressureListener * listener,bool sync)21   void AddObserver(MemoryPressureListener* listener, bool sync) {
22     // TODO(crbug.com/1063868): DCHECK instead of silently failing when a
23     // MemoryPressureListener is created in a non-sequenced context. Tests will
24     // need to be adjusted for that to work.
25     if (SequencedTaskRunnerHandle::IsSet()) {
26       async_observers_->AddObserver(listener);
27     }
28 
29     if (sync) {
30       AutoLock lock(sync_observers_lock_);
31       sync_observers_.AddObserver(listener);
32     }
33   }
34 
RemoveObserver(MemoryPressureListener * listener)35   void RemoveObserver(MemoryPressureListener* listener) {
36     async_observers_->RemoveObserver(listener);
37     AutoLock lock(sync_observers_lock_);
38     sync_observers_.RemoveObserver(listener);
39   }
40 
Notify(MemoryPressureListener::MemoryPressureLevel memory_pressure_level)41   void Notify(
42       MemoryPressureListener::MemoryPressureLevel memory_pressure_level) {
43     async_observers_->Notify(FROM_HERE, &MemoryPressureListener::Notify,
44                              memory_pressure_level);
45     AutoLock lock(sync_observers_lock_);
46     for (auto& observer : sync_observers_)
47       observer.SyncNotify(memory_pressure_level);
48   }
49 
50  private:
51   const scoped_refptr<ObserverListThreadSafe<MemoryPressureListener>>
52       async_observers_ =
53           base::MakeRefCounted<ObserverListThreadSafe<MemoryPressureListener>>(
54               ObserverListPolicy::EXISTING_ONLY);
55   ObserverList<MemoryPressureListener>::Unchecked sync_observers_;
56   Lock sync_observers_lock_;
57 };
58 
59 // Gets the shared MemoryPressureObserver singleton instance.
GetMemoryPressureObserver()60 MemoryPressureObserver* GetMemoryPressureObserver() {
61   static auto* const observer = new MemoryPressureObserver();
62   return observer;
63 }
64 
65 subtle::Atomic32 g_notifications_suppressed = 0;
66 
67 }  // namespace
68 
MemoryPressureListener(const base::Location & creation_location,const MemoryPressureListener::MemoryPressureCallback & callback)69 MemoryPressureListener::MemoryPressureListener(
70     const base::Location& creation_location,
71     const MemoryPressureListener::MemoryPressureCallback& callback)
72     : callback_(callback), creation_location_(creation_location) {
73   GetMemoryPressureObserver()->AddObserver(this, false);
74 }
75 
MemoryPressureListener(const base::Location & creation_location,const MemoryPressureListener::MemoryPressureCallback & callback,const MemoryPressureListener::SyncMemoryPressureCallback & sync_memory_pressure_callback)76 MemoryPressureListener::MemoryPressureListener(
77     const base::Location& creation_location,
78     const MemoryPressureListener::MemoryPressureCallback& callback,
79     const MemoryPressureListener::SyncMemoryPressureCallback&
80         sync_memory_pressure_callback)
81     : callback_(callback),
82       sync_memory_pressure_callback_(sync_memory_pressure_callback),
83       creation_location_(creation_location) {
84   GetMemoryPressureObserver()->AddObserver(this, true);
85 }
86 
~MemoryPressureListener()87 MemoryPressureListener::~MemoryPressureListener() {
88   GetMemoryPressureObserver()->RemoveObserver(this);
89 }
90 
Notify(MemoryPressureLevel memory_pressure_level)91 void MemoryPressureListener::Notify(MemoryPressureLevel memory_pressure_level) {
92   TRACE_EVENT2("base", "MemoryPressureListener::Notify",
93                "listener_creation_info", creation_location_.ToString(), "level",
94                memory_pressure_level);
95   callback_.Run(memory_pressure_level);
96 }
97 
SyncNotify(MemoryPressureLevel memory_pressure_level)98 void MemoryPressureListener::SyncNotify(
99     MemoryPressureLevel memory_pressure_level) {
100   if (!sync_memory_pressure_callback_.is_null()) {
101     sync_memory_pressure_callback_.Run(memory_pressure_level);
102   }
103 }
104 
105 // static
NotifyMemoryPressure(MemoryPressureLevel memory_pressure_level)106 void MemoryPressureListener::NotifyMemoryPressure(
107     MemoryPressureLevel memory_pressure_level) {
108   DCHECK_NE(memory_pressure_level, MEMORY_PRESSURE_LEVEL_NONE);
109   TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("memory-infra"),
110                        "MemoryPressureListener::NotifyMemoryPressure",
111                        TRACE_EVENT_SCOPE_THREAD, "level",
112                        memory_pressure_level);
113   if (AreNotificationsSuppressed())
114     return;
115   DoNotifyMemoryPressure(memory_pressure_level);
116 }
117 
118 // static
AreNotificationsSuppressed()119 bool MemoryPressureListener::AreNotificationsSuppressed() {
120   return subtle::Acquire_Load(&g_notifications_suppressed) == 1;
121 }
122 
123 // static
SetNotificationsSuppressed(bool suppress)124 void MemoryPressureListener::SetNotificationsSuppressed(bool suppress) {
125   subtle::Release_Store(&g_notifications_suppressed, suppress ? 1 : 0);
126 }
127 
128 // static
SimulatePressureNotification(MemoryPressureLevel memory_pressure_level)129 void MemoryPressureListener::SimulatePressureNotification(
130     MemoryPressureLevel memory_pressure_level) {
131   // Notify all listeners even if regular pressure notifications are suppressed.
132   DoNotifyMemoryPressure(memory_pressure_level);
133 }
134 
135 // static
DoNotifyMemoryPressure(MemoryPressureLevel memory_pressure_level)136 void MemoryPressureListener::DoNotifyMemoryPressure(
137     MemoryPressureLevel memory_pressure_level) {
138   DCHECK_NE(memory_pressure_level, MEMORY_PRESSURE_LEVEL_NONE);
139 
140   GetMemoryPressureObserver()->Notify(memory_pressure_level);
141 }
142 
143 }  // namespace base
144