VirtualBox

source: vbox/trunk/src/libs/xpcom18a4/xpcom/base/nsMemoryImpl.cpp@ 43213

Last change on this file since 43213 was 1, checked in by vboxsync, 55 years ago

import

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 13.6 KB
Line 
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 *
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
9 *
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
14 *
15 * The Original Code is mozilla.org code.
16 *
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998
20 * the Initial Developer. All Rights Reserved.
21 *
22 * Contributor(s):
23 *
24 * Alternatively, the contents of this file may be used under the terms of
25 * either of the GNU General Public License Version 2 or later (the "GPL"),
26 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 * in which case the provisions of the GPL or the LGPL are applicable instead
28 * of those above. If you wish to allow use of your version of this file only
29 * under the terms of either the GPL or the LGPL, and not to allow others to
30 * use your version of this file under the terms of the MPL, indicate your
31 * decision by deleting the provisions above and replace them with the notice
32 * and other provisions required by the GPL or the LGPL. If you do not delete
33 * the provisions above, a recipient may use your version of this file under
34 * the terms of any one of the MPL, the GPL or the LGPL.
35 *
36 * ***** END LICENSE BLOCK ***** */
37
38#include "nsMemoryImpl.h"
39#include "prmem.h"
40#include "nsAlgorithm.h"
41#include "nsIServiceManager.h"
42#include "nsIObserverService.h"
43#include "nsAutoLock.h"
44#include "nsIThread.h"
45#include "nsIEventQueueService.h"
46#include "nsString.h"
47
48#if defined(XP_WIN)
49#include <windows.h>
50#define NS_MEMORY_FLUSHER_THREAD
51#elif defined(XP_MAC)
52#include <MacMemory.h>
53#define NS_MEMORY_FLUSHER_THREAD
54#else
55// Need to implement the nsIMemory::IsLowMemory() predicate
56#undef NS_MEMORY_FLUSHER_THREAD
57#endif
58
59//----------------------------------------------------------------------
60
61#if defined(XDEBUG_waterson)
62#define NS_TEST_MEMORY_FLUSHER
63#endif
64
65/**
66 * A runnable that is used to periodically check the status
67 * of the system, determine if too much memory is in use,
68 * and if so, trigger a "memory flush".
69 */
70class MemoryFlusher : public nsIRunnable
71{
72protected:
73 nsMemoryImpl* mMemoryImpl; // WEAK, it owns us.
74 PRBool mRunning;
75 PRIntervalTime mTimeout;
76 PRLock* mLock;
77 PRCondVar* mCVar;
78
79 MemoryFlusher(nsMemoryImpl* aMemoryImpl);
80
81 enum {
82 kInitialTimeout = 60 /*seconds*/
83 };
84
85private:
86 ~MemoryFlusher();
87
88public:
89 /**
90 * Create a memory flusher.
91 * @param aResult the memory flusher
92 * @param aMemoryImpl the owning nsMemoryImpl object
93 * @return NS_OK if the memory flusher was created successfully
94 */
95 static nsresult
96 Create(MemoryFlusher** aResult, nsMemoryImpl* aMemoryImpl);
97
98 NS_DECL_ISUPPORTS
99 NS_DECL_NSIRUNNABLE
100
101 /**
102 * Stop the memory flusher.
103 */
104 nsresult Stop();
105};
106
107
108MemoryFlusher::MemoryFlusher(nsMemoryImpl* aMemoryImpl)
109 : mMemoryImpl(aMemoryImpl),
110 mRunning(PR_FALSE),
111 mTimeout(PR_SecondsToInterval(kInitialTimeout)),
112 mLock(nsnull),
113 mCVar(nsnull)
114{
115}
116
117MemoryFlusher::~MemoryFlusher()
118{
119 if (mLock)
120 PR_DestroyLock(mLock);
121
122 if (mCVar)
123 PR_DestroyCondVar(mCVar);
124}
125
126
127nsresult
128MemoryFlusher::Create(MemoryFlusher** aResult, nsMemoryImpl* aMemoryImpl)
129{
130 MemoryFlusher* result = new MemoryFlusher(aMemoryImpl);
131 if (! result)
132 return NS_ERROR_OUT_OF_MEMORY;
133
134 do {
135 if ((result->mLock = PR_NewLock()) == nsnull)
136 break;
137
138 if ((result->mCVar = PR_NewCondVar(result->mLock)) == nsnull)
139 break;
140
141 NS_ADDREF(*aResult = result);
142 return NS_OK;
143 } while (0);
144
145 // Something bad happened if we get here...
146 delete result;
147 return NS_ERROR_OUT_OF_MEMORY;
148}
149
150NS_IMPL_THREADSAFE_ISUPPORTS1(MemoryFlusher, nsIRunnable)
151
152NS_IMETHODIMP
153MemoryFlusher::Run()
154{
155 nsresult rv;
156
157 mRunning = PR_TRUE;
158
159 while (1) {
160 PRStatus status;
161
162 {
163 nsAutoLock l(mLock);
164 if (! mRunning) {
165 rv = NS_OK;
166 break;
167 }
168
169 status = PR_WaitCondVar(mCVar, mTimeout);
170 }
171
172 if (status != PR_SUCCESS) {
173 rv = NS_ERROR_FAILURE;
174 break;
175 }
176
177 if (! mRunning) {
178 rv = NS_OK;
179 break;
180 }
181
182 PRBool isLowMemory;
183 rv = mMemoryImpl->IsLowMemory(&isLowMemory);
184 if (NS_FAILED(rv))
185 break;
186
187#ifdef NS_TEST_MEMORY_FLUSHER
188 // Fire the flusher *every* time
189 isLowMemory = PR_TRUE;
190#endif
191
192 if (isLowMemory) {
193 mMemoryImpl->FlushMemory(NS_LITERAL_STRING("low-memory").get(), PR_FALSE);
194 }
195 }
196
197 mRunning = PR_FALSE;
198
199 return rv;
200}
201
202
203nsresult
204MemoryFlusher::Stop()
205{
206 if (mRunning) {
207 nsAutoLock l(mLock);
208 mRunning = PR_FALSE;
209 PR_NotifyCondVar(mCVar);
210 }
211
212 return NS_OK;
213}
214
215//----------------------------------------------------------------------
216
217nsMemoryImpl* gMemory = nsnull;
218
219NS_IMPL_THREADSAFE_ISUPPORTS1(nsMemoryImpl, nsIMemory)
220
221NS_METHOD
222nsMemoryImpl::Create(nsISupports* outer, const nsIID& aIID, void* *aInstancePtr)
223{
224 NS_ENSURE_ARG_POINTER(aInstancePtr);
225 NS_ENSURE_PROPER_AGGREGATION(outer, aIID);
226 if (gMemory && NS_SUCCEEDED(gMemory->QueryInterface(aIID, aInstancePtr)))
227 return NS_OK;
228
229 nsMemoryImpl* mm = new nsMemoryImpl();
230 if (mm == NULL)
231 return NS_ERROR_OUT_OF_MEMORY;
232
233 nsresult rv;
234
235 do {
236 rv = mm->QueryInterface(aIID, aInstancePtr);
237 if (NS_FAILED(rv))
238 break;
239
240 rv = NS_ERROR_OUT_OF_MEMORY;
241
242 mm->mFlushLock = PR_NewLock();
243 if (! mm->mFlushLock)
244 break;
245
246 rv = NS_OK;
247 } while (0);
248
249 if (NS_FAILED(rv))
250 delete mm;
251
252 return rv;
253}
254
255
256nsMemoryImpl::nsMemoryImpl()
257 : mFlusher(nsnull),
258 mFlushLock(nsnull),
259 mIsFlushing(PR_FALSE)
260{
261}
262
263nsMemoryImpl::~nsMemoryImpl()
264{
265 if (mFlushLock)
266 PR_DestroyLock(mFlushLock);
267}
268
269////////////////////////////////////////////////////////////////////////////////
270// Define NS_OUT_OF_MEMORY_TESTER if you want to force memory failures
271
272#ifdef DEBUG_xwarren
273#define NS_OUT_OF_MEMORY_TESTER
274#endif
275
276#ifdef NS_OUT_OF_MEMORY_TESTER
277
278// flush memory one in this number of times:
279#define NS_FLUSH_FREQUENCY 100000
280
281// fail allocation one in this number of flushes:
282#define NS_FAIL_FREQUENCY 10
283
284PRUint32 gFlushFreq = 0;
285PRUint32 gFailFreq = 0;
286
287static void*
288mallocator(PRSize size, PRUint32& counter, PRUint32 max)
289{
290 if (counter++ >= max) {
291 counter = 0;
292 NS_ASSERTION(0, "about to fail allocation... watch out");
293 return nsnull;
294 }
295 return PR_Malloc(size);
296}
297
298static void*
299reallocator(void* ptr, PRSize size, PRUint32& counter, PRUint32 max)
300{
301 if (counter++ >= max) {
302 counter = 0;
303 NS_ASSERTION(0, "about to fail reallocation... watch out");
304 return nsnull;
305 }
306 return PR_Realloc(ptr, size);
307}
308
309#define MALLOC1(s) mallocator(s, gFlushFreq, NS_FLUSH_FREQUENCY)
310#define REALLOC1(p, s) reallocator(p, s, gFlushFreq, NS_FLUSH_FREQUENCY)
311
312#else
313
314#define MALLOC1(s) PR_Malloc(s)
315#define REALLOC1(p, s) PR_Realloc(p, s)
316
317#endif // NS_OUT_OF_MEMORY_TESTER
318
319////////////////////////////////////////////////////////////////////////////////
320
321NS_IMETHODIMP_(void *)
322nsMemoryImpl::Alloc(PRSize size)
323{
324 NS_ASSERTION(size, "nsMemoryImpl::Alloc of 0");
325 void* result = MALLOC1(size);
326 if (! result) {
327 // Request an asynchronous flush
328 FlushMemory(NS_LITERAL_STRING("alloc-failure").get(), PR_FALSE);
329 }
330 return result;
331}
332
333NS_IMETHODIMP_(void *)
334nsMemoryImpl::Realloc(void * ptr, PRSize size)
335{
336 void* result = REALLOC1(ptr, size);
337 if (! result) {
338 // Request an asynchronous flush
339 FlushMemory(NS_LITERAL_STRING("alloc-failure").get(), PR_FALSE);
340 }
341 return result;
342}
343
344NS_IMETHODIMP_(void)
345nsMemoryImpl::Free(void * ptr)
346{
347 PR_Free(ptr);
348}
349
350NS_IMETHODIMP
351nsMemoryImpl::HeapMinimize(PRBool aImmediate)
352{
353 return FlushMemory(NS_LITERAL_STRING("heap-minimize").get(), aImmediate);
354}
355
356NS_IMETHODIMP
357nsMemoryImpl::IsLowMemory(PRBool *result)
358{
359#if defined(XP_WIN)
360 MEMORYSTATUS stat;
361 GlobalMemoryStatus(&stat);
362 *result = ((float)stat.dwAvailPageFile / stat.dwTotalPageFile) < 0.1;
363#elif defined(XP_MAC)
364
365 const long kReserveHeapFreeSpace = (256 * 1024);
366 const long kReserveHeapContigSpace = (128 * 1024);
367
368 long totalSpace, contiguousSpace;
369 // this call measures how much memory would be available if the OS
370 // purged. Despite the name, it does not purge (that happens
371 // automatically when heap space is low).
372 ::PurgeSpace(&totalSpace, &contiguousSpace);
373 if (totalSpace < kReserveHeapFreeSpace || contiguousSpace < kReserveHeapContigSpace)
374 {
375 NS_WARNING("Found that heap mem is low");
376 *result = PR_TRUE;
377 return NS_OK;
378 }
379
380 // see how much temp mem is available (since our allocators allocate 1Mb chunks
381 // in temp mem. We don't use TempMaxMem() (to get contig space) here, because it
382 // compacts the application heap, so can be slow.
383 const long kReserveTempFreeSpace = (2 * 1024 * 1024); // 2Mb
384 long totalTempSpace = ::TempFreeMem();
385 if (totalTempSpace < kReserveTempFreeSpace)
386 {
387 NS_WARNING("Found that temp mem is low");
388 *result = PR_TRUE;
389 return NS_OK;
390 }
391
392 *result = PR_FALSE;
393
394#else
395 *result = PR_FALSE;
396#endif
397 return NS_OK;
398}
399
400nsresult
401nsMemoryImpl::FlushMemory(const PRUnichar* aReason, PRBool aImmediate)
402{
403 nsresult rv;
404
405 if (aImmediate) {
406 // They've asked us to run the flusher *immediately*. We've
407 // got to be on the UI main thread for us to be able to do
408 // that...are we?
409 PRBool isOnUIThread = PR_FALSE;
410
411 nsCOMPtr<nsIThread> main;
412 rv = nsIThread::GetMainThread(getter_AddRefs(main));
413 if (NS_SUCCEEDED(rv)) {
414 nsCOMPtr<nsIThread> current;
415 rv = nsIThread::GetCurrent(getter_AddRefs(current));
416 if (NS_SUCCEEDED(rv)) {
417 if (current == main)
418 isOnUIThread = PR_TRUE;
419 }
420 }
421
422 if (! isOnUIThread) {
423 NS_ERROR("can't synchronously flush memory: not on UI thread");
424 return NS_ERROR_FAILURE;
425 }
426 }
427
428 {
429 // Are we already flushing?
430 nsAutoLock l(mFlushLock);
431 if (mIsFlushing)
432 return NS_OK;
433
434 // Well, we are now!
435 mIsFlushing = PR_TRUE;
436 }
437
438 // Run the flushers immediately if we can; otherwise, proxy to the
439 // UI thread an run 'em asynchronously.
440 if (aImmediate) {
441 rv = RunFlushers(this, aReason);
442 }
443 else {
444 nsCOMPtr<nsIEventQueueService> eqs = do_GetService(NS_EVENTQUEUESERVICE_CONTRACTID, &rv);
445 if (eqs) {
446 nsCOMPtr<nsIEventQueue> eq;
447 rv = eqs->GetThreadEventQueue(NS_UI_THREAD, getter_AddRefs(eq));
448 if (NS_SUCCEEDED(rv)) {
449 PL_InitEvent(&mFlushEvent.mEvent, this, HandleFlushEvent, DestroyFlushEvent);
450 mFlushEvent.mReason = aReason;
451
452 rv = eq->PostEvent(NS_REINTERPRET_CAST(PLEvent*, &mFlushEvent));
453 }
454 }
455 }
456
457 return rv;
458}
459
460nsresult
461nsMemoryImpl::RunFlushers(nsMemoryImpl* aSelf, const PRUnichar* aReason)
462{
463 nsCOMPtr<nsIObserverService> os = do_GetService("@mozilla.org/observer-service;1");
464 if (os) {
465 os->NotifyObservers(aSelf, "memory-pressure", aReason);
466 }
467
468 {
469 // Done flushing
470 nsAutoLock l(aSelf->mFlushLock);
471 aSelf->mIsFlushing = PR_FALSE;
472 }
473
474 return NS_OK;
475}
476
477void*
478nsMemoryImpl::HandleFlushEvent(PLEvent* aEvent)
479{
480 nsMemoryImpl* self = NS_STATIC_CAST(nsMemoryImpl*, PL_GetEventOwner(aEvent));
481 FlushEvent* event = NS_REINTERPRET_CAST(FlushEvent*, aEvent);
482
483 RunFlushers(self, event->mReason);
484 return 0;
485}
486
487void
488nsMemoryImpl::DestroyFlushEvent(PLEvent* aEvent)
489{
490 // no-op, since mEvent is a member of nsMemoryImpl
491}
492
493static void
494EnsureGlobalMemoryService()
495{
496 if (gMemory) return;
497 nsresult rv = nsMemoryImpl::Create(nsnull, NS_GET_IID(nsIMemory), (void**)&gMemory);
498 NS_ASSERTION(NS_SUCCEEDED(rv), "nsMemoryImpl::Create failed");
499 NS_ASSERTION(gMemory, "improper xpcom initialization");
500}
501
502nsresult
503nsMemoryImpl::Startup()
504{
505 EnsureGlobalMemoryService();
506 if (! gMemory)
507 return NS_ERROR_FAILURE;
508
509#ifdef NS_MEMORY_FLUSHER_THREAD
510 nsresult rv;
511
512 // Create and start a memory flusher thread
513 rv = MemoryFlusher::Create(&gMemory->mFlusher, gMemory);
514 if (NS_FAILED(rv)) return rv;
515
516 rv = NS_NewThread(getter_AddRefs(gMemory->mFlusherThread),
517 gMemory->mFlusher,
518 0, /* XXX use default stack size? */
519 PR_JOINABLE_THREAD);
520
521 if (NS_FAILED(rv)) return rv;
522#endif
523
524 return NS_OK;
525}
526
527nsresult
528nsMemoryImpl::Shutdown()
529{
530 if (gMemory) {
531#ifdef NS_MEMORY_FLUSHER_THREAD
532 if (gMemory->mFlusher) {
533 // Stop the runnable...
534 gMemory->mFlusher->Stop();
535 NS_RELEASE(gMemory->mFlusher);
536
537 // ...and wait for the thread to exit
538 if (gMemory->mFlusherThread)
539 gMemory->mFlusherThread->Join();
540 }
541#endif
542
543 NS_RELEASE(gMemory);
544 gMemory = nsnull;
545 }
546
547 return NS_OK;
548}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette