From 6cf529ec881435bf9de9483e2c0d6ffb6a14c87c Mon Sep 17 00:00:00 2001 From: Taras Glek Date: Thu, 20 Jan 2011 13:40:45 -0800 Subject: [PATCH] Bug 586859 - Move startupcache write off the main thread r=cjones a=2.0 --- startupcache/StartupCache.cpp | 64 +++++++++++++++++++++++++++++------ 1 file changed, 54 insertions(+), 10 deletions(-) diff --git a/startupcache/StartupCache.cpp b/startupcache/StartupCache.cpp index 1c194f7f24c..daca67c3dff 100644 --- a/startupcache/StartupCache.cpp +++ b/startupcache/StartupCache.cpp @@ -64,6 +64,7 @@ #include "nsZipArchive.h" #include "mozilla/Omnijar.h" #include "prenv.h" +#include "mozilla/FunctionTimer.h" #ifdef IS_BIG_ENDIAN #define SC_ENDIAN "big" @@ -116,7 +117,7 @@ StartupCache* StartupCache::gStartupCache; PRBool StartupCache::gShutdownInitiated; StartupCache::StartupCache() - : mArchive(NULL), mStartupWriteInitiated(PR_FALSE) { } + : mArchive(NULL), mStartupWriteInitiated(PR_FALSE), mWriteThread(NULL) {} StartupCache::~StartupCache() { @@ -203,6 +204,7 @@ StartupCache::Init() nsresult StartupCache::LoadArchive() { + WaitOnWriteThread(); PRBool exists; mArchive = NULL; nsresult rv = mFile->Exists(&exists); @@ -218,6 +220,7 @@ StartupCache::LoadArchive() nsresult StartupCache::GetBuffer(const char* id, char** outbuf, PRUint32* length) { + WaitOnWriteThread(); if (!mStartupWriteInitiated) { CacheEntry* entry; nsDependentCString idStr(id); @@ -257,6 +260,7 @@ StartupCache::GetBuffer(const char* id, char** outbuf, PRUint32* length) nsresult StartupCache::PutBuffer(const char* id, const char* inbuf, PRUint32 len) { + WaitOnWriteThread(); if (StartupCache::gShutdownInitiated) { return NS_ERROR_NOT_AVAILABLE; } @@ -308,7 +312,7 @@ CacheCloseHelper(const nsACString& key, nsAutoPtr& data, NS_ASSERTION(NS_SUCCEEDED(rv) && hasEntry == PR_FALSE, "Existing entry in disk StartupCache."); #endif - rv = writer->AddEntryStream(key, holder->time, PR_FALSE, stream, false); + rv = writer->AddEntryStream(key, holder->time, PR_TRUE, stream, PR_FALSE); if (NS_FAILED(rv)) { NS_WARNING("cache entry deleted but not written to disk."); @@ -319,6 +323,7 @@ CacheCloseHelper(const nsACString& key, nsAutoPtr& data, void StartupCache::WriteToDisk() { + WaitOnWriteThread(); nsresult rv; mStartupWriteInitiated = PR_TRUE; @@ -352,7 +357,7 @@ StartupCache::WriteToDisk() // Close the archive so Windows doesn't choke. mArchive = NULL; zipW->Close(); - + // our reader's view of the archive is outdated now, reload it. LoadArchive(); @@ -362,17 +367,52 @@ StartupCache::WriteToDisk() void StartupCache::InvalidateCache() { + WaitOnWriteThread(); mTable.Clear(); mArchive = NULL; mFile->Remove(false); LoadArchive(); } +/* + * WaitOnWriteThread() is called from a main thread to wait for the worker + * thread to finish. However since the same code is used in the worker thread and + * main thread, the worker thread can also call WaitOnWriteThread() which is a no-op. + */ +void +StartupCache::WaitOnWriteThread() +{ + PRThread* writeThread = mWriteThread; + if (!writeThread || writeThread == PR_GetCurrentThread()) + return; + + NS_TIME_FUNCTION_MIN(30); + //NS_WARNING("Waiting on startupcache write"); + PR_JoinThread(writeThread); + mWriteThread = NULL; +} + +void +StartupCache::ThreadedWrite(void *aClosure) +{ + gStartupCache->WriteToDisk(); +} + +/* + * The write-thread is spawned on a timeout(which is reset with every write). This + * can avoid a slow shutdown. After writing out the cache, the zipreader is + * reloaded on the worker thread. + */ void StartupCache::WriteTimeout(nsITimer *aTimer, void *aClosure) { - StartupCache* sc = (StartupCache*) aClosure; - sc->WriteToDisk(); + gStartupCache->mWriteThread = PR_CreateThread(PR_USER_THREAD, + StartupCache::ThreadedWrite, + NULL, + PR_PRIORITY_NORMAL, + PR_LOCAL_THREAD, + PR_JOINABLE_THREAD, + 0); } // We don't want to refcount StartupCache, so we'll just @@ -382,15 +422,18 @@ NS_IMPL_THREADSAFE_ISUPPORTS1(StartupCacheListener, nsIObserver) nsresult StartupCacheListener::Observe(nsISupports *subject, const char* topic, const PRUnichar* data) { - nsresult rv = NS_OK; + StartupCache* sc = StartupCache::GetSingleton(); + if (!sc) + return NS_OK; + if (strcmp(topic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) { + // Do not leave the thread running past xpcom shutdown + sc->WaitOnWriteThread(); StartupCache::gShutdownInitiated = PR_TRUE; } else if (strcmp(topic, "startupcache-invalidate") == 0) { - StartupCache* sc = StartupCache::GetSingleton(); - if (sc) - sc->InvalidateCache(); + sc->InvalidateCache(); } - return rv; + return NS_OK; } nsresult @@ -582,6 +625,7 @@ StartupCacheWrapper::StartupWriteComplete(PRBool *complete) if (!sc) { return NS_ERROR_NOT_INITIALIZED; } + sc->WaitOnWriteThread(); *complete = sc->mStartupWriteInitiated && sc->mTable.Count() == 0; return NS_OK; }