/src/mozilla-central/xpcom/base/nsMemoryImpl.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
2 | | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ |
3 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
4 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
5 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
6 | | |
7 | | #include "nsMemoryImpl.h" |
8 | | #include "nsThreadUtils.h" |
9 | | |
10 | | #include "nsIObserver.h" |
11 | | #include "nsIObserverService.h" |
12 | | #include "nsISimpleEnumerator.h" |
13 | | |
14 | | #include "nsCOMPtr.h" |
15 | | #include "mozilla/Services.h" |
16 | | |
17 | | #ifdef ANDROID |
18 | | #include <stdio.h> |
19 | | |
20 | | // Minimum memory threshold for a device to be considered |
21 | | // a low memory platform. This value has be in sync with |
22 | | // Java's equivalent threshold, defined in |
23 | | // mobile/android/base/util/HardwareUtils.java |
24 | | #define LOW_MEMORY_THRESHOLD_KB (384 * 1024) |
25 | | #endif |
26 | | |
27 | | static nsMemoryImpl sGlobalMemory; |
28 | | |
29 | | NS_IMPL_QUERY_INTERFACE(nsMemoryImpl, nsIMemory) |
30 | | |
31 | | NS_IMETHODIMP |
32 | | nsMemoryImpl::HeapMinimize(bool aImmediate) |
33 | 0 | { |
34 | 0 | return FlushMemory(u"heap-minimize", aImmediate); |
35 | 0 | } |
36 | | |
37 | | NS_IMETHODIMP |
38 | | nsMemoryImpl::IsLowMemoryPlatform(bool* aResult) |
39 | 0 | { |
40 | | #ifdef ANDROID |
41 | | static int sLowMemory = -1; // initialize to unknown, lazily evaluate to 0 or 1 |
42 | | if (sLowMemory == -1) { |
43 | | sLowMemory = 0; // assume "not low memory" in case file operations fail |
44 | | *aResult = false; |
45 | | |
46 | | // check if MemTotal from /proc/meminfo is less than LOW_MEMORY_THRESHOLD_KB |
47 | | FILE* fd = fopen("/proc/meminfo", "r"); |
48 | | if (!fd) { |
49 | | return NS_OK; |
50 | | } |
51 | | uint64_t mem = 0; |
52 | | int rv = fscanf(fd, "MemTotal: %" PRIu64 " kB", &mem); |
53 | | if (fclose(fd)) { |
54 | | return NS_OK; |
55 | | } |
56 | | if (rv != 1) { |
57 | | return NS_OK; |
58 | | } |
59 | | sLowMemory = (mem < LOW_MEMORY_THRESHOLD_KB) ? 1 : 0; |
60 | | } |
61 | | *aResult = (sLowMemory == 1); |
62 | | #else |
63 | | *aResult = false; |
64 | 0 | #endif |
65 | 0 | return NS_OK; |
66 | 0 | } |
67 | | |
68 | | /*static*/ nsresult |
69 | | nsMemoryImpl::Create(nsISupports* aOuter, const nsIID& aIID, void** aResult) |
70 | 0 | { |
71 | 0 | if (NS_WARN_IF(aOuter)) { |
72 | 0 | return NS_ERROR_NO_AGGREGATION; |
73 | 0 | } |
74 | 0 | return sGlobalMemory.QueryInterface(aIID, aResult); |
75 | 0 | } |
76 | | |
77 | | nsresult |
78 | | nsMemoryImpl::FlushMemory(const char16_t* aReason, bool aImmediate) |
79 | 0 | { |
80 | 0 | nsresult rv = NS_OK; |
81 | 0 |
|
82 | 0 | if (aImmediate) { |
83 | 0 | // They've asked us to run the flusher *immediately*. We've |
84 | 0 | // got to be on the UI main thread for us to be able to do |
85 | 0 | // that...are we? |
86 | 0 | if (!NS_IsMainThread()) { |
87 | 0 | NS_ERROR("can't synchronously flush memory: not on UI thread"); |
88 | 0 | return NS_ERROR_FAILURE; |
89 | 0 | } |
90 | 0 | } |
91 | 0 |
|
92 | 0 | bool lastVal = sIsFlushing.exchange(true); |
93 | 0 | if (lastVal) { |
94 | 0 | return NS_OK; |
95 | 0 | } |
96 | 0 | |
97 | 0 | PRIntervalTime now = PR_IntervalNow(); |
98 | 0 |
|
99 | 0 | // Run the flushers immediately if we can; otherwise, proxy to the |
100 | 0 | // UI thread an run 'em asynchronously. |
101 | 0 | if (aImmediate) { |
102 | 0 | rv = RunFlushers(aReason); |
103 | 0 | } else { |
104 | 0 | // Don't broadcast more than once every 1000ms to avoid being noisy |
105 | 0 | if (PR_IntervalToMicroseconds(now - sLastFlushTime) > 1000) { |
106 | 0 | sFlushEvent.mReason = aReason; |
107 | 0 | rv = NS_DispatchToMainThread(&sFlushEvent); |
108 | 0 | } |
109 | 0 | } |
110 | 0 |
|
111 | 0 | sLastFlushTime = now; |
112 | 0 | return rv; |
113 | 0 | } |
114 | | |
115 | | nsresult |
116 | | nsMemoryImpl::RunFlushers(const char16_t* aReason) |
117 | 0 | { |
118 | 0 | nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService(); |
119 | 0 | if (os) { |
120 | 0 |
|
121 | 0 | // Instead of: |
122 | 0 | // os->NotifyObservers(this, "memory-pressure", aReason); |
123 | 0 | // we are going to do this manually to see who/what is |
124 | 0 | // deallocating. |
125 | 0 |
|
126 | 0 | nsCOMPtr<nsISimpleEnumerator> e; |
127 | 0 | os->EnumerateObservers("memory-pressure", getter_AddRefs(e)); |
128 | 0 |
|
129 | 0 | if (e) { |
130 | 0 | nsCOMPtr<nsIObserver> observer; |
131 | 0 | bool loop = true; |
132 | 0 |
|
133 | 0 | while (NS_SUCCEEDED(e->HasMoreElements(&loop)) && loop) { |
134 | 0 | nsCOMPtr<nsISupports> supports; |
135 | 0 | e->GetNext(getter_AddRefs(supports)); |
136 | 0 |
|
137 | 0 | if (!supports) { |
138 | 0 | continue; |
139 | 0 | } |
140 | 0 | |
141 | 0 | observer = do_QueryInterface(supports); |
142 | 0 | observer->Observe(observer, "memory-pressure", aReason); |
143 | 0 | } |
144 | 0 | } |
145 | 0 | } |
146 | 0 |
|
147 | 0 | sIsFlushing = false; |
148 | 0 | return NS_OK; |
149 | 0 | } |
150 | | |
151 | | // XXX need NS_IMPL_STATIC_ADDREF/RELEASE |
152 | | NS_IMETHODIMP_(MozExternalRefCountType) |
153 | | nsMemoryImpl::FlushEvent::AddRef() |
154 | 0 | { |
155 | 0 | return 2; |
156 | 0 | } |
157 | | NS_IMETHODIMP_(MozExternalRefCountType) |
158 | | nsMemoryImpl::FlushEvent::Release() |
159 | 0 | { |
160 | 0 | return 1; |
161 | 0 | } |
162 | | NS_IMPL_QUERY_INTERFACE(nsMemoryImpl::FlushEvent, nsIRunnable) |
163 | | |
164 | | NS_IMETHODIMP |
165 | | nsMemoryImpl::FlushEvent::Run() |
166 | 0 | { |
167 | 0 | sGlobalMemory.RunFlushers(mReason); |
168 | 0 | return NS_OK; |
169 | 0 | } |
170 | | |
171 | | mozilla::Atomic<bool> |
172 | | nsMemoryImpl::sIsFlushing; |
173 | | |
174 | | PRIntervalTime |
175 | | nsMemoryImpl::sLastFlushTime = 0; |
176 | | |
177 | | nsMemoryImpl::FlushEvent |
178 | | nsMemoryImpl::sFlushEvent; |
179 | | |
180 | | nsresult |
181 | | NS_GetMemoryManager(nsIMemory** aResult) |
182 | 0 | { |
183 | 0 | return sGlobalMemory.QueryInterface(NS_GET_IID(nsIMemory), (void**)aResult); |
184 | 0 | } |