Changeset 101848 in vbox
- Timestamp:
- Nov 6, 2023 9:44:58 AM (16 months ago)
- svn:sync-xref-src-repo-rev:
- 159944
- Location:
- trunk/src/libs/xpcom18a4/xpcom/base
- Files:
-
- 2 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/libs/xpcom18a4/xpcom/base/nsMemoryImpl.cpp
r1 r101848 42 42 #include "nsIObserverService.h" 43 43 #include "nsAutoLock.h" 44 #include "nsIThread.h"45 44 #include "nsIEventQueueService.h" 46 45 #include "nsString.h" 47 48 #if defined(XP_WIN)49 #include <windows.h>50 #define NS_MEMORY_FLUSHER_THREAD51 #elif defined(XP_MAC)52 #include <MacMemory.h>53 #define NS_MEMORY_FLUSHER_THREAD54 #else55 // Need to implement the nsIMemory::IsLowMemory() predicate56 #undef NS_MEMORY_FLUSHER_THREAD57 #endif58 59 //----------------------------------------------------------------------60 61 #if defined(XDEBUG_waterson)62 #define NS_TEST_MEMORY_FLUSHER63 #endif64 65 /**66 * A runnable that is used to periodically check the status67 * of the system, determine if too much memory is in use,68 * and if so, trigger a "memory flush".69 */70 class MemoryFlusher : public nsIRunnable71 {72 protected: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 85 private:86 ~MemoryFlusher();87 88 public:89 /**90 * Create a memory flusher.91 * @param aResult the memory flusher92 * @param aMemoryImpl the owning nsMemoryImpl object93 * @return NS_OK if the memory flusher was created successfully94 */95 static nsresult96 Create(MemoryFlusher** aResult, nsMemoryImpl* aMemoryImpl);97 98 NS_DECL_ISUPPORTS99 NS_DECL_NSIRUNNABLE100 101 /**102 * Stop the memory flusher.103 */104 nsresult Stop();105 };106 107 108 MemoryFlusher::MemoryFlusher(nsMemoryImpl* aMemoryImpl)109 : mMemoryImpl(aMemoryImpl),110 mRunning(PR_FALSE),111 mTimeout(PR_SecondsToInterval(kInitialTimeout)),112 mLock(nsnull),113 mCVar(nsnull)114 {115 }116 117 MemoryFlusher::~MemoryFlusher()118 {119 if (mLock)120 PR_DestroyLock(mLock);121 122 if (mCVar)123 PR_DestroyCondVar(mCVar);124 }125 126 127 nsresult128 MemoryFlusher::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 150 NS_IMPL_THREADSAFE_ISUPPORTS1(MemoryFlusher, nsIRunnable)151 152 NS_IMETHODIMP153 MemoryFlusher::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_FLUSHER188 // Fire the flusher *every* time189 isLowMemory = PR_TRUE;190 #endif191 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 203 nsresult204 MemoryFlusher::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 46 217 47 nsMemoryImpl* gMemory = nsnull; … … 238 68 break; 239 69 240 rv = NS_ERROR_OUT_OF_MEMORY;241 242 mm->mFlushLock = PR_NewLock();243 if (! mm->mFlushLock)244 break;245 246 70 rv = NS_OK; 247 71 } while (0); … … 255 79 256 80 nsMemoryImpl::nsMemoryImpl() 257 : mFlusher(nsnull),258 mFlushLock(nsnull),259 mIsFlushing(PR_FALSE)260 81 { 261 82 } … … 263 84 nsMemoryImpl::~nsMemoryImpl() 264 85 { 265 if (mFlushLock)266 PR_DestroyLock(mFlushLock);267 86 } 268 269 ////////////////////////////////////////////////////////////////////////////////270 // Define NS_OUT_OF_MEMORY_TESTER if you want to force memory failures271 272 #ifdef DEBUG_xwarren273 #define NS_OUT_OF_MEMORY_TESTER274 #endif275 276 #ifdef NS_OUT_OF_MEMORY_TESTER277 278 // flush memory one in this number of times:279 #define NS_FLUSH_FREQUENCY 100000280 281 // fail allocation one in this number of flushes:282 #define NS_FAIL_FREQUENCY 10283 284 PRUint32 gFlushFreq = 0;285 PRUint32 gFailFreq = 0;286 287 static void*288 mallocator(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 298 static void*299 reallocator(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 #else313 314 #define MALLOC1(s) PR_Malloc(s)315 #define REALLOC1(p, s) PR_Realloc(p, s)316 317 #endif // NS_OUT_OF_MEMORY_TESTER318 87 319 88 //////////////////////////////////////////////////////////////////////////////// … … 323 92 { 324 93 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 } 94 void* result = PR_Malloc(size); 330 95 return result; 331 96 } … … 334 99 nsMemoryImpl::Realloc(void * ptr, PRSize size) 335 100 { 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 } 101 void* result = PR_Realloc(ptr, size); 341 102 return result; 342 103 } … … 351 112 nsMemoryImpl::HeapMinimize(PRBool aImmediate) 352 113 { 353 return FlushMemory(NS_LITERAL_STRING("heap-minimize").get(), aImmediate);114 return NS_OK; 354 115 } 355 116 … … 357 118 nsMemoryImpl::IsLowMemory(PRBool *result) 358 119 { 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; 120 *result = PR_FALSE; 377 121 return NS_OK; 378 }379 380 // see how much temp mem is available (since our allocators allocate 1Mb chunks381 // in temp mem. We don't use TempMaxMem() (to get contig space) here, because it382 // compacts the application heap, so can be slow.383 const long kReserveTempFreeSpace = (2 * 1024 * 1024); // 2Mb384 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 #else395 *result = PR_FALSE;396 #endif397 return NS_OK;398 }399 400 nsresult401 nsMemoryImpl::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've407 // got to be on the UI main thread for us to be able to do408 // 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 the439 // 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 460 nsresult461 nsMemoryImpl::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 flushing470 nsAutoLock l(aSelf->mFlushLock);471 aSelf->mIsFlushing = PR_FALSE;472 }473 474 return NS_OK;475 }476 477 void*478 nsMemoryImpl::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 487 void488 nsMemoryImpl::DestroyFlushEvent(PLEvent* aEvent)489 {490 // no-op, since mEvent is a member of nsMemoryImpl491 122 } 492 123 … … 507 138 return NS_ERROR_FAILURE; 508 139 509 #ifdef NS_MEMORY_FLUSHER_THREAD510 nsresult rv;511 512 // Create and start a memory flusher thread513 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 #endif523 524 140 return NS_OK; 525 141 } … … 529 145 { 530 146 if (gMemory) { 531 #ifdef NS_MEMORY_FLUSHER_THREAD532 if (gMemory->mFlusher) {533 // Stop the runnable...534 gMemory->mFlusher->Stop();535 NS_RELEASE(gMemory->mFlusher);536 537 // ...and wait for the thread to exit538 if (gMemory->mFlusherThread)539 gMemory->mFlusherThread->Join();540 }541 #endif542 543 147 NS_RELEASE(gMemory); 544 148 gMemory = nsnull; -
trunk/src/libs/xpcom18a4/xpcom/base/nsMemoryImpl.h
r1 r101848 41 41 #include "nsMemory.h" 42 42 #include "nsISupportsArray.h" 43 #include "nsIRunnable.h"44 #include "nsIThread.h"45 43 #include "nsCOMPtr.h" 46 44 #include "plevent.h" 47 48 struct PRLock;49 class MemoryFlusher;50 45 51 46 class nsMemoryImpl : public nsIMemory … … 56 51 57 52 nsMemoryImpl(); 58 59 nsresult FlushMemory(const PRUnichar* aReason, PRBool aImmediate);60 53 61 54 // called from xpcom initialization/finalization: … … 68 61 private: 69 62 ~nsMemoryImpl(); 70 71 protected:72 MemoryFlusher* mFlusher;73 nsCOMPtr<nsIThread> mFlusherThread;74 75 PRLock* mFlushLock;76 PRBool mIsFlushing;77 78 struct FlushEvent {79 PLEvent mEvent;80 const PRUnichar* mReason;81 };82 83 FlushEvent mFlushEvent;84 85 static nsresult RunFlushers(nsMemoryImpl* aSelf, const PRUnichar* aReason);86 87 static void* PR_CALLBACK HandleFlushEvent(PLEvent* aEvent);88 static void PR_CALLBACK DestroyFlushEvent(PLEvent* aEvent);89 63 }; 90 64
Note:
See TracChangeset
for help on using the changeset viewer.