Line data Source code
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 9 : NS_IMPL_QUERY_INTERFACE(nsMemoryImpl, nsIMemory)
30 :
31 : NS_IMETHODIMP
32 0 : nsMemoryImpl::HeapMinimize(bool aImmediate)
33 : {
34 0 : return FlushMemory(u"heap-minimize", aImmediate);
35 : }
36 :
37 : NS_IMETHODIMP
38 1 : nsMemoryImpl::IsLowMemoryPlatform(bool* aResult)
39 : {
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 1 : *aResult = false;
64 : #endif
65 1 : return NS_OK;
66 : }
67 :
68 : /*static*/ nsresult
69 1 : nsMemoryImpl::Create(nsISupports* aOuter, const nsIID& aIID, void** aResult)
70 : {
71 1 : if (NS_WARN_IF(aOuter)) {
72 0 : return NS_ERROR_NO_AGGREGATION;
73 : }
74 1 : return sGlobalMemory.QueryInterface(aIID, aResult);
75 : }
76 :
77 : nsresult
78 0 : nsMemoryImpl::FlushMemory(const char16_t* aReason, bool aImmediate)
79 : {
80 0 : nsresult rv = NS_OK;
81 :
82 0 : if (aImmediate) {
83 : // They've asked us to run the flusher *immediately*. We've
84 : // got to be on the UI main thread for us to be able to do
85 : // 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 : }
90 : }
91 :
92 0 : bool lastVal = sIsFlushing.exchange(true);
93 0 : if (lastVal) {
94 0 : return NS_OK;
95 : }
96 :
97 0 : PRIntervalTime now = PR_IntervalNow();
98 :
99 : // Run the flushers immediately if we can; otherwise, proxy to the
100 : // UI thread an run 'em asynchronously.
101 0 : if (aImmediate) {
102 0 : rv = RunFlushers(aReason);
103 : } else {
104 : // 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 : }
109 : }
110 :
111 0 : sLastFlushTime = now;
112 0 : return rv;
113 : }
114 :
115 : nsresult
116 0 : nsMemoryImpl::RunFlushers(const char16_t* aReason)
117 : {
118 0 : nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
119 0 : if (os) {
120 :
121 : // Instead of:
122 : // os->NotifyObservers(this, "memory-pressure", aReason);
123 : // we are going to do this manually to see who/what is
124 : // deallocating.
125 :
126 0 : nsCOMPtr<nsISimpleEnumerator> e;
127 0 : os->EnumerateObservers("memory-pressure", getter_AddRefs(e));
128 :
129 0 : if (e) {
130 0 : nsCOMPtr<nsIObserver> observer;
131 0 : bool loop = true;
132 :
133 0 : while (NS_SUCCEEDED(e->HasMoreElements(&loop)) && loop) {
134 0 : nsCOMPtr<nsISupports> supports;
135 0 : e->GetNext(getter_AddRefs(supports));
136 :
137 0 : if (!supports) {
138 0 : continue;
139 : }
140 :
141 0 : observer = do_QueryInterface(supports);
142 0 : observer->Observe(observer, "memory-pressure", aReason);
143 : }
144 : }
145 : }
146 :
147 0 : sIsFlushing = false;
148 0 : return NS_OK;
149 : }
150 :
151 : // XXX need NS_IMPL_STATIC_ADDREF/RELEASE
152 : NS_IMETHODIMP_(MozExternalRefCountType)
153 0 : nsMemoryImpl::FlushEvent::AddRef()
154 : {
155 0 : return 2;
156 : }
157 : NS_IMETHODIMP_(MozExternalRefCountType)
158 0 : nsMemoryImpl::FlushEvent::Release()
159 : {
160 0 : return 1;
161 : }
162 0 : NS_IMPL_QUERY_INTERFACE(nsMemoryImpl::FlushEvent, nsIRunnable)
163 :
164 : NS_IMETHODIMP
165 0 : nsMemoryImpl::FlushEvent::Run()
166 : {
167 0 : sGlobalMemory.RunFlushers(mReason);
168 0 : return NS_OK;
169 : }
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 0 : NS_GetMemoryManager(nsIMemory** aResult)
182 : {
183 0 : return sGlobalMemory.QueryInterface(NS_GET_IID(nsIMemory), (void**)aResult);
184 : }
|