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
#include "jsxdrapi.h"
#include "jsprf.h"
#include "nsIFastLoadFileControl.h"
// For reporting errors with the console service
#include "nsIScriptError.h"
#include "nsIConsoleService.h"
#include "nsIStorageStream.h"
#include "nsIStringStream.h"
#include "prmem.h"
#include "plbase64.h"
#if defined(XP_WIN)
#include "nsILocalFileWin.h"
#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)
#include "jsdbgapi.h"
#endif
@ -107,9 +113,6 @@ static const char kObserverServiceContractID[] = "@mozilla.org/observer-service;
#define XPC_SERIALIZATION_BUFFER_SIZE (64 * 1024)
#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
// NSPR_LOG_MODULES=JSComponentLoader:5
static PRLogModuleInfo *gJSCLLog;
@ -388,59 +391,6 @@ ReportOnCaller(JSCLContextHelper &helper,
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
ReadScriptFromStream(JSContext *cx, nsIObjectInputStream *stream,
JSScript **script)
@ -566,9 +516,6 @@ mozJSComponentLoader::~mozJSComponentLoader()
UnloadModules();
}
NS_ASSERTION(!mFastLoadTimer,
"Fastload file should have been closed via xpcom-shutdown");
sSelf = nsnull;
}
@ -584,9 +531,9 @@ nsresult
mozJSComponentLoader::ReallyInit()
{
NS_TIME_FUNCTION;
nsresult rv;
/*
* Get the JSRuntime from the runtime svc, if possible.
* We keep a reference around, because it's a Bad Thing if the runtime
@ -634,23 +581,10 @@ mozJSComponentLoader::ReallyInit()
if (!mInProgressImports.Init(32))
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 =
do_GetService(kObserverServiceContractID, &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);
NS_ENSURE_SUCCESS(rv, rv);
@ -932,245 +866,66 @@ class JSScriptHolder
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 */
void
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;
}
#ifdef MOZ_ENABLE_LIBXUL
nsresult
mozJSComponentLoader::StartFastLoad(nsIFastLoadService *flSvc)
{
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,
mozJSComponentLoader::ReadScript(StartupCache* cache, nsIURI *uri,
JSContext *cx, JSScript **script)
{
NS_ASSERTION(flSvc, "fastload not initialized");
nsresult rv = flSvc->StartMuxedDocument(uri, nativePath,
nsIFastLoadService::NS_FASTLOAD_READ);
nsresult rv;
nsCAutoString spec;
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)) {
return rv; // don't warn since NOT_AVAILABLE is an ok error
}
LOG(("Found %s in fastload file\n", nativePath));
nsCOMPtr<nsIURI> oldURI;
rv = flSvc->SelectMuxedDocument(uri, getter_AddRefs(oldURI));
LOG(("Found %s in startupcache\n", spec.get()));
nsCOMPtr<nsIObjectInputStream> ois;
rv = NS_NewObjectInputStreamFromBuffer(buf, len, getter_AddRefs(ois));
NS_ENSURE_SUCCESS(rv, rv);
buf.forget();
NS_ASSERTION(mFastLoadInput,
"FASTLOAD_READ should only succeed with an input stream");
rv = ReadScriptFromStream(cx, mFastLoadInput, script);
if (NS_SUCCEEDED(rv)) {
rv = flSvc->EndMuxedDocument(uri);
}
return rv;
return ReadScriptFromStream(cx, ois, script);
}
nsresult
mozJSComponentLoader::WriteScript(nsIFastLoadService *flSvc, JSScript *script,
nsIFile *component, const char *nativePath,
nsIURI *uri, JSContext *cx)
mozJSComponentLoader::WriteScript(StartupCache* cache, JSScript *script,
nsIFile *component, nsIURI *uri, JSContext *cx)
{
NS_ASSERTION(flSvc, "fastload not initialized");
nsresult rv;
if (!mFastLoadOutput) {
// Trying to read a URI that was not in the fastload file will have
// 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);
nsCAutoString spec;
rv = uri->GetSpec(spec);
NS_ENSURE_SUCCESS(rv, rv);
rv = flSvc->StartMuxedDocument(uri, nativePath,
nsIFastLoadService::NS_FASTLOAD_WRITE);
LOG(("Writing %s to startupcache\n", spec.get()));
nsCOMPtr<nsIObjectOutputStream> oos;
nsCOMPtr<nsIStorageStream> storageStream;
rv = NS_NewObjectOutputWrappedStorageStream(getter_AddRefs(oos),
getter_AddRefs(storageStream));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIURI> oldURI;
rv = flSvc->SelectMuxedDocument(uri, getter_AddRefs(oldURI));
rv = WriteScriptToStream(cx, script, oos);
oos->Close();
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);
return flSvc->EndMuxedDocument(uri);
rv = cache->PutBuffer(spec.get(), buf, len);
return rv;
}
#endif //MOZ_ENABLE_LIBXUL
nsresult
mozJSComponentLoader::GlobalForLocation(nsILocalFile *aComponentFile,
@ -1263,60 +1018,31 @@ mozJSComponentLoader::GlobalForLocation(nsILocalFile *aComponentFile,
NS_ENSURE_SUCCESS(rv, rv);
#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;
if (fastLoading) {
rv = ReadScript(flSvc, nativePath.get(), aURI, cx, &script);
if (NS_SUCCEEDED(rv)) {
LOG(("Successfully loaded %s from fastload\n", nativePath.get()));
fastLoading = PR_FALSE; // no need to write out the script
} else if (rv == NS_ERROR_NOT_AVAILABLE) {
// This is ok, it just means the script is not yet in the
// fastload file.
rv = NS_OK;
} else {
LOG(("Failed to deserialize %s\n", nativePath.get()));
#ifdef MOZ_ENABLE_LIBXUL
// Before compiling the script, first check to see if we have it in
// the startupcache. Note: as a rule, startupcache errors are not fatal
// to loading the script, since we can always slow-load.
PRBool writeToCache = PR_FALSE;
StartupCache* cache = StartupCache::GetSingleton();
// Remove the fastload file, it may be corrupted.
LOG(("Invalid fastload file detected, removing it\n"));
nsCOMPtr<nsIObjectOutputStream> objectOutput;
flSvc->GetOutputStream(getter_AddRefs(objectOutput));
if (objectOutput) {
flSvc->SetOutputStream(nsnull);
objectOutput->Close();
}
nsCOMPtr<nsIObjectInputStream> objectInput;
flSvc->GetInputStream(getter_AddRefs(objectInput));
if (objectInput) {
flSvc->SetInputStream(nsnull);
objectInput->Close();
}
if (mFastLoadFile) {
mFastLoadFile->Remove(PR_FALSE);
}
fastLoading = PR_FALSE;
if (cache) {
rv = ReadScript(cache, aURI, cx, &script);
if (NS_SUCCEEDED(rv)) {
LOG(("Successfully loaded %s from startupcache\n", nativePath.get()));
} else {
// This is ok, it just means the script is not yet in the
// cache. Could mean that the cache was corrupted and got removed,
// but either way we're going to write this out.
writeToCache = PR_TRUE;
}
}
#endif
if (!script || NS_FAILED(rv)) {
// The script wasn't in the fastload cache, so compile it now.
if (!script) {
// The script wasn't in the cache , so compile it now.
LOG(("Slow loading %s\n", nativePath.get()));
// If |exception| is non-null, then our caller wants us to propagate
@ -1475,21 +1201,20 @@ mozJSComponentLoader::GlobalForLocation(nsILocalFile *aComponentFile,
nativePath.get());
#endif
if (fastLoading) {
// We successfully compiled the script, so cache it in fastload.
rv = WriteScript(flSvc, script, aComponentFile, nativePath.get(), aURI, cx);
#ifdef MOZ_ENABLE_LIBXUL
if (writeToCache) {
// 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
// with a read-only fastload file.
// with a read-only cache.
if (NS_SUCCEEDED(rv)) {
LOG(("Successfully wrote to fastload\n"));
LOG(("Successfully wrote to cache\n"));
} else {
LOG(("Failed to write to fastload\n"));
LOG(("Failed to write to cache\n"));
}
}
// Restore the old state of the fastload service.
flState.pop();
#endif
// Assign aGlobal here so that it's available to recursive imports.
// See bug 384168.
@ -1819,14 +1544,7 @@ NS_IMETHODIMP
mozJSComponentLoader::Observe(nsISupports *subject, const char *topic,
const PRUnichar *data)
{
if (!strcmp(topic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
if (mFastLoadTimer) {
mFastLoadTimer->Cancel();
}
CloseFastLoad();
}
else if (!strcmp(topic, "xpcom-shutdown-loaders")) {
if (!strcmp(topic, "xpcom-shutdown-loaders")) {
UnloadModules();
}
else {

View File

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