bug 520309, startupcache core r=dwitte sr=bsmedberg a=bsmedberg

This commit is contained in:
bhsieh@mozilla.com 2010-08-12 12:37:44 -07:00
parent 976a5d3c42
commit fba716bbcd
19 changed files with 1790 additions and 2 deletions

View File

@ -736,6 +736,12 @@ PRUint8* nsZipArchive::GetData(nsZipItem* aItem)
return data + offset;
}
PRBool
nsZipArchive::CheckCRC(nsZipItem* aItem, PRUint8* aItemData) {
PRUint32 crc = crc32(0, (const unsigned char*)aItemData, aItem->Size());
return crc == aItem->CRC32();
}
//------------------------------------------
// nsZipArchive constructor and destructor
//------------------------------------------

View File

@ -196,6 +196,8 @@ public:
*/
PRUint8* GetData(nsZipItem* aItem);
PRBool CheckCRC(nsZipItem* aItem, PRUint8* aData);
private:
//--- private members ---

82
startupcache/Makefile.in Normal file
View File

@ -0,0 +1,82 @@
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is Startup Cache.
#
# The Initial Developer of the Original Code is
# Mozilla Corporation.
# Portions created by the Initial Developer are Copyright (C) 2010
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Benedict Hsieh <bhsieh@mozilla.com>
#
# Alternatively, the contents of this file may be used under the terms of
# either the GNU General Public License Version 2 or later (the "GPL"), or
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# and other provisions required by the GPL or the LGPL. If you do not delete
# the provisions above, a recipient may use your version of this file under
# the terms of any one of the MPL, the GPL or the LGPL.
#
# ***** END LICENSE BLOCK *****
DEPTH = ..
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
DIRS = $(NULL)
ifdef ENABLE_TESTS
TOOL_DIRS += test
endif
MODULE = startupcache
MODULE_NAME = StartupCacheModule
LIBRARY_NAME = startupcache
SHORT_LIBNAME = scache
EXPORT_LIBRARY = 1
LIBXUL_LIBRARY = 1
IS_COMPONENT = 1
GRE_MODULE = 1
CPPSRCS = StartupCache.cpp \
StartupCacheUtils.cpp \
StartupCacheModule.cpp \
$(NULL)
EXPORTS_NAMESPACES = mozilla/scache
EXPORTS_mozilla/scache = StartupCache.h \
StartupCacheUtils.h \
$(NULL)
XPIDLSRCS = nsIStartupCache.idl \
$(NULL)
EXTRA_DSO_LDOPTS += \
$(LIBS_DIR) \
$(ZLIB_LIBS) \
$(MOZ_COMPONENT_LIBS) \
$(NULL)
include $(topsrcdir)/config/rules.mk

View File

@ -0,0 +1,628 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Startup Cache.
*
* The Initial Developer of the Original Code is
* The Mozilla Foundation <http://www.mozilla.org/>.
* Portions created by the Initial Developer are Copyright (C) 2009
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Benedict Hsieh <bhsieh@mozilla.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "prio.h"
#include "prtypes.h"
#include "pldhash.h"
#include "mozilla/scache/StartupCache.h"
#include "nsAutoPtr.h"
#include "nsClassHashtable.h"
#include "nsComponentManagerUtils.h"
#include "nsDirectoryServiceUtils.h"
#include "nsIClassInfo.h"
#include "nsIFile.h"
#include "nsILocalFile.h"
#include "nsIObserver.h"
#include "nsIObserverService.h"
#include "nsIOutputStream.h"
#include "nsIStartupCache.h"
#include "nsIStorageStream.h"
#include "nsIStreamBufferAccess.h"
#include "nsIStringStream.h"
#include "nsISupports.h"
#include "nsITimer.h"
#include "nsIZipWriter.h"
#include "nsIZipReader.h"
#include "nsWeakReference.h"
#include "nsZipArchive.h"
#ifdef IS_BIG_ENDIAN
#define SC_ENDIAN "big"
#else
#define SC_ENDIAN "little"
#endif
#if PR_BYTES_PER_WORD == 4
#define SC_WORDSIZE "4"
#else
#define SC_WORDSIZE "8"
#endif
namespace mozilla {
namespace scache {
static const char sStartupCacheName[] = "startupCache." SC_WORDSIZE "." SC_ENDIAN;
static NS_DEFINE_CID(kZipReaderCID, NS_ZIPREADER_CID);
StartupCache*
StartupCache::GetSingleton()
{
if (!gStartupCache)
StartupCache::InitSingleton();
return StartupCache::gStartupCache;
}
void
StartupCache::DeleteSingleton()
{
delete StartupCache::gStartupCache;
}
nsresult
StartupCache::InitSingleton()
{
nsresult rv;
StartupCache::gStartupCache = new StartupCache();
rv = StartupCache::gStartupCache->Init();
if (NS_FAILED(rv)) {
delete StartupCache::gStartupCache;
}
return rv;
}
StartupCache* StartupCache::gStartupCache;
PRBool StartupCache::gShutdownInitiated;
StartupCache::StartupCache()
: mArchive(NULL), mStartupWriteInitiated(PR_FALSE) { }
StartupCache::~StartupCache()
{
// Generally, the in-memory table should be empty here,
// but in special cases (like Talos Ts tests) we
// could shut down before we write.
// This mechanism will change when IO is moved off-thread
// (bug 586859) or when Talos first-run is changed to allow
// our timer to work (bug 591471).
WriteToDisk();
gStartupCache = nsnull;
}
nsresult
StartupCache::Init()
{
nsresult rv;
mTable.Init();
#ifdef DEBUG
mWriteObjectMap.Init();
#endif
mZipW = do_CreateInstance("@mozilla.org/zipwriter;1", &rv);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIFile> file;
rv = NS_GetSpecialDirectory("ProfLDS",
getter_AddRefs(file));
if (NS_FAILED(rv)) {
// return silently, this will fail in mochitests's xpcshell process.
return rv;
}
rv = file->AppendNative(NS_LITERAL_CSTRING("startupCache"));
NS_ENSURE_SUCCESS(rv, rv);
// Try to create the directory if it's not there yet
rv = file->Create(nsIFile::DIRECTORY_TYPE, 0777);
if (NS_FAILED(rv) && rv != NS_ERROR_FILE_ALREADY_EXISTS)
return rv;
rv = file->AppendNative(NS_LITERAL_CSTRING(sStartupCacheName));
NS_ENSURE_SUCCESS(rv, rv);
mFile = do_QueryInterface(file);
NS_ENSURE_TRUE(mFile, NS_ERROR_UNEXPECTED);
mObserverService = do_GetService("@mozilla.org/observer-service;1");
if (!mObserverService) {
NS_WARNING("Could not get observerService.");
return NS_ERROR_UNEXPECTED;
}
mListener = new StartupCacheListener();
rv = mObserverService->AddObserver(mListener, NS_XPCOM_SHUTDOWN_OBSERVER_ID,
PR_FALSE);
NS_ENSURE_SUCCESS(rv, rv);
rv = LoadArchive();
// Sometimes we don't have a cache yet, that's ok.
// If it's corrupted, just remove it and start over.
if (NS_FAILED(rv) && rv != NS_ERROR_FILE_NOT_FOUND) {
NS_WARNING("Failed to load startupcache file correctly, removing!");
InvalidateCache();
}
mTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
NS_ENSURE_SUCCESS(rv, rv);
// Wait for 10 seconds, then write out the cache.
rv = mTimer->InitWithFuncCallback(StartupCache::WriteTimeout, this, 10000,
nsITimer::TYPE_ONE_SHOT);
return rv;
}
nsresult
StartupCache::LoadArchive()
{
PRBool exists;
mArchive = NULL;
nsresult rv = mFile->Exists(&exists);
if (NS_FAILED(rv) || !exists)
return NS_ERROR_FILE_NOT_FOUND;
mArchive = new nsZipArchive();
return mArchive->OpenArchive(mFile);
}
// NOTE: this will not find a new entry until it has been written to disk!
// Consumer should take ownership of the resulting buffer.
nsresult
StartupCache::GetBuffer(const char* id, char** outbuf, PRUint32* length)
{
PRBool exists;
char* data = NULL;
PRUint32 len;
if (!mStartupWriteInitiated) {
CacheEntry* entry;
nsDependentCString idStr(id);
mTable.Get(idStr, &entry);
if (entry) {
data = entry->data;
len = entry->size;
}
}
if (!data && mArchive) {
nsZipItem* zipItem = mArchive->GetItem(id);
if (zipItem) {
PRUint8* itemData = mArchive->GetData(zipItem);
if (!itemData || !mArchive->CheckCRC(zipItem, itemData)) {
NS_WARNING("StartupCache file corrupted!");
InvalidateCache();
return NS_ERROR_FILE_CORRUPTED;
}
len = zipItem->Size();
data = (char*) itemData;
}
}
if (data) {
*outbuf = new char[len];
memcpy(*outbuf, data, len);
*length = len;
return NS_OK;
}
return NS_ERROR_NOT_AVAILABLE;
}
// Makes a copy of the buffer, client retains ownership of inbuf.
nsresult
StartupCache::PutBuffer(const char* id, const char* inbuf, PRUint32 len)
{
nsresult rv;
if (StartupCache::gShutdownInitiated) {
return NS_ERROR_NOT_AVAILABLE;
}
nsAutoArrayPtr<char> data(new char[len]);
memcpy(data, inbuf, len);
nsDependentCString idStr(id);
if (!mStartupWriteInitiated) {
// Cache it for now, we'll write all together later.
CacheEntry* entry;
#ifdef DEBUG
mTable.Get(idStr, &entry);
NS_ASSERTION(entry == nsnull, "Existing entry in StartupCache.");
if (mArchive) {
nsZipItem* zipItem = mArchive->GetItem(id);
NS_ASSERTION(zipItem == nsnull, "Existing entry in disk StartupCache.");
}
#endif
entry = new CacheEntry(data.forget(), len);
mTable.Put(idStr, entry);
return NS_OK;
}
rv = mZipW->Open(mFile, PR_RDWR | PR_CREATE_FILE);
NS_ENSURE_SUCCESS(rv, rv);
// XXX We need to think about whether to write this out every time,
// or somehow detect a good time to write. We need to finish writing
// before shutdown though, and writing also requires a reload of the
// reader's archive, which probably can't handle having the underlying
// file change underneath it. Potentially could reload on the next
// read request, if this is a problem. See Bug 586859.
#ifdef DEBUG
PRBool hasEntry;
rv = mZipW->HasEntry(idStr, &hasEntry);
NS_ENSURE_SUCCESS(rv, rv);
NS_ASSERTION(hasEntry == PR_FALSE, "Existing entry in disk StartupCache.");
#endif
nsCOMPtr<nsIStringInputStream> stream
= do_CreateInstance("@mozilla.org/io/string-input-stream;1",
&rv);
NS_ENSURE_SUCCESS(rv, rv);
rv = stream->AdoptData(data, len);
NS_ENSURE_SUCCESS(rv, rv);
data.forget();
rv = mZipW->AddEntryStream(idStr, 0, 0, stream, false);
NS_ENSURE_SUCCESS(rv, rv);
// Close the archive so Windows doesn't choke.
mArchive = NULL;
rv = mZipW->Close();
NS_ENSURE_SUCCESS(rv, rv);
// our reader's view of the archive is outdated now, reload it.
return LoadArchive();
}
struct CacheWriteHolder
{
nsCOMPtr<nsIZipWriter> writer;
nsCOMPtr<nsIStringInputStream> stream;
};
PLDHashOperator
CacheCloseHelper(const nsACString& key, nsAutoPtr<CacheEntry>& data,
void* closure)
{
nsresult rv;
CacheWriteHolder* holder = (CacheWriteHolder*) closure;
nsIStringInputStream* stream = holder->stream;
nsIZipWriter* writer = holder->writer;
stream->ShareData(data->data, data->size);
#ifdef DEBUG
PRBool hasEntry;
rv = writer->HasEntry(key, &hasEntry);
NS_ASSERTION(NS_SUCCEEDED(rv) && hasEntry == PR_FALSE,
"Existing entry in disk StartupCache.");
#endif
rv = writer->AddEntryStream(key, 0, 0, stream, false);
if (NS_FAILED(rv)) {
NS_WARNING("cache entry deleted but not written to disk.");
}
return PL_DHASH_REMOVE;
}
void
StartupCache::WriteToDisk()
{
nsresult rv;
mStartupWriteInitiated = PR_TRUE;
if (mTable.Count() == 0)
return;
rv = mZipW->Open(mFile, PR_RDWR | PR_CREATE_FILE);
if (NS_FAILED(rv)) {
NS_WARNING("could not open zipfile for write");
return;
}
nsCOMPtr<nsIStringInputStream> stream
= do_CreateInstance("@mozilla.org/io/string-input-stream;1", &rv);
if (NS_FAILED(rv)) {
NS_WARNING("Couldn't create string input stream.");
return;
}
CacheWriteHolder holder;
holder.stream = stream;
holder.writer = mZipW;
mTable.Enumerate(CacheCloseHelper, &holder);
// Close the archive so Windows doesn't choke.
mArchive = NULL;
mZipW->Close();
// our reader's view of the archive is outdated now, reload it.
LoadArchive();
return;
}
void
StartupCache::InvalidateCache()
{
mTable.Clear();
mArchive = NULL;
// This is usually closed, but it's possible to get into
// an inconsistent state.
mZipW->Close();
mFile->Remove(false);
LoadArchive();
}
void
StartupCache::WriteTimeout(nsITimer *aTimer, void *aClosure)
{
StartupCache* sc = (StartupCache*) aClosure;
sc->WriteToDisk();
}
// We don't want to refcount StartupCache, so we'll just
// hold a ref to this and pass it to observerService instead.
NS_IMPL_THREADSAFE_ISUPPORTS1(StartupCacheListener, nsIObserver)
nsresult
StartupCacheListener::Observe(nsISupports *subject, const char* topic, const PRUnichar* data)
{
nsresult rv = NS_OK;
if (strcmp(topic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) {
StartupCache::gShutdownInitiated = PR_TRUE;
}
return rv;
}
nsresult
StartupCache::GetDebugObjectOutputStream(nsIObjectOutputStream* aStream,
nsIObjectOutputStream** aOutStream)
{
NS_ENSURE_ARG_POINTER(aStream);
#ifdef DEBUG
StartupCacheDebugOutputStream* stream
= new StartupCacheDebugOutputStream(aStream, &mWriteObjectMap);
NS_ADDREF(*aOutStream = stream);
#else
NS_ADDREF(*aOutStream = aStream);
#endif
return NS_OK;
}
// StartupCacheDebugOutputStream implementation
#ifdef DEBUG
NS_IMPL_ISUPPORTS3(StartupCacheDebugOutputStream, nsIObjectOutputStream,
nsIBinaryOutputStream, nsIOutputStream)
PRBool
StartupCacheDebugOutputStream::CheckReferences(nsISupports* aObject)
{
nsresult rv;
nsCOMPtr<nsIClassInfo> classInfo = do_QueryInterface(aObject);
if (!classInfo) {
NS_ERROR("aObject must implement nsIClassInfo");
return PR_FALSE;
}
PRUint32 flags;
rv = classInfo->GetFlags(&flags);
NS_ENSURE_SUCCESS(rv, rv);
if (flags & nsIClassInfo::SINGLETON)
return PR_TRUE;
nsISupportsHashKey* key = mObjectMap->GetEntry(aObject);
if (key) {
NS_ERROR("non-singleton aObject is referenced multiple times in this"
"serialization, we don't support that.");
return PR_FALSE;
}
mObjectMap->PutEntry(aObject);
return PR_TRUE;
}
// nsIObjectOutputStream implementation
nsresult
StartupCacheDebugOutputStream::WriteObject(nsISupports* aObject, PRBool aIsStrongRef)
{
nsresult rv;
nsCOMPtr<nsISupports> rootObject(do_QueryInterface(aObject));
NS_ASSERTION(rootObject.get() == aObject,
"bad call to WriteObject -- call WriteCompoundObject!");
PRBool check = CheckReferences(aObject);
NS_ENSURE_TRUE(check, NS_ERROR_FAILURE);
return mBinaryStream->WriteObject(aObject, aIsStrongRef);
}
nsresult
StartupCacheDebugOutputStream::WriteSingleRefObject(nsISupports* aObject)
{
nsresult rv;
nsCOMPtr<nsISupports> rootObject(do_QueryInterface(aObject));
NS_ASSERTION(rootObject.get() == aObject,
"bad call to WriteSingleRefObject -- call WriteCompoundObject!");
PRBool check = CheckReferences(aObject);
NS_ENSURE_TRUE(check, NS_ERROR_FAILURE);
return mBinaryStream->WriteSingleRefObject(aObject);
}
nsresult
StartupCacheDebugOutputStream::WriteCompoundObject(nsISupports* aObject,
const nsIID& aIID,
PRBool aIsStrongRef)
{
nsresult rv;
nsCOMPtr<nsISupports> rootObject(do_QueryInterface(aObject));
nsCOMPtr<nsISupports> roundtrip;
rootObject->QueryInterface(aIID, getter_AddRefs(roundtrip));
NS_ASSERTION(roundtrip.get() == aObject,
"bad aggregation or multiple inheritance detected by call to "
"WriteCompoundObject!");
PRBool check = CheckReferences(aObject);
NS_ENSURE_TRUE(check, NS_ERROR_FAILURE);
return mBinaryStream->WriteCompoundObject(aObject, aIID, aIsStrongRef);
}
nsresult
StartupCacheDebugOutputStream::WriteID(nsID const& aID)
{
return mBinaryStream->WriteID(aID);
}
char*
StartupCacheDebugOutputStream::GetBuffer(PRUint32 aLength, PRUint32 aAlignMask)
{
return mBinaryStream->GetBuffer(aLength, aAlignMask);
}
void
StartupCacheDebugOutputStream::PutBuffer(char* aBuffer, PRUint32 aLength)
{
mBinaryStream->PutBuffer(aBuffer, aLength);
}
#endif //DEBUG
StartupCacheWrapper* StartupCacheWrapper::gStartupCacheWrapper = nsnull;
NS_IMPL_THREADSAFE_ISUPPORTS1(StartupCacheWrapper, nsIStartupCache)
StartupCacheWrapper* StartupCacheWrapper::GetSingleton()
{
if (!gStartupCacheWrapper)
gStartupCacheWrapper = new StartupCacheWrapper();
NS_ADDREF(gStartupCacheWrapper);
return gStartupCacheWrapper;
}
nsresult
StartupCacheWrapper::GetBuffer(const char* id, char** outbuf, PRUint32* length)
{
StartupCache* sc = StartupCache::GetSingleton();
if (!sc) {
return NS_ERROR_NOT_INITIALIZED;
}
return sc->GetBuffer(id, outbuf, length);
}
nsresult
StartupCacheWrapper::PutBuffer(const char* id, char* inbuf, PRUint32 length)
{
StartupCache* sc = StartupCache::GetSingleton();
if (!sc) {
return NS_ERROR_NOT_INITIALIZED;
}
return sc->PutBuffer(id, inbuf, length);
}
nsresult
StartupCacheWrapper::InvalidateCache()
{
StartupCache* sc = StartupCache::GetSingleton();
if (!sc) {
return NS_ERROR_NOT_INITIALIZED;
}
sc->InvalidateCache();
return NS_OK;
}
nsresult
StartupCacheWrapper::GetDebugObjectOutputStream(nsIObjectOutputStream* stream,
nsIObjectOutputStream** outStream)
{
StartupCache* sc = StartupCache::GetSingleton();
if (!sc) {
return NS_ERROR_NOT_INITIALIZED;
}
return sc->GetDebugObjectOutputStream(stream, outStream);
}
nsresult
StartupCacheWrapper::StartupWriteComplete(PRBool *complete)
{
StartupCache* sc = StartupCache::GetSingleton();
if (!sc) {
return NS_ERROR_NOT_INITIALIZED;
}
*complete = sc->mStartupWriteInitiated && sc->mTable.Count() == 0;
return NS_OK;
}
nsresult
StartupCacheWrapper::ResetStartupWriteTimer()
{
StartupCache* sc = StartupCache::GetSingleton();
if (!sc) {
return NS_ERROR_NOT_INITIALIZED;
}
sc->mStartupWriteInitiated = PR_FALSE;
sc->mTimer->InitWithFuncCallback(StartupCache::WriteTimeout, sc, 10000,
nsITimer::TYPE_ONE_SHOT);
return NS_OK;
}
nsresult
StartupCacheWrapper::GetObserver(nsIObserver** obv) {
StartupCache* sc = StartupCache::GetSingleton();
if (!sc) {
return NS_ERROR_NOT_INITIALIZED;
}
NS_ADDREF(*obv = sc->mListener);
return NS_OK;
}
} // namespace scache
} // namespace mozilla

223
startupcache/StartupCache.h Normal file
View File

@ -0,0 +1,223 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Startup Cache.
*
* The Initial Developer of the Original Code is
* The Mozilla Foundation <http://www.mozilla.org/>.
* Portions created by the Initial Developer are Copyright (C) 2009
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Benedict Hsieh <bhsieh@mozilla.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef StartupCache_h_
#define StartupCache_h_
#include "prio.h"
#include "prtypes.h"
#include "nsClassHashtable.h"
#include "nsIZipWriter.h"
#include "nsIZipReader.h"
#include "nsComponentManagerUtils.h"
#include "nsZipArchive.h"
#include "nsIStartupCache.h"
#include "nsIStorageStream.h"
#include "nsITimer.h"
#include "nsIObserverService.h"
#include "nsIObserver.h"
#include "nsIOutputStream.h"
#include "nsIFile.h"
/**
* The StartupCache is a persistent cache of simple key-value pairs,
* where the keys are null-terminated c-strings and the values are
* arbitrary data, passed as a (char*, size) tuple.
*
* Clients should use the GetSingleton() static method to access the cache. It
* will be available from the end of XPCOM init (NS_InitXPCOM3 in nsXPComInit.cpp),
* until XPCOM shutdown begins. The GetSingleton() method will return null if the cache
* is unavailable. The cache is only provided for libxul builds --
* it will fail to link in non-libxul builds. The XPCOM interface is provided
* only to allow compiled-code tests; clients should avoid using it.
*
* The API provided is very simple: GetBuffer() returns a buffer that was previously
* stored in the cache (if any), and PutBuffer() inserts a buffer into the cache.
* GetBuffer returns a new buffer, and the caller must take ownership of it.
* PutBuffer will assert if the client attempts to insert a buffer with the same name as
* an existing entry. The cache makes a copy of the passed-in buffer, so client
* retains ownership.
*
* InvalidateCache() may be called if a client suspects data corruption
* or wishes to invalidate for any other reason. This will remove all existing cache data.
* Finally, getDebugObjectOutputStream() allows debug code to wrap an objectstream
* with a debug objectstream, to check for multiply-referenced objects. These will
* generally fail to deserialize correctly, unless they are stateless singletons or the
* client maintains their own object data map for deserialization.
*
* Writes before the final-ui-startup notification are placed in an intermediate
* cache in memory, then written out to disk at a later time, to get writes off the
* startup path. In any case, clients should not rely on being able to GetBuffer()
* data that is written to the cache, since it may not have been written to disk or
* another client may have invalidated the cache. In other words, it should be used as
* a cache only, and not a reliable persistent store.
*
* Some utility functions are provided in StartupCacheUtils. These functions wrap the
* buffers into object streams, which may be useful for serializing objects. Note
* the above caution about multiply-referenced objects, though -- the streams are just
* as 'dumb' as the underlying buffers about multiply-referenced objects. They just
* provide some convenience in writing out data.
*/
namespace mozilla {
namespace scache {
struct CacheEntry
{
nsAutoArrayPtr<char> data;
PRUint32 size;
CacheEntry() : data(nsnull), size(0) { }
// Takes possession of buf
CacheEntry(char* buf, PRUint32 len) : data(buf), size(len) { }
~CacheEntry()
{
}
};
// We don't want to refcount StartupCache, and ObserverService wants to
// refcount its listeners, so we'll let it refcount this instead.
class StartupCacheListener : public nsIObserver
{
NS_DECL_ISUPPORTS
NS_DECL_NSIOBSERVER
};
class StartupCache
{
friend class StartupCacheListener;
friend class StartupCacheWrapper;
public:
// StartupCache methods. See above comments for a more detailed description.
// Returns a buffer that was previously stored, caller takes ownership.
nsresult GetBuffer(const char* id, char** outbuf, PRUint32* length);
// Stores a buffer. Caller keeps ownership, we make a copy.
nsresult PutBuffer(const char* id, const char* inbuf, PRUint32 length);
// Removes the cache file.
void InvalidateCache();
// In DEBUG builds, returns a stream that will attempt to check for
// and disallow multiple writes of the same object.
nsresult GetDebugObjectOutputStream(nsIObjectOutputStream* aStream,
nsIObjectOutputStream** outStream);
static StartupCache* GetSingleton();
static void DeleteSingleton();
private:
StartupCache();
~StartupCache();
nsresult LoadArchive();
nsresult Init();
void WriteToDisk();
static nsresult InitSingleton();
static void WriteTimeout(nsITimer *aTimer, void *aClosure);
nsClassHashtable<nsCStringHashKey, CacheEntry> mTable;
nsCOMPtr<nsIZipWriter> mZipW;
nsAutoPtr<nsZipArchive> mArchive;
nsCOMPtr<nsILocalFile> mFile;
nsCOMPtr<nsIObserverService> mObserverService;
nsRefPtr<StartupCacheListener> mListener;
nsCOMPtr<nsITimer> mTimer;
PRBool mStartupWriteInitiated;
static StartupCache *gStartupCache;
static PRBool gShutdownInitiated;
#ifdef DEBUG
nsTHashtable<nsISupportsHashKey> mWriteObjectMap;
#endif
};
// This debug outputstream attempts to detect if clients are writing multiple
// references to the same object. We only support that if that object
// is a singleton.
#ifdef DEBUG
class StartupCacheDebugOutputStream
: public nsIObjectOutputStream
{
NS_DECL_ISUPPORTS
NS_DECL_NSIOBJECTOUTPUTSTREAM
StartupCacheDebugOutputStream (nsIObjectOutputStream* binaryStream,
nsTHashtable<nsISupportsHashKey>* objectMap)
: mBinaryStream(binaryStream), mObjectMap(objectMap) { }
NS_FORWARD_SAFE_NSIBINARYOUTPUTSTREAM(mBinaryStream)
NS_FORWARD_SAFE_NSIOUTPUTSTREAM(mBinaryStream)
PRBool CheckReferences(nsISupports* aObject);
nsCOMPtr<nsIObjectOutputStream> mBinaryStream;
nsTHashtable<nsISupportsHashKey> *mObjectMap;
};
#endif // DEBUG
// XPCOM wrapper interface provided for tests only.
#define NS_STARTUPCACHE_CID \
{0xae4505a9, 0x87ab, 0x477c, \
{0xb5, 0x77, 0xf9, 0x23, 0x57, 0xed, 0xa8, 0x84}}
// contract id: "@mozilla.org/startupcache/cache;1"
class StartupCacheWrapper
: public nsIStartupCache
{
NS_DECL_ISUPPORTS
NS_DECL_NSISTARTUPCACHE
static StartupCacheWrapper* GetSingleton();
static StartupCacheWrapper *gStartupCacheWrapper;
};
} // namespace scache
} // namespace mozilla
#endif //StartupCache_h_

View File

@ -0,0 +1,83 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Startup Cache.
*
* The Initial Developer of the Original Code is
* The Mozilla Foundation <http://www.mozilla.org/>.
* Portions created by the Initial Developer are Copyright (C) 2009
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Benedict Hsieh <bhsieh@mozilla.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include <string.h>
#include "nscore.h"
#include "pratom.h"
#include "prmem.h"
#include "prio.h"
#include "plstr.h"
#include "prlog.h"
#include "nsID.h"
#include "nsIComponentManager.h"
#include "nsIServiceManager.h"
#include "nsCOMPtr.h"
#include "nsIModule.h"
#include "mozilla/ModuleUtils.h"
#include "mozilla/scache/StartupCache.h"
using namespace mozilla::scache;
// XXX Need help with guard for ENABLE_TEST
NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(StartupCacheWrapper,
StartupCacheWrapper::GetSingleton)
NS_DEFINE_NAMED_CID(NS_STARTUPCACHE_CID);
static const mozilla::Module::CIDEntry kStartupCacheCIDs[] = {
{ &kNS_STARTUPCACHE_CID, false, NULL, StartupCacheWrapperConstructor },
{ NULL }
};
static const mozilla::Module::ContractIDEntry kStartupCacheContracts[] = {
{ "@mozilla.org/startupcache/cache;1", &kNS_STARTUPCACHE_CID },
{ NULL }
};
static const mozilla::Module kStartupCacheModule = {
mozilla::Module::kVersion,
kStartupCacheCIDs,
kStartupCacheContracts,
NULL,
NULL,
NULL,
NULL
};
NSMODULE_DEFN(StartupCacheModule) = &kStartupCacheModule;

View File

@ -0,0 +1,90 @@
#include "nsCOMPtr.h"
#include "nsIInputStream.h"
#include "nsIStringStream.h"
#include "nsAutoPtr.h"
#include "StartupCacheUtils.h"
#include "mozilla/scache/StartupCache.h"
namespace mozilla {
namespace scache {
NS_EXPORT nsresult
NS_NewObjectInputStreamFromBuffer(char* buffer, PRUint32 len,
nsIObjectInputStream** stream)
{
nsCOMPtr<nsIStringInputStream> stringStream
= do_CreateInstance("@mozilla.org/io/string-input-stream;1");
nsCOMPtr<nsIObjectInputStream> objectInput
= do_CreateInstance("@mozilla.org/binaryinputstream;1");
stringStream->AdoptData(buffer, len);
objectInput->SetInputStream(stringStream);
objectInput.forget(stream);
return NS_OK;
}
NS_EXPORT nsresult
NS_NewObjectOutputWrappedStorageStream(nsIObjectOutputStream **wrapperStream,
nsIStorageStream** stream)
{
nsresult rv;
nsCOMPtr<nsIStorageStream> storageStream
= do_CreateInstance("@mozilla.org/storagestream;1");
rv = storageStream->Init(256, PR_UINT32_MAX, nsnull);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIObjectOutputStream> objectOutput
= do_CreateInstance("@mozilla.org/binaryoutputstream;1");
nsCOMPtr<nsIOutputStream> outputStream
= do_QueryInterface(storageStream);
objectOutput->SetOutputStream(outputStream);
#ifdef DEBUG
// Wrap in debug stream to detect unsupported writes of
// multiply-referenced non-singleton objects
StartupCache* sc = StartupCache::GetSingleton();
NS_ENSURE_TRUE(sc, NS_ERROR_UNEXPECTED);
nsCOMPtr<nsIObjectOutputStream> debugStream;
sc->GetDebugObjectOutputStream(objectOutput, getter_AddRefs(debugStream));
debugStream.forget(wrapperStream);
#else
objectOutput.forget(wrapperStream);
#endif
storageStream.forget(stream);
return NS_OK;
}
NS_EXPORT nsresult
NS_NewBufferFromStorageStream(nsIStorageStream *storageStream,
char** buffer, PRUint32* len)
{
nsresult rv;
nsCOMPtr<nsIInputStream> inputStream;
rv = storageStream->NewInputStream(0, getter_AddRefs(inputStream));
NS_ENSURE_SUCCESS(rv, rv);
PRUint32 avail, read;
rv = inputStream->Available(&avail);
NS_ENSURE_SUCCESS(rv, rv);
nsAutoArrayPtr<char> temp (new char[avail]);
rv = inputStream->Read(temp, avail, &read);
if (NS_SUCCEEDED(rv) && avail != read)
rv = NS_ERROR_UNEXPECTED;
if (NS_FAILED(rv)) {
return rv;
}
*len = avail;
*buffer = temp.forget();
return NS_OK;
}
}
}

View File

@ -0,0 +1,63 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Startup Cache.
*
* The Initial Developer of the Original Code is
* The Mozilla Foundation <http://www.mozilla.org/>.
* Portions created by the Initial Developer are Copyright (C) 2009
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Benedict Hsieh <bhsieh@mozilla.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef nsStartupCacheUtils_h_
#define nsStartupCacheUtils_h_
#include "nsIStorageStream.h"
#include "nsIObjectInputStream.h"
#include "nsIObjectOutputStream.h"
namespace mozilla {
namespace scache {
NS_EXPORT nsresult
NS_NewObjectInputStreamFromBuffer(char* buffer, PRUint32 len,
nsIObjectInputStream** stream);
// We can't retrieve the wrapped stream from the objectOutputStream later,
// so we return it here.
NS_EXPORT nsresult
NS_NewObjectOutputWrappedStorageStream(nsIObjectOutputStream **wrapperStream,
nsIStorageStream** stream);
NS_EXPORT nsresult
NS_NewBufferFromStorageStream(nsIStorageStream *storageStream,
char** buffer, PRUint32* len);
}
}
#endif //nsStartupCacheUtils_h_

View File

@ -0,0 +1,76 @@
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla Corporation startup cache code.
*
* The Initial Developer of the Original Code is
* Mozilla Corporation
* Portions created by the Initial Developer are Copyright (C) 2009
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Benedict Hsieh <bhsieh@mozilla.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nsIInputStream.idl"
#include "nsISupports.idl"
#include "nsIObserver.idl"
#include "nsIObjectOutputStream.idl"
[uuid(de798fab-af49-4a61-8144-81550986e1da)]
interface nsIStartupCache : nsISupports
{
/** This interface is provided for testing purposes only, basically
* just to solve link vagaries. See docs in StartupCache.h
* GetBuffer, PutBuffer, and InvalidateCache act as described
* in that file. */
PRUint32 getBuffer(in string aID, out charPtr aBuffer);
void putBuffer(in string aID, in charPtr aBuffer,
in PRUint32 aLength);
void invalidateCache();
/** In debug builds, wraps this object output stream with a stream that will
* detect and prevent the write of a multiply-referenced non-singleton object
* during serialization. In non-debug, returns an add-ref'd pointer to
* original stream, unwrapped. */
nsIObjectOutputStream getDebugObjectOutputStream(in nsIObjectOutputStream aStream);
/* Allows clients to check whether the one-time writeout after startup
* has finished yet, and also to set this variable as needed (so test
* code can fire mulitple startup writes if needed).
*/
boolean startupWriteComplete();
void resetStartupWriteTimer();
/* Allows clients to simulate the behavior of ObserverService. */
readonly attribute nsIObserver observer;
};

View File

@ -0,0 +1,124 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Startup Cache.
*
* The Initial Developer of the Original Code is
* The Mozilla Foundation <http://www.mozilla.org/>.
* Portions created by the Initial Developer are Copyright (C) 2009
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Benedict Hsieh <bhsieh@mozilla.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nsStartupCacheUtils.h"
#include "nsCOMPtr.h"
#include "nsComponentManagerUtils.h"
#include "nsIInputStream.h"
#include "nsIStorageStream.h"
#include "nsIStringStream.h"
#include "nsIObjectInputStream.h"
#include "nsIObjectOutputStream.h"
nsresult
NS_NewObjectInputStreamFromBuffer(char* buffer, int len,
nsIObjectInputStream** stream)
{
nsCOMPtr<nsIStringInputStream> stringStream
= do_CreateInstance("@mozilla.org/io/string-input-stream;1");
if (!stringStream)
return NS_ERROR_OUT_OF_MEMORY;
nsCOMPtr<nsIObjectInputStream> objectInput
= do_CreateInstance("@mozilla.org/binaryinputstream;1");
if (!objectInput)
return NS_ERROR_OUT_OF_MEMORY;
stringStream->AdoptData(buffer, len);
objectInput->SetInputStream(stringStream);
NS_ADDREF(*stream = objectInput);
return NS_OK;
}
// This is questionable API name and design, but we can't
// retrieve the wrapped stream from the objectOutputStream later...
nsresult
NS_NewObjectOutputWrappedStorageStream(nsIObjectOutputStream **wrapperStream,
nsIStorageStream** stream)
{
nsCOMPtr<nsIStorageStream> storageStream;
nsresult rv = NS_NewStorageStream(256, (PRUint32)-1,
getter_AddRefs(storageStream));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIObjectOutputStream> objectOutput
= do_CreateInstance("@mozilla.org/binaryoutputstream;1");
if (!objectOutput)
return NS_ERROR_OUT_OF_MEMORY;
nsCOMPtr<nsIOutputStream> outputStream
= do_QueryInterface(storageStream);
objectOutput->SetOutputStream(outputStream);
NS_ADDREF(*wrapperStream = objectOutput);
NS_ADDREF(*stream = storageStream);
return NS_OK;
}
nsresult
NS_NewBufferFromStorageStream(nsIStorageStream *storageStream,
char** buffer, int* len)
{
nsresult rv;
nsCOMPtr<nsIInputStream> inputStream;
rv = storageStream->NewInputStream(0, getter_AddRefs(inputStream));
NS_ENSURE_SUCCESS(rv, rv);
PRUint32 avail, read;
rv = inputStream->Available(&avail);
NS_ENSURE_SUCCESS(rv, rv);
char* temp = new char[avail];
if (!temp)
return NS_ERROR_OUT_OF_MEMORY;
rv = inputStream->Read(temp, avail, &read);
if (NS_SUCCEEDED(rv) && avail != read)
rv = NS_ERROR_UNEXPECTED;
if (NS_FAILED(rv)) {
delete temp;
return rv;
}
*len = avail;
*buffer = temp;
return NS_OK;
}

View File

@ -0,0 +1,53 @@
#
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is mozilla.org code.
#
# The Initial Developer of the Original Code is
# Mozilla.org.
# Portions created by the Initial Developer are Copyright (C) 2010
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Benedict Hsieh <bhsieh@mozilla.com>
#
# Alternatively, the contents of this file may be used under the terms of
# either of the GNU General Public License Version 2 or later (the "GPL"),
# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# and other provisions required by the GPL or the LGPL. If you do not delete
# the provisions above, a recipient may use your version of this file under
# the terms of any one of the MPL, the GPL or the LGPL.
#
# ***** END LICENSE BLOCK *****
DEPTH = ../..
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
MODULE = test_startupcache
CPP_UNIT_TESTS = TestStartupCache.cpp
EXTRA_DSO_LIBS += xul
LIBS += $(MOZ_COMPONENT_LIBS)
include $(topsrcdir)/config/rules.mk

View File

@ -0,0 +1,332 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Startup Cache.
*
* The Initial Developer of the Original Code is
* The Mozilla Foundation <http://www.mozilla.org/>.
* Portions created by the Initial Developer are Copyright (C) 2009
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Benedict Hsieh <bhsieh@mozilla.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "TestHarness.h"
#include "nsThreadUtils.h"
#include "nsIClassInfo.h"
#include "nsIOutputStream.h"
#include "nsIObserver.h"
#include "nsISerializable.h"
#include "nsISupports.h"
#include "nsIStartupCache.h"
#include "nsIStringStream.h"
#include "nsIStorageStream.h"
#include "nsIObjectInputStream.h"
#include "nsIObjectOutputStream.h"
#include "nsIURI.h"
#include "nsStringAPI.h"
namespace mozilla {
namespace scache {
NS_IMPORT nsresult
NS_NewObjectInputStreamFromBuffer(char* buffer, PRUint32 len,
nsIObjectInputStream** stream);
// We can't retrieve the wrapped stream from the objectOutputStream later,
// so we return it here.
NS_IMPORT nsresult
NS_NewObjectOutputWrappedStorageStream(nsIObjectOutputStream **wrapperStream,
nsIStorageStream** stream);
NS_IMPORT nsresult
NS_NewBufferFromStorageStream(nsIStorageStream *storageStream,
char** buffer, PRUint32* len);
}
}
using namespace mozilla::scache;
#define NS_ENSURE_STR_MATCH(str1, str2, testname) \
PR_BEGIN_MACRO \
if (0 != strcmp(str1, str2)) { \
fail("failed " testname); \
return NS_ERROR_FAILURE; \
} \
passed("passed " testname); \
PR_END_MACRO
nsresult
WaitForStartupTimer() {
nsresult rv;
nsCOMPtr<nsIStartupCache> sc
= do_GetService("@mozilla.org/startupcache/cache;1");
PR_Sleep(10 * PR_TicksPerSecond());
PRBool complete;
while (true) {
NS_ProcessPendingEvents(nsnull);
rv = sc->StartupWriteComplete(&complete);
if (NS_FAILED(rv) || complete)
break;
PR_Sleep(1 * PR_TicksPerSecond());
}
return rv;
}
nsresult
TestStartupWriteRead() {
nsresult rv;
nsCOMPtr<nsIStartupCache> sc
= do_GetService("@mozilla.org/startupcache/cache;1", &rv);
if (!sc) {
fail("didn't get a pointer...");
return NS_ERROR_FAILURE;
} else {
passed("got a pointer?");
}
sc->InvalidateCache();
char* buf = "Market opportunities for BeardBook";
char* id = "id";
char* outbufPtr = NULL;
nsAutoArrayPtr<char> outbuf;
PRUint32 len;
rv = sc->PutBuffer(id, buf, strlen(buf) + 1);
NS_ENSURE_SUCCESS(rv, rv);
rv = sc->GetBuffer(id, &outbufPtr, &len);
NS_ENSURE_SUCCESS(rv, rv);
outbuf = outbufPtr;
NS_ENSURE_STR_MATCH(buf, outbuf, "pre-write read");
rv = WaitForStartupTimer();
NS_ENSURE_SUCCESS(rv, rv);
rv = sc->GetBuffer(id, &outbufPtr, &len);
NS_ENSURE_SUCCESS(rv, rv);
outbuf = outbufPtr;
NS_ENSURE_STR_MATCH(buf, outbuf, "simple write/read");
return NS_OK;
}
nsresult
TestWriteInvalidateRead() {
nsresult rv;
char* buf = "BeardBook competitive analysis";
char* id = "id";
char* outbuf = NULL;
PRUint32 len;
nsCOMPtr<nsIStartupCache> sc
= do_GetService("@mozilla.org/startupcache/cache;1", &rv);
sc->InvalidateCache();
rv = sc->PutBuffer(id, buf, strlen(buf) + 1);
NS_ENSURE_SUCCESS(rv, rv);
sc->InvalidateCache();
rv = sc->GetBuffer(id, &outbuf, &len);
delete[] outbuf;
if (rv == NS_ERROR_NOT_AVAILABLE) {
passed("buffer not available after invalidate");
} else if (NS_SUCCEEDED(rv)) {
fail("GetBuffer succeeded unexpectedly after invalidate");
return NS_ERROR_UNEXPECTED;
} else {
fail("GetBuffer gave an unexpected failure, expected NOT_AVAILABLE");
return rv;
}
sc->InvalidateCache();
return NS_OK;
}
nsresult
TestWriteObject() {
nsresult rv;
nsCOMPtr<nsIURI> obj
= do_CreateInstance("@mozilla.org/network/simple-uri;1");
if (!obj) {
fail("did not create object in test write object");
return NS_ERROR_UNEXPECTED;
}
NS_NAMED_LITERAL_CSTRING(spec, "http://www.mozilla.org");
obj->SetSpec(spec);
nsCOMPtr<nsIStartupCache> sc = do_GetService("@mozilla.org/startupcache/cache;1", &rv);
sc->InvalidateCache();
// Create an object stream. Usually this is done with
// NS_NewObjectOutputWrappedStorageStream, but that uses
// StartupCache::GetSingleton in debug builds, and we
// don't have access to that here. Obviously.
char* id = "id";
nsCOMPtr<nsIStorageStream> storageStream
= do_CreateInstance("@mozilla.org/storagestream;1");
NS_ENSURE_ARG_POINTER(storageStream);
rv = storageStream->Init(256, (PRUint32) -1, nsnull);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIObjectOutputStream> objectOutput
= do_CreateInstance("@mozilla.org/binaryoutputstream;1");
if (!objectOutput)
return NS_ERROR_OUT_OF_MEMORY;
nsCOMPtr<nsIOutputStream> outputStream
= do_QueryInterface(storageStream);
rv = objectOutput->SetOutputStream(outputStream);
if (NS_FAILED(rv)) {
fail("failed to create output stream");
return rv;
}
nsCOMPtr<nsISupports> objQI(do_QueryInterface(obj));
rv = objectOutput->WriteObject(objQI, PR_TRUE);
if (NS_FAILED(rv)) {
fail("failed to write object");
return rv;
}
char* bufPtr = NULL;
nsAutoArrayPtr<char> buf;
PRUint32 len;
NS_NewBufferFromStorageStream(storageStream, &bufPtr, &len);
buf = bufPtr;
// Since this is a post-startup write, it should be written and
// available.
rv = sc->PutBuffer(id, buf, len);
if (NS_FAILED(rv)) {
fail("failed to insert input stream");
return rv;
}
char* buf2Ptr = NULL;
nsAutoArrayPtr<char> buf2;
PRUint32 len2;
nsCOMPtr<nsIObjectInputStream> objectInput;
rv = sc->GetBuffer(id, &buf2Ptr, &len2);
if (NS_FAILED(rv)) {
fail("failed to retrieve buffer");
return rv;
}
buf2 = buf2Ptr;
rv = NS_NewObjectInputStreamFromBuffer(buf2, len2, getter_AddRefs(objectInput));
if (NS_FAILED(rv)) {
fail("failed to created input stream");
return rv;
}
buf2.forget();
nsCOMPtr<nsISupports> deserialized;
rv = objectInput->ReadObject(PR_TRUE, getter_AddRefs(deserialized));
if (NS_FAILED(rv)) {
fail("failed to read object");
return rv;
}
PRBool match = false;
nsCOMPtr<nsIURI> uri(do_QueryInterface(deserialized));
if (uri) {
nsCString outSpec;
uri->GetSpec(outSpec);
match = outSpec.Equals(spec);
}
if (!match) {
fail("deserialized object has incorrect information");
return rv;
}
passed("write object");
return NS_OK;
}
nsresult
TestEarlyShutdown() {
nsresult rv;
nsCOMPtr<nsIStartupCache> sc
= do_GetService("@mozilla.org/startupcache/cache;1", &rv);
sc->InvalidateCache();
char* buf = "Find your soul beardmate on BeardBook";
char* id = "id";
PRUint32 len;
char* outbuf = NULL;
sc->ResetStartupWriteTimer();
rv = sc->PutBuffer(buf, id, strlen(buf) + 1);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIObserver> obs;
sc->GetObserver(getter_AddRefs(obs));
obs->Observe(nsnull, "xpcom-shutdown", nsnull);
rv = WaitForStartupTimer();
NS_ENSURE_SUCCESS(rv, rv);
rv = sc->GetBuffer(id, &outbuf, &len);
delete[] outbuf;
if (rv == NS_ERROR_NOT_AVAILABLE) {
passed("buffer not available after early shutdown");
} else if (NS_SUCCEEDED(rv)) {
fail("GetBuffer succeeded unexpectedly after early shutdown");
return NS_ERROR_UNEXPECTED;
} else {
fail("GetBuffer gave an unexpected failure, expected NOT_AVAILABLE");
return rv;
}
return NS_OK;
}
int main(int argc, char** argv)
{
int rv = 0;
nsresult rv2;
ScopedXPCOM xpcom("Startup Cache");
if (NS_FAILED(TestStartupWriteRead()))
rv = 1;
if (NS_FAILED(TestWriteInvalidateRead()))
rv = 1;
if (NS_FAILED(TestWriteObject()))
rv = 1;
if (NS_FAILED(TestEarlyShutdown()))
rv = 1;
return rv;
}

View File

@ -139,6 +139,7 @@ COMPONENT_LIBS += \
i18n \
chardet \
jar$(VERSION_NUMBER) \
startupcache \
pref \
htmlpars \
imglib2 \

View File

@ -241,6 +241,7 @@
AUTH_MODULE \
MODULE(nsJarModule) \
ZIPWRITER_MODULE \
MODULE(StartupCacheModule) \
MODULE(nsPrefModule) \
RDF_MODULES \
MODULE(nsParserModule) \

View File

@ -779,6 +779,10 @@ MAKEFILES_extensions="
extensions/pref/autoconfig/src/Makefile
"
MAKEFILES_startupcache="
startupcache/Makefile
"
add_makefiles "
$MAKEFILES_db
$MAKEFILES_dom
@ -820,6 +824,7 @@ add_makefiles "
$MAKEFILES_libmar
$MAKEFILES_lib7z
$MAKEFILES_extensions
$MAKEFILES_startupcache
"
#
@ -926,6 +931,7 @@ if [ "$ENABLE_TESTS" ]; then
parser/htmlparser/tests/mochitest/Makefile
parser/xml/test/Makefile
rdf/tests/triplescat/Makefile
startupcache/tests/Makefile
testing/mochitest/Makefile
testing/mochitest/MochiKit/Makefile
testing/mochitest/chrome/Makefile

View File

@ -240,6 +240,10 @@ tier_platform_dirs += embedding/browser/gtk
endif
endif
ifdef MOZ_ENABLE_LIBXUL
tier_platform_dirs += startupcache
endif
ifndef BUILD_STATIC_LIBS
tier_platform_dirs += toolkit/library
endif

View File

@ -2491,6 +2491,9 @@ static void RemoveComponentRegistries(nsIFile* aProfileDir, nsIFile* aLocalProfi
file->SetNativeLeafName(NS_LITERAL_CSTRING("XPC" PLATFORM_FASL_SUFFIX));
file->Remove(PR_FALSE);
file->SetNativeLeafName(NS_LITERAL_CSTRING("startupCache"));
file->Remove(PR_TRUE);
}
// To support application initiated restart via nsIAppStartup.quit, we

View File

@ -147,6 +147,10 @@ extern nsresult nsStringInputStreamConstructor(nsISupports *, REFNSIID, void **)
#include "nsChromeRegistry.h"
#include "nsChromeProtocolHandler.h"
#ifdef MOZ_ENABLE_LIBXUL
#include "mozilla/scache/StartupCache.h"
#endif
#ifdef MOZ_IPC
#include "base/at_exit.h"
#include "base/command_line.h"
@ -542,6 +546,9 @@ NS_InitXPCOM2(nsIServiceManager* *result,
// to the directory service.
nsDirectoryService::gService->RegisterCategoryProviders();
#ifdef MOZ_ENABLE_LIBXUL
mozilla::scache::StartupCache::GetSingleton();
#endif
NS_TIME_FUNCTION_MARK("Next: create services from category");
// Notify observers of xpcom autoregistration start
@ -619,7 +626,9 @@ ShutdownXPCOM(nsIServiceManager* servMgr)
}
NS_ProcessPendingEvents(thread);
#ifdef MOZ_ENABLE_LIBXUL
mozilla::scache::StartupCache::DeleteSingleton();
#endif
if (observerService)
(void) observerService->
NotifyObservers(nsnull, NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID,

View File

@ -55,6 +55,7 @@
#include "nsIDirectoryService.h"
#include "nsIFile.h"
#include "nsIProperties.h"
#include "nsXULAppAPI.h"
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
@ -341,7 +342,8 @@ class ScopedXPCOM : public nsIDirectoryServiceProvider2
// Otherwise, the test harness provides some directories automatically.
if (0 == strcmp(aProperty, NS_APP_USER_PROFILE_50_DIR) ||
0 == strcmp(aProperty, NS_APP_USER_PROFILE_LOCAL_50_DIR)) {
0 == strcmp(aProperty, NS_APP_USER_PROFILE_LOCAL_50_DIR) ||
0 == strcmp(aProperty, NS_APP_PROFILE_LOCAL_DIR_STARTUP)) {
nsCOMPtr<nsIFile> profD = GetProfileDirectory();
NS_ENSURE_TRUE(profD, NS_ERROR_FAILURE);