Bug 669410 - Make the url-classifier PrefixSet persistent on startup/shutdown. r=tony

This commit is contained in:
Gian-Carlo Pascutto 2011-09-08 22:16:59 +02:00
parent 8a3e4007ed
commit 3609767baa
5 changed files with 216 additions and 24 deletions

View File

@ -160,9 +160,9 @@
#define NS_TYPEAHEADFIND_CID \
{ 0xe7f70966, 0x9a37, 0x48d7, { 0x8a, 0xeb, 0x35, 0x99, 0x8f, 0x31, 0x09, 0x0e} }
// {6e9f759a-3f8d-4552-99ed-dbf0ea0a5f67}
// {42ef1d52-3351-4973-98f8-d18f089bccfa}
#define NS_URLCLASSIFIERPREFIXSET_CID \
{ 0x6e9f759a, 0x3f8d, 0x4552, { 0x99, 0xed, 0xdb, 0xf0, 0xea, 0x0a, 0x5f, 0x67} }
{ 0x42ef1d52, 0x3351, 0x4973, { 0x98, 0xf8, 0xd1, 0x8f, 0x08, 0x9b, 0xcc, 0xfa} }
// {5eb7c3c1-ec1f-4007-87cc-eefb37d68ce6}
#define NS_URLCLASSIFIERDBSERVICE_CID \

View File

@ -1,8 +1,9 @@
#include "nsISupports.idl"
#include "nsIFile.idl"
interface nsIArray;
[scriptable, uuid(6e9f759a-3f8d-4552-99ed-dbf0ea0a5f67)]
[scriptable, uuid(42ef1d52-3351-4973-98f8-d18f089bccfa)]
interface nsIUrlClassifierPrefixSet : nsISupports
{
void setPrefixes([const, array, size_is(aLength)] in unsigned long aPrefixes,
@ -12,4 +13,7 @@ interface nsIUrlClassifierPrefixSet : nsISupports
boolean contains(in unsigned long aPrefix);
boolean probe(in unsigned long aPrefix, inout boolean aReady);
PRUint32 estimateSize();
boolean isEmpty();
void loadFromFile(in nsIFile aFile);
void storeToFile(in nsIFile aFile);
};

View File

@ -138,6 +138,9 @@ static const PRLogModuleInfo *gUrlClassifierDbServiceLog = nsnull;
// and we start over.
#define IMPLEMENTATION_VERSION 7
// Name of the persistent PrefixSet storage
#define PREFIXSET_FILENAME "urlclassifier.pset"
#define MAX_HOST_COMPONENTS 5
#define MAX_PATH_COMPONENTS 4
@ -1192,14 +1195,16 @@ private:
PRInt32 count,
nsTArray<nsUrlClassifierLookupResult>& results);
// Construct a Prefix Tree with known prefixes
nsresult ConstructPrefixTree();
// Construct a Prefix Set with known prefixes
nsresult LoadPrefixSet(nsCOMPtr<nsIFile> & aFile);
nsresult ConstructPrefixSet();
// Set the SQLite cache size
nsresult SetCacheSize(mozIStorageConnection * aConnection,
PRInt32 aCacheSize);
nsCOMPtr<nsIFile> mDBFile;
nsCOMPtr<nsIFile> mPSFile;
nsCOMPtr<nsICryptoHash> mCryptoHash;
@ -1360,9 +1365,15 @@ nsUrlClassifierDBServiceWorker::Init(PRInt32 gethashNoise,
if (NS_FAILED(rv)) return NS_ERROR_NOT_AVAILABLE;
rv = mDBFile->Clone(getter_AddRefs(mPSFile));
NS_ENSURE_SUCCESS(rv, rv);
rv = mDBFile->Append(NS_LITERAL_STRING(DATABASE_FILENAME));
NS_ENSURE_SUCCESS(rv, rv);
rv = mPSFile->Append(NS_LITERAL_STRING(PREFIXSET_FILENAME));
NS_ENSURE_SUCCESS(rv, rv);
ResetUpdate();
mTableFreshness.Init();
@ -1399,7 +1410,7 @@ nsUrlClassifierDBService::CheckCleanHost(const nsACString &spec,
nsUrlClassifierDomainHash hostKeyHash;
hostKeyHash.FromPlaintext(lookupHosts[i], mHash);
// First probe the Prefix Tree for presence
// First probe the PrefixSet for presence
PRUint32 domainkey = hostKeyHash.ToUint32() ^ ENCODE_DOMAIN_MAGIC;
PRBool found;
@ -3131,7 +3142,7 @@ nsUrlClassifierDBServiceWorker::ApplyUpdate()
if (NS_SUCCEEDED(mUpdateStatus)) {
// Reconstruct the prefix tree from the DB
nsresult rv = ConstructPrefixTree();
nsresult rv = ConstructPrefixSet();
NS_ENSURE_SUCCESS(rv, rv);
}
@ -3219,6 +3230,7 @@ nsUrlClassifierDBServiceWorker::ResetDatabase()
NS_ENSURE_SUCCESS(rv, rv);
mDBFile->Remove(PR_FALSE);
mPSFile->Remove(PR_FALSE);
return NS_OK;
}
@ -3425,9 +3437,8 @@ nsUrlClassifierDBServiceWorker::OpenDb()
mCryptoHash = do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
LOG(("SB construcing Prefix Tree\n"));
rv = ConstructPrefixTree();
LOG(("loading Prefix Set\n"));
rv = LoadPrefixSet(mPSFile);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
@ -3495,7 +3506,7 @@ nsresult nsUrlClassifierStore::ReadPrefixes(nsTArray<PRUint32>& array)
}
nsresult
nsUrlClassifierDBServiceWorker::ConstructPrefixTree()
nsUrlClassifierDBServiceWorker::ConstructPrefixSet()
{
nsTArray<PRUint32> array;
nsresult rv = mMainStore.ReadPrefixes(array);
@ -3511,6 +3522,39 @@ nsUrlClassifierDBServiceWorker::ConstructPrefixTree()
// construct new one
rv = mPrefixSet->SetPrefixes(array.Elements(), array.Length());
NS_ENSURE_SUCCESS(rv, rv);
// store the new tree to disk
rv = mPrefixSet->StoreToFile(mPSFile);
NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "failed to store the prefixset");
return NS_OK;
}
nsresult
nsUrlClassifierDBServiceWorker::LoadPrefixSet(nsCOMPtr<nsIFile> & aFile)
{
PRBool empty;
nsresult rv = mPrefixSet->IsEmpty(&empty);
NS_ENSURE_SUCCESS(rv, rv);
if (!empty) {
LOG(("PrefixSet already loaded, not loading again"));
return NS_OK;
}
PRBool exists;
rv = aFile->Exists(&exists);
NS_ENSURE_SUCCESS(rv, rv);
if (exists) {
LOG(("stored PrefixSet exists, loading from disk"));
rv = mPrefixSet->LoadFromFile(aFile);
}
if (!exists || NS_FAILED(rv)) {
LOG(("no (usable) stored PrefixSet found, constructing from store"));
ConstructPrefixSet();
}
#ifdef DEBUG
PRUint32 size = 0;
rv = mPrefixSet->EstimateSize(&size);
@ -4310,6 +4354,7 @@ nsUrlClassifierDBService::Shutdown()
if (mWorker) {
rv = mWorkerProxy->CancelUpdate();
NS_ASSERTION(NS_SUCCEEDED(rv), "failed to post cancel update event");
rv = mWorkerProxy->CloseDb();
NS_ASSERTION(NS_SUCCEEDED(rv), "failed to post close db event");
}

View File

@ -40,13 +40,17 @@
#include "nsAutoPtr.h"
#include "nsCOMPtr.h"
#include "nsDebug.h"
#include "nsTArray.h"
#include "nsUrlClassifierPrefixSet.h"
#include "nsIUrlClassifierPrefixSet.h"
#include "nsIFile.h"
#include "nsILocalFile.h"
#include "nsToolkitCompsCID.h"
#include "nsTArray.h"
#include "nsThreadUtils.h"
#include "mozilla/Mutex.h"
#include "mozilla/FileUtils.h"
#include "prlog.h"
using namespace mozilla;
@ -61,11 +65,11 @@ static const PRLogModuleInfo *gUrlClassifierPrefixSetLog = nsnull;
#define LOG_ENABLED() (PR_FALSE)
#endif
NS_IMPL_THREADSAFE_ISUPPORTS1(nsUrlClassifierPrefixSet, nsIUrlClassifierPrefixSet);
NS_IMPL_THREADSAFE_ISUPPORTS1(nsUrlClassifierPrefixSet, nsIUrlClassifierPrefixSet)
nsUrlClassifierPrefixSet::nsUrlClassifierPrefixSet()
: mPrefixTreeLock("mPrefixTreeLock"),
mTreeIsReady(mPrefixTreeLock, "mTreeIsReady"),
: mPrefixSetLock("mPrefixSetLock"),
mSetIsReady(mPrefixSetLock, "mSetIsReady"),
mHasPrefixes(PR_FALSE)
{
#if defined(PR_LOGGING)
@ -79,7 +83,7 @@ NS_IMETHODIMP
nsUrlClassifierPrefixSet::SetPrefixes(const PRUint32 * aArray, PRUint32 aLength)
{
{
MutexAutoLock lock(mPrefixTreeLock);
MutexAutoLock lock(mPrefixSetLock);
if (mHasPrefixes) {
LOG(("Clearing PrefixSet"));
mDeltas.Clear();
@ -136,7 +140,7 @@ nsUrlClassifierPrefixSet::AddPrefixes(const PRUint32 * prefixes, PRUint32 aLengt
LOG(("Total number of indices: %d", mNewIndexPrefixes.Length()));
LOG(("Total number of deltas: %d", mNewDeltas.Length()));
MutexAutoLock lock(mPrefixTreeLock);
MutexAutoLock lock(mPrefixSetLock);
// This just swaps some pointers
mIndexPrefixes.SwapElements(mNewIndexPrefixes);
@ -144,7 +148,7 @@ nsUrlClassifierPrefixSet::AddPrefixes(const PRUint32 * prefixes, PRUint32 aLengt
mDeltas.SwapElements(mNewDeltas);
mHasPrefixes = PR_TRUE;
mTreeIsReady.NotifyAll();
mSetIsReady.NotifyAll();
return NS_OK;
}
@ -217,7 +221,7 @@ nsUrlClassifierPrefixSet::Contains(PRUint32 aPrefix, PRBool * aFound)
NS_IMETHODIMP
nsUrlClassifierPrefixSet::EstimateSize(PRUint32 * aSize)
{
MutexAutoLock lock(mPrefixTreeLock);
MutexAutoLock lock(mPrefixSetLock);
*aSize = sizeof(PRBool);
if (mHasPrefixes) {
*aSize += sizeof(PRUint16) * mDeltas.Length();
@ -227,17 +231,25 @@ nsUrlClassifierPrefixSet::EstimateSize(PRUint32 * aSize)
return NS_OK;
}
NS_IMETHODIMP
nsUrlClassifierPrefixSet::IsEmpty(PRBool * aEmpty)
{
MutexAutoLock lock(mPrefixSetLock);
*aEmpty = !mHasPrefixes;
return NS_OK;
}
NS_IMETHODIMP
nsUrlClassifierPrefixSet::Probe(PRUint32 aPrefix, PRBool * aReady, PRBool * aFound)
{
MutexAutoLock lock(mPrefixTreeLock);
MutexAutoLock lock(mPrefixSetLock);
// check whether we are opportunistically probing or should wait
if (*aReady) {
// we should block until we are ready
while (!mHasPrefixes) {
LOG(("Tree is empty, probe must wait"));
mTreeIsReady.Wait();
LOG(("Set is empty, probe must wait"));
mSetIsReady.Wait();
}
} else {
// opportunistic probe -> check if set is loaded
@ -253,3 +265,126 @@ nsUrlClassifierPrefixSet::Probe(PRUint32 aPrefix, PRBool * aReady, PRBool * aFou
return NS_OK;
}
nsresult
nsUrlClassifierPrefixSet::LoadFromFd(AutoFDClose & fileFd)
{
PRUint32 magic;
PRInt32 read;
read = PR_Read(fileFd, &magic, sizeof(PRUint32));
NS_ENSURE_TRUE(read > 0, NS_ERROR_FAILURE);
if (magic == PREFIXSET_VERSION_MAGIC) {
PRUint32 indexSize;
PRUint32 deltaSize;
read = PR_Read(fileFd, &indexSize, sizeof(PRUint32));
NS_ENSURE_TRUE(read > 0, NS_ERROR_FAILURE);
read = PR_Read(fileFd, &deltaSize, sizeof(PRUint32));
NS_ENSURE_TRUE(read > 0, NS_ERROR_FAILURE);
if (indexSize == 0) {
LOG(("stored PrefixSet is empty!"));
return NS_ERROR_FAILURE;
}
nsTArray<PRUint32> mNewIndexPrefixes;
nsTArray<PRUint32> mNewIndexStarts;
nsTArray<PRUint16> mNewDeltas;
mNewIndexStarts.SetLength(indexSize);
mNewIndexPrefixes.SetLength(indexSize);
mNewDeltas.SetLength(deltaSize);
read = PR_Read(fileFd, mNewIndexPrefixes.Elements(), indexSize*sizeof(PRUint32));
NS_ENSURE_TRUE(read > 0, NS_ERROR_FAILURE);
read = PR_Read(fileFd, mNewIndexStarts.Elements(), indexSize*sizeof(PRUint32));
NS_ENSURE_TRUE(read > 0, NS_ERROR_FAILURE);
if (deltaSize > 0) {
read = PR_Read(fileFd, mNewDeltas.Elements(), deltaSize*sizeof(PRUint16));
NS_ENSURE_TRUE(read > 0, NS_ERROR_FAILURE);
}
MutexAutoLock lock(mPrefixSetLock);
mIndexPrefixes.SwapElements(mNewIndexPrefixes);
mIndexStarts.SwapElements(mNewIndexStarts);
mDeltas.SwapElements(mNewDeltas);
mHasPrefixes = PR_TRUE;
mSetIsReady.NotifyAll();
} else {
LOG(("Version magic mismatch, not loading"));
return NS_ERROR_FAILURE;
}
LOG(("Loading PrefixSet successful"));
return NS_OK;
}
NS_IMETHODIMP
nsUrlClassifierPrefixSet::LoadFromFile(nsIFile * aFile)
{
nsresult rv;
nsCOMPtr<nsILocalFile> file(do_QueryInterface(aFile, &rv));
NS_ENSURE_SUCCESS(rv, rv);
AutoFDClose fileFd;
rv = file->OpenNSPRFileDesc(PR_RDONLY, 0, &fileFd);
NS_ENSURE_SUCCESS(rv, rv);
return LoadFromFd(fileFd);
}
nsresult
nsUrlClassifierPrefixSet::StoreToFd(AutoFDClose & fileFd)
{
PRInt32 written;
PRUint32 magic = PREFIXSET_VERSION_MAGIC;
written = PR_Write(fileFd, &magic, sizeof(PRUint32));
NS_ENSURE_TRUE(written > 0, NS_ERROR_FAILURE);
PRUint32 indexSize = mIndexStarts.Length();
PRUint32 deltaSize = mDeltas.Length();
written = PR_Write(fileFd, &indexSize, sizeof(PRUint32));
NS_ENSURE_TRUE(written > 0, NS_ERROR_FAILURE);
written = PR_Write(fileFd, &deltaSize, sizeof(PRUint32));
NS_ENSURE_TRUE(written > 0, NS_ERROR_FAILURE);
written = PR_Write(fileFd, mIndexPrefixes.Elements(), indexSize * sizeof(PRUint32));
NS_ENSURE_TRUE(written > 0, NS_ERROR_FAILURE);
written = PR_Write(fileFd, mIndexStarts.Elements(), indexSize * sizeof(PRUint32));
NS_ENSURE_TRUE(written > 0, NS_ERROR_FAILURE);
if (deltaSize > 0) {
written = PR_Write(fileFd, mDeltas.Elements(), deltaSize * sizeof(PRUint16));
NS_ENSURE_TRUE(written > 0, NS_ERROR_FAILURE);
}
LOG(("Saving PrefixSet successful\n"));
return NS_OK;
}
NS_IMETHODIMP
nsUrlClassifierPrefixSet::StoreToFile(nsIFile * aFile)
{
if (!mHasPrefixes) {
LOG(("Attempt to serialize empty PrefixSet"));
return NS_ERROR_FAILURE;
}
nsresult rv;
nsCOMPtr<nsILocalFile> file(do_QueryInterface(aFile, &rv));
NS_ENSURE_SUCCESS(rv, rv);
AutoFDClose fileFd;
rv = file->OpenNSPRFileDesc(PR_RDWR | PR_TRUNCATE | PR_CREATE_FILE,
0644, &fileFd);
NS_ENSURE_SUCCESS(rv, rv);
MutexAutoLock lock(mPrefixSetLock);
return StoreToFd(fileFd);
}

View File

@ -43,10 +43,12 @@
#include "nsISupportsUtils.h"
#include "nsID.h"
#include "nsIFile.h"
#include "nsIUrlClassifierPrefixSet.h"
#include "nsToolkitCompsCID.h"
#include "mozilla/Mutex.h"
#include "mozilla/CondVar.h"
#include "mozilla/FileUtils.h"
class nsUrlClassifierPrefixSet : public nsIUrlClassifierPrefixSet
{
@ -57,7 +59,7 @@ public:
// Can send an empty Array to clean the tree
NS_IMETHOD SetPrefixes(const PRUint32* aArray, PRUint32 aLength);
// Given prefixes must be in sorted order and bigger than
// anything currently in the Prefix Tree
// anything currently in the Prefix Set
NS_IMETHOD AddPrefixes(const PRUint32* aArray, PRUint32 aLength);
// Does the PrefixSet contain this prefix? not thread-safe
NS_IMETHOD Contains(PRUint32 aPrefix, PRBool* aFound);
@ -66,17 +68,23 @@ public:
// if not set, we will return in aReady whether we were ready or not
NS_IMETHOD Probe(PRUint32 aPrefix, PRBool* aReady, PRBool* aFound);
NS_IMETHOD EstimateSize(PRUint32* aSize);
NS_IMETHOD IsEmpty(PRBool * aEmpty);
NS_IMETHOD LoadFromFile(nsIFile * aFile);
NS_IMETHOD StoreToFile(nsIFile * aFile);
NS_DECL_ISUPPORTS
protected:
static const PRUint32 DELTAS_LIMIT = 100;
static const PRUint32 MAX_INDEX_DIFF = (1 << 16);
static const PRUint32 PREFIXSET_VERSION_MAGIC = 1;
mozilla::Mutex mPrefixTreeLock;
mozilla::CondVar mTreeIsReady;
mozilla::Mutex mPrefixSetLock;
mozilla::CondVar mSetIsReady;
PRUint32 BinSearch(PRUint32 start, PRUint32 end, PRUint32 target);
nsresult LoadFromFd(mozilla::AutoFDClose & fileFd);
nsresult StoreToFd(mozilla::AutoFDClose & fileFd);
// boolean indicating whether |setPrefixes| has been
// called with a non-empty array.