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