Back to index

lightning-sunbird  0.9+nobinonly
nsMemoryImpl.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either of the GNU General Public License Version 2 or later (the "GPL"),
00026  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 #include "nsXPCOM.h"
00039 #include "nsMemoryImpl.h"
00040 
00041 #include "nsIEventQueueService.h"
00042 #include "nsIObserverService.h"
00043 #include "nsIRunnable.h"
00044 #include "nsIServiceManager.h"
00045 #include "nsISupportsArray.h"
00046 #include "nsIThread.h"
00047 
00048 #include "prmem.h"
00049 #include "plevent.h"
00050 
00051 #include "nsAlgorithm.h"
00052 #include "nsAutoLock.h"
00053 #include "nsCOMPtr.h"
00054 #include "nsString.h"
00055 
00056 #if defined(XP_WIN) && !defined(WINCE)
00057 #include <windows.h>
00058 #define NS_MEMORY_FLUSHER_THREAD
00059 #else
00060 // Need to implement the nsIMemory::IsLowMemory() predicate
00061 #undef NS_MEMORY_FLUSHER_THREAD
00062 #endif
00063 
00065 // Define NS_OUT_OF_MEMORY_TESTER if you want to force memory failures
00066 
00067 #ifdef DEBUG_xwarren
00068 #define NS_OUT_OF_MEMORY_TESTER
00069 #endif
00070 
00071 #ifdef NS_OUT_OF_MEMORY_TESTER
00072 
00073 // flush memory one in this number of times:
00074 #define NS_FLUSH_FREQUENCY        100000
00075 
00076 // fail allocation one in this number of flushes:
00077 #define NS_FAIL_FREQUENCY         10
00078 
00079 PRUint32 gFlushFreq = 0;
00080 PRUint32 gFailFreq = 0;
00081 
00082 static void*
00083 mallocator(PRSize size, PRUint32& counter, PRUint32 max)
00084 {
00085     if (counter++ >= max) {
00086         counter = 0;
00087         NS_ASSERTION(0, "about to fail allocation... watch out");
00088         return nsnull;
00089     }
00090     return PR_Malloc(size);
00091 }
00092 
00093 static void*
00094 reallocator(void* ptr, PRSize size, PRUint32& counter, PRUint32 max)
00095 {
00096     if (counter++ >= max) {
00097         counter = 0;
00098         NS_ASSERTION(0, "about to fail reallocation... watch out");
00099         return nsnull;
00100     }
00101     return PR_Realloc(ptr, size);
00102 }
00103 
00104 #define MALLOC1(s)       mallocator(s, gFlushFreq, NS_FLUSH_FREQUENCY)
00105 #define REALLOC1(p, s)   reallocator(p, s, gFlushFreq, NS_FLUSH_FREQUENCY)
00106 
00107 #else
00108 
00109 #define MALLOC1(s)       PR_Malloc(s)
00110 #define REALLOC1(p, s)   PR_Realloc(p, s)
00111 
00112 #endif // NS_OUT_OF_MEMORY_TESTER
00113 
00114 #if defined(XDEBUG_waterson)
00115 #define NS_TEST_MEMORY_FLUSHER
00116 #endif
00117 
00118 #ifdef NS_MEMORY_FLUSHER_THREAD
00119 
00125 class MemoryFlusher : public nsIRunnable
00126 {
00127 public:
00128     // We don't use the generic macros because we are a special static object
00129     NS_IMETHOD QueryInterface(REFNSIID aIID, void** aResult);
00130     NS_IMETHOD_(nsrefcnt) AddRef(void) { return 1; }
00131     NS_IMETHOD_(nsrefcnt) Release(void) { return 1; }
00132 
00133     NS_DECL_NSIRUNNABLE
00134 
00135     nsresult Init();
00136     void StopAndJoin();
00137 
00138 private:
00139     static PRBool         sRunning;
00140     static PRIntervalTime sTimeout;
00141     static PRLock*        sLock;
00142     static PRCondVar*     sCVar;
00143     static nsIThread*     sThread;
00144     
00145     enum {
00146         kInitialTimeout = 60 /*seconds*/
00147     };
00148 };
00149 
00150 static MemoryFlusher sGlobalMemoryFlusher;
00151 
00152 #endif // NS_MEMORY_FLUSHER_THREAD
00153 
00154 static nsMemoryImpl sGlobalMemory;
00155 
00156 NS_IMPL_QUERY_INTERFACE1(nsMemoryImpl, nsIMemory)
00157 
00158 NS_IMETHODIMP_(void*)
00159 nsMemoryImpl::Alloc(PRSize size)
00160 {
00161     return NS_Alloc(size);
00162 }
00163 
00164 NS_IMETHODIMP_(void*)
00165 nsMemoryImpl::Realloc(void* ptr, PRSize size)
00166 {
00167     return NS_Realloc(ptr, size);
00168 }
00169 
00170 NS_IMETHODIMP_(void)
00171 nsMemoryImpl::Free(void* ptr)
00172 {
00173     NS_Free(ptr);
00174 }
00175 
00176 NS_IMETHODIMP
00177 nsMemoryImpl::HeapMinimize(PRBool aImmediate)
00178 {
00179     return FlushMemory(NS_LITERAL_STRING("heap-minimize").get(), aImmediate);
00180 }
00181 
00182 NS_IMETHODIMP
00183 nsMemoryImpl::IsLowMemory(PRBool *result)
00184 {
00185 #if defined(WINCE)
00186     MEMORYSTATUS stat;
00187     GlobalMemoryStatus(&stat);
00188     *result = ((float)stat.dwAvailPhys / stat.dwTotalPhys) < 0.1;
00189 #elif defined(XP_WIN)
00190     MEMORYSTATUS stat;
00191     GlobalMemoryStatus(&stat);
00192     *result = ((float)stat.dwAvailPageFile / stat.dwTotalPageFile) < 0.1;
00193 #else
00194     *result = PR_FALSE;
00195 #endif
00196     return NS_OK;
00197 }
00198 
00199 
00200 nsresult 
00201 nsMemoryImpl::Startup()
00202 {
00203     sFlushLock = PR_NewLock();
00204     if (!sFlushLock) return NS_ERROR_FAILURE;
00205 
00206 #ifdef NS_MEMORY_FLUSHER_THREAD
00207     return sGlobalMemoryFlusher.Init();
00208 #else
00209     return NS_OK;
00210 #endif
00211 }
00212 
00213 void
00214 nsMemoryImpl::Shutdown()
00215 {
00216 #ifdef NS_MEMORY_FLUSHER_THREAD
00217     sGlobalMemoryFlusher.StopAndJoin();
00218 #endif
00219 
00220     if (sFlushLock) PR_DestroyLock(sFlushLock);
00221     sFlushLock = nsnull;
00222 }
00223 
00224 nsresult
00225 nsMemoryImpl::Create(nsISupports* outer, const nsIID& aIID, void **aResult)
00226 {
00227     NS_ENSURE_NO_AGGREGATION(outer);
00228     return sGlobalMemory.QueryInterface(aIID, aResult);
00229 }
00230 
00231 nsresult
00232 nsMemoryImpl::FlushMemory(const PRUnichar* aReason, PRBool aImmediate)
00233 {
00234     nsresult rv;
00235 
00236     if (aImmediate) {
00237         // They've asked us to run the flusher *immediately*. We've
00238         // got to be on the UI main thread for us to be able to do
00239         // that...are we?
00240         PRBool isOnUIThread = PR_FALSE;
00241 
00242         nsCOMPtr<nsIThread> main;
00243         rv = nsIThread::GetMainThread(getter_AddRefs(main));
00244         if (NS_SUCCEEDED(rv)) {
00245             nsCOMPtr<nsIThread> current;
00246             rv = nsIThread::GetCurrent(getter_AddRefs(current));
00247             if (NS_SUCCEEDED(rv)) {
00248                 if (current == main)
00249                     isOnUIThread = PR_TRUE;
00250             }
00251         }
00252 
00253         if (! isOnUIThread) {
00254             NS_ERROR("can't synchronously flush memory: not on UI thread");
00255             return NS_ERROR_FAILURE;
00256         }
00257     }
00258 
00259     {
00260         // Are we already flushing?
00261         nsAutoLock l(sFlushLock);
00262         if (sIsFlushing)
00263             return NS_OK;
00264 
00265         // Well, we are now!
00266         sIsFlushing = PR_TRUE;
00267     }
00268 
00269     // Run the flushers immediately if we can; otherwise, proxy to the
00270     // UI thread an run 'em asynchronously.
00271     if (aImmediate) {
00272         rv = RunFlushers(aReason);
00273     }
00274     else {
00275         nsCOMPtr<nsIEventQueueService> eqs = do_GetService(NS_EVENTQUEUESERVICE_CONTRACTID, &rv);
00276         if (eqs) {
00277             nsCOMPtr<nsIEventQueue> eq;
00278             rv = eqs->GetThreadEventQueue(NS_UI_THREAD, getter_AddRefs(eq));
00279             if (NS_SUCCEEDED(rv)) {
00280                 PL_InitEvent(&sFlushEvent.mEvent, this, HandleFlushEvent, DestroyFlushEvent);
00281                 sFlushEvent.mReason = aReason;
00282 
00283                 rv = eq->PostEvent(NS_REINTERPRET_CAST(PLEvent*, &sFlushEvent));
00284             }
00285         }
00286     }
00287 
00288     return rv;
00289 }
00290 
00291 nsresult
00292 nsMemoryImpl::RunFlushers(const PRUnichar* aReason)
00293 {
00294     nsCOMPtr<nsIObserverService> os = do_GetService("@mozilla.org/observer-service;1");
00295     if (os) {
00296         os->NotifyObservers(this, "memory-pressure", aReason);
00297     }
00298 
00299     {
00300         // Done flushing
00301         nsAutoLock l(sFlushLock);
00302         sIsFlushing = PR_FALSE;
00303     }
00304 
00305     return NS_OK;
00306 }
00307 
00308 void*
00309 nsMemoryImpl::HandleFlushEvent(PLEvent* aEvent)
00310 {
00311     FlushEvent* event = NS_REINTERPRET_CAST(FlushEvent*, aEvent);
00312 
00313     sGlobalMemory.RunFlushers(event->mReason);
00314     return 0;
00315 }
00316 
00317 void
00318 nsMemoryImpl::DestroyFlushEvent(PLEvent* aEvent)
00319 {
00320     // no-op, since mEvent is a member of nsMemoryImpl
00321 }
00322 
00323 PRLock*
00324 nsMemoryImpl::sFlushLock;
00325 
00326 PRBool
00327 nsMemoryImpl::sIsFlushing = PR_FALSE;
00328 
00329 nsMemoryImpl::FlushEvent
00330 nsMemoryImpl::sFlushEvent;
00331 
00332 extern "C" NS_EXPORT void*
00333 NS_Alloc(PRSize size)
00334 {
00335     NS_ASSERTION(size, "NS_Alloc of size 0");
00336 
00337     void* result = MALLOC1(size);
00338     if (! result) {
00339         // Request an asynchronous flush
00340         sGlobalMemory.FlushMemory(NS_LITERAL_STRING("alloc-failure").get(), PR_FALSE);
00341     }
00342     return result;
00343 }
00344 
00345 extern "C" NS_EXPORT void*
00346 NS_Realloc(void* ptr, PRSize size)
00347 {
00348     NS_ASSERTION(size, "NS_Realloc of size 0");
00349     void* result = REALLOC1(ptr, size);
00350     if (! result) {
00351         // Request an asynchronous flush
00352         sGlobalMemory.FlushMemory(NS_LITERAL_STRING("alloc-failure").get(), PR_FALSE);
00353     }
00354     return result;
00355 }
00356 
00357 extern "C" NS_EXPORT void
00358 NS_Free(void* ptr)
00359 {
00360     PR_Free(ptr);
00361 }
00362 
00363 #ifdef NS_MEMORY_FLUSHER_THREAD
00364 
00365 NS_IMPL_QUERY_INTERFACE1(MemoryFlusher, nsIRunnable)
00366 
00367 NS_IMETHODIMP
00368 MemoryFlusher::Run()
00369 {
00370     nsresult rv;
00371 
00372     sRunning = PR_TRUE;
00373 
00374     while (1) {
00375         PRStatus status;
00376 
00377         {
00378             nsAutoLock l(sLock);
00379             if (! sRunning) {
00380                 rv = NS_OK;
00381                 break;
00382             }
00383 
00384             status = PR_WaitCondVar(sCVar, sTimeout);
00385         }
00386 
00387         if (status != PR_SUCCESS) {
00388             rv = NS_ERROR_FAILURE;
00389             break;
00390         }
00391 
00392         if (! sRunning) {
00393             rv = NS_OK;
00394             break;
00395         }
00396 
00397         PRBool isLowMemory;
00398         rv = sGlobalMemory.IsLowMemory(&isLowMemory);
00399         if (NS_FAILED(rv))
00400             break;
00401 
00402 #ifdef NS_TEST_MEMORY_FLUSHER
00403         // Fire the flusher *every* time
00404         isLowMemory = PR_TRUE;
00405 #endif
00406 
00407         if (isLowMemory) {
00408             sGlobalMemory.FlushMemory(NS_LITERAL_STRING("low-memory").get(), PR_FALSE);
00409         }
00410     }
00411 
00412     sRunning = PR_FALSE;
00413 
00414     return rv;
00415 }
00416 
00417 nsresult
00418 MemoryFlusher::Init()
00419 {
00420     sTimeout = PR_SecondsToInterval(kInitialTimeout);
00421     sLock = PR_NewLock();
00422     if (!sLock) return NS_ERROR_FAILURE;
00423 
00424     sCVar = PR_NewCondVar(sLock);
00425     if (!sCVar) return NS_ERROR_FAILURE;
00426 
00427     return NS_NewThread(&sThread,
00428                         this,
00429                         0, /* XXX use default stack size? */
00430                         PR_JOINABLE_THREAD);
00431 }
00432 
00433 void
00434 MemoryFlusher::StopAndJoin()
00435 {
00436     if (sRunning) {
00437         {
00438             nsAutoLock l(sLock);
00439             sRunning = PR_FALSE;
00440             PR_NotifyCondVar(sCVar);
00441         }
00442 
00443         if (sThread)
00444             sThread->Join();
00445     }
00446 
00447     NS_IF_RELEASE(sThread);
00448 
00449     if (sLock)
00450         PR_DestroyLock(sLock);
00451 
00452     if (sCVar)
00453         PR_DestroyCondVar(sCVar);
00454 }
00455 
00456 
00457 PRBool
00458 MemoryFlusher::sRunning;
00459 
00460 PRIntervalTime
00461 MemoryFlusher::sTimeout;
00462 
00463 PRLock*
00464 MemoryFlusher::sLock;
00465 
00466 PRCondVar*
00467 MemoryFlusher::sCVar;
00468 
00469 nsIThread*
00470 MemoryFlusher::sThread;
00471 
00472 #endif // NS_MEMORY_FLUSHER_THREAD
00473 
00474 nsresult
00475 NS_GetMemoryManager(nsIMemory* *result)
00476 {
00477     return sGlobalMemory.QueryInterface(NS_GET_IID(nsIMemory), (void**) result);
00478 }