bug 520309, startupcache client mozJSComponentLoader, r=dwitte a=bsmedberg

This commit is contained in:
Benedict Hsieh 2010-08-12 12:37:52 -07:00
parent fba716bbcd
commit f0f5cb9f08
2 changed files with 84 additions and 367 deletions

View File

@ -75,16 +75,22 @@
#endif #endif
#include "jsxdrapi.h" #include "jsxdrapi.h"
#include "jsprf.h" #include "jsprf.h"
#include "nsIFastLoadFileControl.h"
// For reporting errors with the console service // For reporting errors with the console service
#include "nsIScriptError.h" #include "nsIScriptError.h"
#include "nsIConsoleService.h" #include "nsIConsoleService.h"
#include "nsIStorageStream.h"
#include "nsIStringStream.h"
#include "prmem.h" #include "prmem.h"
#include "plbase64.h" #include "plbase64.h"
#if defined(XP_WIN) #if defined(XP_WIN)
#include "nsILocalFileWin.h" #include "nsILocalFileWin.h"
#endif #endif
#ifdef MOZ_ENABLE_LIBXUL
#include "mozilla/scache/StartupCache.h"
#include "mozilla/scache/StartupCacheUtils.h"
#endif
#if defined(MOZ_SHARK) || defined(MOZ_CALLGRIND) || defined(MOZ_VTUNE) || defined(MOZ_TRACEVIS) #if defined(MOZ_SHARK) || defined(MOZ_CALLGRIND) || defined(MOZ_VTUNE) || defined(MOZ_TRACEVIS)
#include "jsdbgapi.h" #include "jsdbgapi.h"
#endif #endif
@ -107,9 +113,6 @@ static const char kObserverServiceContractID[] = "@mozilla.org/observer-service;
#define XPC_SERIALIZATION_BUFFER_SIZE (64 * 1024) #define XPC_SERIALIZATION_BUFFER_SIZE (64 * 1024)
#define XPC_DESERIALIZATION_BUFFER_SIZE (12 * 8192) #define XPC_DESERIALIZATION_BUFFER_SIZE (12 * 8192)
// Inactivity delay before closing our fastload file stream.
static const int kFastLoadWriteDelay = 10000; // 10 seconds
#ifdef PR_LOGGING #ifdef PR_LOGGING
// NSPR_LOG_MODULES=JSComponentLoader:5 // NSPR_LOG_MODULES=JSComponentLoader:5
static PRLogModuleInfo *gJSCLLog; static PRLogModuleInfo *gJSCLLog;
@ -388,59 +391,6 @@ ReportOnCaller(JSCLContextHelper &helper,
return OutputError(cx, format, ap); return OutputError(cx, format, ap);
} }
NS_IMPL_ISUPPORTS1(nsXPCFastLoadIO, nsIFastLoadFileIO)
NS_IMETHODIMP
nsXPCFastLoadIO::GetInputStream(nsIInputStream **_retval)
{
if (! mInputStream) {
nsCOMPtr<nsIInputStream> fileInput;
nsresult rv = NS_NewLocalFileInputStream(getter_AddRefs(fileInput),
mFile);
NS_ENSURE_SUCCESS(rv, rv);
rv = NS_NewBufferedInputStream(getter_AddRefs(mInputStream),
fileInput,
XPC_DESERIALIZATION_BUFFER_SIZE);
NS_ENSURE_SUCCESS(rv, rv);
mTruncateOutputFile = false;
}
NS_ADDREF(*_retval = mInputStream);
return NS_OK;
}
NS_IMETHODIMP
nsXPCFastLoadIO::GetOutputStream(nsIOutputStream **_retval)
{
if (! mOutputStream) {
PRInt32 ioFlags = PR_WRONLY;
if (mTruncateOutputFile) {
ioFlags |= PR_CREATE_FILE | PR_TRUNCATE;
}
nsCOMPtr<nsIOutputStream> fileOutput;
nsresult rv = NS_NewLocalFileOutputStream(getter_AddRefs(fileOutput),
mFile, ioFlags, 0644);
NS_ENSURE_SUCCESS(rv, rv);
rv = NS_NewBufferedOutputStream(getter_AddRefs(mOutputStream),
fileOutput,
XPC_SERIALIZATION_BUFFER_SIZE);
NS_ENSURE_SUCCESS(rv, rv);
}
NS_ADDREF(*_retval = mOutputStream);
return NS_OK;
}
NS_IMETHODIMP
nsXPCFastLoadIO::DisableTruncate()
{
mTruncateOutputFile = false;
return NS_OK;
}
static nsresult static nsresult
ReadScriptFromStream(JSContext *cx, nsIObjectInputStream *stream, ReadScriptFromStream(JSContext *cx, nsIObjectInputStream *stream,
JSScript **script) JSScript **script)
@ -566,9 +516,6 @@ mozJSComponentLoader::~mozJSComponentLoader()
UnloadModules(); UnloadModules();
} }
NS_ASSERTION(!mFastLoadTimer,
"Fastload file should have been closed via xpcom-shutdown");
sSelf = nsnull; sSelf = nsnull;
} }
@ -584,9 +531,9 @@ nsresult
mozJSComponentLoader::ReallyInit() mozJSComponentLoader::ReallyInit()
{ {
NS_TIME_FUNCTION; NS_TIME_FUNCTION;
nsresult rv; nsresult rv;
/* /*
* Get the JSRuntime from the runtime svc, if possible. * Get the JSRuntime from the runtime svc, if possible.
* We keep a reference around, because it's a Bad Thing if the runtime * We keep a reference around, because it's a Bad Thing if the runtime
@ -634,23 +581,10 @@ mozJSComponentLoader::ReallyInit()
if (!mInProgressImports.Init(32)) if (!mInProgressImports.Init(32))
return NS_ERROR_OUT_OF_MEMORY; return NS_ERROR_OUT_OF_MEMORY;
// Set up our fastload file
nsCOMPtr<nsIFastLoadService> flSvc = do_GetFastLoadService(&rv);
if (NS_SUCCEEDED(rv))
rv = flSvc->NewFastLoadFile("XPC", getter_AddRefs(mFastLoadFile));
if (NS_FAILED(rv)) {
LOG(("Could not get fastload file location\n"));
}
// Listen for xpcom-shutdown so that we can close out our fastload file
// at that point (after that we can no longer create an input stream).
nsCOMPtr<nsIObserverService> obsSvc = nsCOMPtr<nsIObserverService> obsSvc =
do_GetService(kObserverServiceContractID, &rv); do_GetService(kObserverServiceContractID, &rv);
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
rv = obsSvc->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, PR_FALSE);
NS_ENSURE_SUCCESS(rv, rv);
rv = obsSvc->AddObserver(this, "xpcom-shutdown-loaders", PR_FALSE); rv = obsSvc->AddObserver(this, "xpcom-shutdown-loaders", PR_FALSE);
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
@ -932,245 +866,66 @@ class JSScriptHolder
JSScript *mScript; JSScript *mScript;
}; };
class FastLoadStateHolder
{
public:
explicit FastLoadStateHolder(nsIFastLoadService *service);
~FastLoadStateHolder() { pop(); }
void pop();
private:
nsCOMPtr<nsIFastLoadService> mService;
nsCOMPtr<nsIFastLoadFileIO> mIO;
nsCOMPtr<nsIObjectInputStream> mInputStream;
nsCOMPtr<nsIObjectOutputStream> mOutputStream;
};
FastLoadStateHolder::FastLoadStateHolder(nsIFastLoadService *service)
{
if (!service)
return;
mService = service;
service->GetFileIO(getter_AddRefs(mIO));
service->GetInputStream(getter_AddRefs(mInputStream));
service->GetOutputStream(getter_AddRefs(mOutputStream));
}
void
FastLoadStateHolder::pop()
{
if (!mService)
return;
mService->SetFileIO(mIO);
mService->SetInputStream(mInputStream);
mService->SetOutputStream(mOutputStream);
mService = nsnull;
}
/* static */ /* static */
void #ifdef MOZ_ENABLE_LIBXUL
mozJSComponentLoader::CloseFastLoad(nsITimer *timer, void *closure)
{
static_cast<mozJSComponentLoader*>(closure)->CloseFastLoad();
}
void
mozJSComponentLoader::CloseFastLoad()
{
// Close our fastload streams
LOG(("Closing fastload file\n"));
if (mFastLoadOutput) {
nsresult rv = mFastLoadOutput->Close();
if (NS_SUCCEEDED(rv)) {
nsCOMPtr<nsIFastLoadService> flSvc = do_GetFastLoadService(&rv);
if (NS_SUCCEEDED(rv)) {
flSvc->CacheChecksum(mFastLoadFile, mFastLoadOutput);
}
}
mFastLoadOutput = nsnull;
}
if (mFastLoadInput) {
mFastLoadInput->Close();
mFastLoadInput = nsnull;
}
mFastLoadIO = nsnull;
mFastLoadTimer = nsnull;
}
nsresult nsresult
mozJSComponentLoader::StartFastLoad(nsIFastLoadService *flSvc) mozJSComponentLoader::ReadScript(StartupCache* cache, nsIURI *uri,
{
if (!mFastLoadFile || !flSvc) {
return NS_ERROR_NOT_AVAILABLE;
}
// Now set our IO object as current, and create our streams.
if (!mFastLoadIO) {
mFastLoadIO = new nsXPCFastLoadIO(mFastLoadFile);
NS_ENSURE_TRUE(mFastLoadIO, NS_ERROR_OUT_OF_MEMORY);
}
nsresult rv = flSvc->SetFileIO(mFastLoadIO);
NS_ENSURE_SUCCESS(rv, rv);
if (!mFastLoadInput && !mFastLoadOutput) {
// First time accessing the fastload file
PRBool exists;
mFastLoadFile->Exists(&exists);
if (exists) {
LOG(("trying to use existing fastload file\n"));
rv = flSvc->NewInputStream(mFastLoadFile, getter_AddRefs(mFastLoadInput));
if (NS_SUCCEEDED(rv)) {
LOG(("opened fastload file for reading\n"));
nsCOMPtr<nsIFastLoadReadControl>
readControl(do_QueryInterface(mFastLoadInput));
if (NS_SUCCEEDED(rv)) {
/* Get the JS bytecode version number and validate it. */
PRUint32 version;
rv = mFastLoadInput->Read32(&version);
if (NS_SUCCEEDED(rv) && version != JSXDR_BYTECODE_VERSION) {
LOG(("Bad JS bytecode version\n"));
rv = NS_ERROR_UNEXPECTED;
}
}
}
if (NS_FAILED(rv)) {
LOG(("Invalid fastload file detected, removing it\n"));
if (mFastLoadInput) {
mFastLoadInput->Close();
mFastLoadInput = nsnull;
}
mFastLoadIO->SetInputStream(nsnull);
mFastLoadFile->Remove(PR_FALSE);
exists = PR_FALSE;
}
}
if (!exists) {
LOG(("Creating new fastload file\n"));
nsCOMPtr<nsIOutputStream> output;
rv = mFastLoadIO->GetOutputStream(getter_AddRefs(output));
NS_ENSURE_SUCCESS(rv, rv);
rv = flSvc->NewOutputStream(output,
getter_AddRefs(mFastLoadOutput));
if (NS_SUCCEEDED(rv))
rv = mFastLoadOutput->Write32(JSXDR_BYTECODE_VERSION);
if (NS_FAILED(rv)) {
LOG(("Fatal error, could not create fastload file\n"));
if (mFastLoadOutput) {
mFastLoadOutput->Close();
mFastLoadOutput = nsnull;
} else {
output->Close();
}
mFastLoadIO->SetOutputStream(nsnull);
mFastLoadFile->Remove(PR_FALSE);
return rv;
}
}
}
flSvc->SetInputStream(mFastLoadInput);
flSvc->SetOutputStream(mFastLoadOutput);
// Start our update timer. This allows us to keep the stream open
// when many components are loaded in succession, but close it once
// there has been a period of inactivity.
if (!mFastLoadTimer) {
mFastLoadTimer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
rv = mFastLoadTimer->InitWithFuncCallback(&mozJSComponentLoader::CloseFastLoad,
this,
kFastLoadWriteDelay,
nsITimer::TYPE_ONE_SHOT);
} else {
// Note, that since CloseFastLoad nulls out mFastLoadTimer,
// SetDelay() will only be called on a timer that hasn't fired.
rv = mFastLoadTimer->SetDelay(kFastLoadWriteDelay);
}
return rv;
}
nsresult
mozJSComponentLoader::ReadScript(nsIFastLoadService *flSvc,
const char *nativePath, nsIURI *uri,
JSContext *cx, JSScript **script) JSContext *cx, JSScript **script)
{ {
NS_ASSERTION(flSvc, "fastload not initialized"); nsresult rv;
nsresult rv = flSvc->StartMuxedDocument(uri, nativePath, nsCAutoString spec;
nsIFastLoadService::NS_FASTLOAD_READ); rv = uri->GetSpec(spec);
NS_ENSURE_SUCCESS(rv, rv);
nsAutoArrayPtr<char> buf;
PRUint32 len;
rv = cache->GetBuffer(spec.get(), getter_Transfers(buf),
&len);
if (NS_FAILED(rv)) { if (NS_FAILED(rv)) {
return rv; // don't warn since NOT_AVAILABLE is an ok error return rv; // don't warn since NOT_AVAILABLE is an ok error
} }
LOG(("Found %s in fastload file\n", nativePath)); LOG(("Found %s in startupcache\n", spec.get()));
nsCOMPtr<nsIObjectInputStream> ois;
nsCOMPtr<nsIURI> oldURI; rv = NS_NewObjectInputStreamFromBuffer(buf, len, getter_AddRefs(ois));
rv = flSvc->SelectMuxedDocument(uri, getter_AddRefs(oldURI));
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
buf.forget();
NS_ASSERTION(mFastLoadInput, return ReadScriptFromStream(cx, ois, script);
"FASTLOAD_READ should only succeed with an input stream");
rv = ReadScriptFromStream(cx, mFastLoadInput, script);
if (NS_SUCCEEDED(rv)) {
rv = flSvc->EndMuxedDocument(uri);
}
return rv;
} }
nsresult nsresult
mozJSComponentLoader::WriteScript(nsIFastLoadService *flSvc, JSScript *script, mozJSComponentLoader::WriteScript(StartupCache* cache, JSScript *script,
nsIFile *component, const char *nativePath, nsIFile *component, nsIURI *uri, JSContext *cx)
nsIURI *uri, JSContext *cx)
{ {
NS_ASSERTION(flSvc, "fastload not initialized");
nsresult rv; nsresult rv;
if (!mFastLoadOutput) { nsCAutoString spec;
// Trying to read a URI that was not in the fastload file will have rv = uri->GetSpec(spec);
// created an output stream for us. But, if we haven't tried to
// load anything that was missing, it will still be null.
rv = flSvc->GetOutputStream(getter_AddRefs(mFastLoadOutput));
NS_ENSURE_SUCCESS(rv, rv);
}
NS_ASSERTION(mFastLoadOutput, "must have an output stream here");
LOG(("Writing %s to fastload\n", nativePath));
rv = flSvc->AddDependency(component);
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
rv = flSvc->StartMuxedDocument(uri, nativePath, LOG(("Writing %s to startupcache\n", spec.get()));
nsIFastLoadService::NS_FASTLOAD_WRITE); nsCOMPtr<nsIObjectOutputStream> oos;
nsCOMPtr<nsIStorageStream> storageStream;
rv = NS_NewObjectOutputWrappedStorageStream(getter_AddRefs(oos),
getter_AddRefs(storageStream));
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIURI> oldURI; rv = WriteScriptToStream(cx, script, oos);
rv = flSvc->SelectMuxedDocument(uri, getter_AddRefs(oldURI)); oos->Close();
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
rv = WriteScriptToStream(cx, script, mFastLoadOutput); nsAutoArrayPtr<char> buf;
PRUint32 len;
rv = NS_NewBufferFromStorageStream(storageStream, getter_Transfers(buf),
&len);
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
return flSvc->EndMuxedDocument(uri); rv = cache->PutBuffer(spec.get(), buf, len);
return rv;
} }
#endif //MOZ_ENABLE_LIBXUL
nsresult nsresult
mozJSComponentLoader::GlobalForLocation(nsILocalFile *aComponentFile, mozJSComponentLoader::GlobalForLocation(nsILocalFile *aComponentFile,
@ -1263,60 +1018,31 @@ mozJSComponentLoader::GlobalForLocation(nsILocalFile *aComponentFile,
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
#endif #endif
// Before compiling the script, first check to see if we have it in
// the fastload file. Note: as a rule, fastload errors are not fatal
// to loading the script, since we can always slow-load.
nsCOMPtr<nsIFastLoadService> flSvc = do_GetFastLoadService(&rv);
// Save the old state and restore it upon return
FastLoadStateHolder flState(flSvc);
PRBool fastLoading = PR_FALSE;
if (NS_SUCCEEDED(rv)) {
rv = StartFastLoad(flSvc);
if (NS_SUCCEEDED(rv)) {
fastLoading = PR_TRUE;
}
}
JSScript *script = nsnull; JSScript *script = nsnull;
if (fastLoading) { #ifdef MOZ_ENABLE_LIBXUL
rv = ReadScript(flSvc, nativePath.get(), aURI, cx, &script); // Before compiling the script, first check to see if we have it in
if (NS_SUCCEEDED(rv)) { // the startupcache. Note: as a rule, startupcache errors are not fatal
LOG(("Successfully loaded %s from fastload\n", nativePath.get())); // to loading the script, since we can always slow-load.
fastLoading = PR_FALSE; // no need to write out the script
} else if (rv == NS_ERROR_NOT_AVAILABLE) { PRBool writeToCache = PR_FALSE;
// This is ok, it just means the script is not yet in the StartupCache* cache = StartupCache::GetSingleton();
// fastload file.
rv = NS_OK;
} else {
LOG(("Failed to deserialize %s\n", nativePath.get()));
// Remove the fastload file, it may be corrupted. if (cache) {
LOG(("Invalid fastload file detected, removing it\n")); rv = ReadScript(cache, aURI, cx, &script);
nsCOMPtr<nsIObjectOutputStream> objectOutput; if (NS_SUCCEEDED(rv)) {
flSvc->GetOutputStream(getter_AddRefs(objectOutput)); LOG(("Successfully loaded %s from startupcache\n", nativePath.get()));
if (objectOutput) { } else {
flSvc->SetOutputStream(nsnull); // This is ok, it just means the script is not yet in the
objectOutput->Close(); // cache. Could mean that the cache was corrupted and got removed,
} // but either way we're going to write this out.
nsCOMPtr<nsIObjectInputStream> objectInput; writeToCache = PR_TRUE;
flSvc->GetInputStream(getter_AddRefs(objectInput));
if (objectInput) {
flSvc->SetInputStream(nsnull);
objectInput->Close();
}
if (mFastLoadFile) {
mFastLoadFile->Remove(PR_FALSE);
}
fastLoading = PR_FALSE;
} }
} }
#endif
if (!script) {
if (!script || NS_FAILED(rv)) { // The script wasn't in the cache , so compile it now.
// The script wasn't in the fastload cache, so compile it now.
LOG(("Slow loading %s\n", nativePath.get())); LOG(("Slow loading %s\n", nativePath.get()));
// If |exception| is non-null, then our caller wants us to propagate // If |exception| is non-null, then our caller wants us to propagate
@ -1475,21 +1201,20 @@ mozJSComponentLoader::GlobalForLocation(nsILocalFile *aComponentFile,
nativePath.get()); nativePath.get());
#endif #endif
if (fastLoading) { #ifdef MOZ_ENABLE_LIBXUL
// We successfully compiled the script, so cache it in fastload. if (writeToCache) {
rv = WriteScript(flSvc, script, aComponentFile, nativePath.get(), aURI, cx); // We successfully compiled the script, so cache it.
rv = WriteScript(cache, script, aComponentFile, aURI, cx);
// Don't treat failure to write as fatal, since we might be working // Don't treat failure to write as fatal, since we might be working
// with a read-only fastload file. // with a read-only cache.
if (NS_SUCCEEDED(rv)) { if (NS_SUCCEEDED(rv)) {
LOG(("Successfully wrote to fastload\n")); LOG(("Successfully wrote to cache\n"));
} else { } else {
LOG(("Failed to write to fastload\n")); LOG(("Failed to write to cache\n"));
} }
} }
#endif
// Restore the old state of the fastload service.
flState.pop();
// Assign aGlobal here so that it's available to recursive imports. // Assign aGlobal here so that it's available to recursive imports.
// See bug 384168. // See bug 384168.
@ -1819,14 +1544,7 @@ NS_IMETHODIMP
mozJSComponentLoader::Observe(nsISupports *subject, const char *topic, mozJSComponentLoader::Observe(nsISupports *subject, const char *topic,
const PRUnichar *data) const PRUnichar *data)
{ {
if (!strcmp(topic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) { if (!strcmp(topic, "xpcom-shutdown-loaders")) {
if (mFastLoadTimer) {
mFastLoadTimer->Cancel();
}
CloseFastLoad();
}
else if (!strcmp(topic, "xpcom-shutdown-loaders")) {
UnloadModules(); UnloadModules();
} }
else { else {

View File

@ -57,6 +57,12 @@
#ifndef XPCONNECT_STANDALONE #ifndef XPCONNECT_STANDALONE
#include "nsIPrincipal.h" #include "nsIPrincipal.h"
#endif #endif
#ifdef MOZ_ENABLE_LIBXUL
#include "mozilla/scache/StartupCache.h"
using namespace mozilla::scache;
#endif
#include "xpcIJSGetFactory.h" #include "xpcIJSGetFactory.h"
/* 6bd13476-1dd2-11b2-bbef-f0ccb5fa64b6 (thanks, mozbot) */ /* 6bd13476-1dd2-11b2-bbef-f0ccb5fa64b6 (thanks, mozbot) */
@ -127,23 +133,16 @@ class mozJSComponentLoader : public mozilla::ModuleLoader,
char **location, char **location,
jsval *exception); jsval *exception);
nsresult StartFastLoad(nsIFastLoadService *flSvc); #ifdef MOZ_ENABLE_LIBXUL
nsresult ReadScript(nsIFastLoadService *flSvc, const char *nativePath, nsresult ReadScript(StartupCache *cache, nsIURI *uri,
nsIURI *uri, JSContext *cx, JSScript **script); JSContext *cx, JSScript **script);
nsresult WriteScript(nsIFastLoadService *flSvc, JSScript *script, nsresult WriteScript(StartupCache *cache, JSScript *script,
nsIFile *component, const char *nativePath, nsIFile *component, nsIURI *uri, JSContext *cx);
nsIURI *uri, JSContext *cx); #endif
static void CloseFastLoad(nsITimer *timer, void *closure);
void CloseFastLoad();
nsCOMPtr<nsIComponentManager> mCompMgr; nsCOMPtr<nsIComponentManager> mCompMgr;
nsCOMPtr<nsIJSRuntimeService> mRuntimeService; nsCOMPtr<nsIJSRuntimeService> mRuntimeService;
nsCOMPtr<nsIThreadJSContextStack> mContextStack; nsCOMPtr<nsIThreadJSContextStack> mContextStack;
nsCOMPtr<nsIFile> mFastLoadFile;
nsRefPtr<nsXPCFastLoadIO> mFastLoadIO;
nsCOMPtr<nsIObjectInputStream> mFastLoadInput;
nsCOMPtr<nsIObjectOutputStream> mFastLoadOutput;
nsCOMPtr<nsITimer> mFastLoadTimer;
#ifndef XPCONNECT_STANDALONE #ifndef XPCONNECT_STANDALONE
nsCOMPtr<nsIPrincipal> mSystemPrincipal; nsCOMPtr<nsIPrincipal> mSystemPrincipal;
#endif #endif