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