Bug 572223 - too much cookies.sqlite io. Part 2: async read. r=sdwilsh

This commit is contained in:
Dan Witte 2010-07-29 12:17:42 -07:00
Родитель 22ce2adbdf
Коммит 8741cf7c86
2 изменённых файлов: 159 добавлений и 79 удалений

Просмотреть файл

@ -357,9 +357,8 @@ LogSuccess(PRBool aSetCookie, nsIURI *aHostURI, const nsAFlatCString &aCookieStr
#define NS_ASSERT_SUCCESS(res) PR_BEGIN_MACRO /* nothing */ PR_END_MACRO
#endif
namespace {
/******************************************************************************
* DBListenerErrorHandler imp:
* DBListenerErrorHandler impl:
* Parent class for our async storage listeners that handles the logging of
* errors.
******************************************************************************/
@ -389,9 +388,8 @@ public:
NS_IMPL_ISUPPORTS1(DBListenerErrorHandler, mozIStorageStatementCallback)
/******************************************************************************
* InsertCookieDBListener imp:
* Static mozIStorageStatementCallback used to track asynchronous insertion
* operations.
* InsertCookieDBListener impl:
* mozIStorageStatementCallback used to track asynchronous insertion operations.
******************************************************************************/
class InsertCookieDBListener : public DBListenerErrorHandler
{
@ -411,9 +409,8 @@ public:
};
/******************************************************************************
* UpdateCookieDBListener imp:
* Static mozIStorageStatementCallback used to track asynchronous update
* operations.
* UpdateCookieDBListener impl:
* mozIStorageStatementCallback used to track asynchronous update operations.
******************************************************************************/
class UpdateCookieDBListener : public DBListenerErrorHandler
{
@ -433,9 +430,8 @@ public:
};
/******************************************************************************
* RemoveCookieDBListener imp:
* Static mozIStorageStatementCallback used to track asynchronous removal
* operations.
* RemoveCookieDBListener impl:
* mozIStorageStatementCallback used to track asynchronous removal operations.
******************************************************************************/
class RemoveCookieDBListener : public DBListenerErrorHandler
{
@ -454,6 +450,77 @@ public:
}
};
/******************************************************************************
* ReadCookieDBListener impl:
* mozIStorageStatementCallback used to track asynchronous removal operations.
******************************************************************************/
class ReadCookieDBListener : public DBListenerErrorHandler
{
protected:
virtual const char *GetOpType() { return "READ"; }
bool mCanceled;
public:
ReadCookieDBListener() : mCanceled(PR_FALSE) { }
void Cancel() { mCanceled = true; }
NS_IMETHOD HandleResult(mozIStorageResultSet *aResult)
{
nsresult rv;
nsCOMPtr<mozIStorageRow> row;
nsTArray<CookieDomainTuple> &cookieArray =
nsCookieService::gCookieService->mDefaultDBState.hostArray;
while (1) {
rv = aResult->GetNextRow(getter_AddRefs(row));
NS_ASSERT_SUCCESS(rv);
if (!row)
break;
CookieDomainTuple *tuple = cookieArray.AppendElement();
nsCookieService::gCookieService->ReadRow(row, *tuple);
}
return NS_OK;
}
NS_IMETHOD HandleCompletion(PRUint16 aReason)
{
// Process the completion of the read operation. If we have been canceled,
// we cannot assume that the cookieservice still has an open connection
// or that it even refers to the same database, so we must return early.
// Conversely, the cookieservice guarantees that if we have not been
// canceled, the database connection is still alive and we can safely
// operate on it.
if (mCanceled) {
// We may receive a REASON_FINISHED after being canceled;
// tweak the reason accordingly.
aReason = mozIStorageStatementCallback::REASON_CANCELED;
}
switch (aReason) {
case mozIStorageStatementCallback::REASON_FINISHED:
nsCookieService::gCookieService->ReadComplete();
break;
case mozIStorageStatementCallback::REASON_CANCELED:
// Nothing more to do here. The partially read data has already been
// thrown away.
COOKIE_LOGSTRING(PR_LOG_DEBUG, ("Read canceled"));
break;
case mozIStorageStatementCallback::REASON_ERROR:
// Nothing more to do here. DBListenerErrorHandler::HandleError()
// can take handle it.
COOKIE_LOGSTRING(PR_LOG_DEBUG, ("Read error"));
break;
default:
NS_NOTREACHED("invalid reason");
}
return NS_OK;
}
};
/******************************************************************************
* CloseCookieDBListener imp:
* Static mozIStorageCompletionCallback used to notify when the database is
@ -478,8 +545,6 @@ public:
NS_IMPL_ISUPPORTS1(CloseCookieDBListener, mozIStorageCompletionCallback)
} // anonymous namespace
/******************************************************************************
* nsCookieService impl:
* singleton instance ctor/dtor methods
@ -901,6 +966,20 @@ nsCookieService::CloseDB()
mDefaultDBState.stmtDelete = nsnull;
mDefaultDBState.stmtUpdate = nsnull;
if (mDefaultDBState.dbConn) {
// Cancel any pending read. No further results will be received by our
// read listener.
if (mDefaultDBState.pendingRead) {
NS_ASSERTION(mDefaultDBState.readListener, "no read listener");
mDefaultDBState.readListener->Cancel();
nsresult rv = mDefaultDBState.pendingRead->Cancel();
NS_ASSERT_SUCCESS(rv);
mDefaultDBState.pendingRead = nsnull;
mDefaultDBState.readListener = nsnull;
mDefaultDBState.hostArray.Clear();
}
mDefaultDBState.dbConn->AsyncClose(mCloseListener);
mDefaultDBState.dbConn = nsnull;
}
@ -1329,28 +1408,9 @@ nsCookieService::Remove(const nsACString &aHost,
nsresult
nsCookieService::Read()
{
nsresult rv;
// delete expired cookies, before we read in the db
{
// scope the deletion, so the write lock is released when finished
nsCOMPtr<mozIStorageStatement> stmtDeleteExpired;
rv = mDBState->dbConn->CreateStatement(NS_LITERAL_CSTRING(
"DELETE FROM moz_cookies WHERE expiry <= ?1"),
getter_AddRefs(stmtDeleteExpired));
NS_ENSURE_SUCCESS(rv, rv);
rv = stmtDeleteExpired->BindInt64ByIndex(0, PR_Now() / PR_USEC_PER_SEC);
NS_ENSURE_SUCCESS(rv, rv);
PRBool hasResult;
rv = stmtDeleteExpired->ExecuteStep(&hasResult);
NS_ENSURE_SUCCESS(rv, rv);
}
// let the reading begin!
// Let the reading begin!
nsCOMPtr<mozIStorageStatement> stmt;
rv = mDBState->dbConn->CreateStatement(NS_LITERAL_CSTRING(
nsresult rv = mDefaultDBState.dbConn->CreateStatement(NS_LITERAL_CSTRING(
"SELECT "
"id, "
"name, "
@ -1365,49 +1425,58 @@ nsCookieService::Read()
"FROM moz_cookies"), getter_AddRefs(stmt));
NS_ENSURE_SUCCESS(rv, rv);
nsCAutoString baseDomain, name, value, host, path;
PRBool hasResult;
while (NS_SUCCEEDED(rv = stmt->ExecuteStep(&hasResult)) && hasResult) {
PRInt64 creationID = stmt->AsInt64(0);
nsRefPtr<ReadCookieDBListener> readListener = new ReadCookieDBListener;
rv = stmt->ExecuteAsync(readListener,
getter_AddRefs(mDefaultDBState.pendingRead));
NS_ASSERT_SUCCESS(rv);
stmt->GetUTF8String(1, name);
stmt->GetUTF8String(2, value);
stmt->GetUTF8String(3, host);
stmt->GetUTF8String(4, path);
mDefaultDBState.readListener = readListener;
return NS_OK;
}
PRInt64 expiry = stmt->AsInt64(5);
PRInt64 lastAccessed = stmt->AsInt64(6);
PRBool isSecure = 0 != stmt->AsInt32(7);
PRBool isHttpOnly = 0 != stmt->AsInt32(8);
void
nsCookieService::ReadRow(mozIStorageRow *aRow, CookieDomainTuple &aTuple)
{
PRInt64 creationID = aRow->AsInt64(0);
stmt->GetUTF8String(9, baseDomain);
nsCString name, value, host, path;
aRow->GetUTF8String(1, name);
aRow->GetUTF8String(2, value);
aRow->GetUTF8String(3, host);
aRow->GetUTF8String(4, path);
// create a new nsCookie and assign the data.
nsCookie* newCookie =
nsCookie::Create(name, value, host, path,
expiry,
lastAccessed,
creationID,
PR_FALSE,
isSecure,
isHttpOnly);
if (!newCookie)
return NS_ERROR_OUT_OF_MEMORY;
PRInt64 expiry = aRow->AsInt64(5);
PRInt64 lastAccessed = aRow->AsInt64(6);
PRBool isSecure = 0 != aRow->AsInt32(7);
PRBool isHttpOnly = 0 != aRow->AsInt32(8);
if (!AddCookieToList(baseDomain, newCookie, NULL, PR_FALSE))
// It is purpose that created us; purpose that connects us;
// purpose that pulls us; that guides us; that drives us.
// It is purpose that defines us; purpose that binds us.
// When a cookie no longer has purpose, it has a choice:
// it can return to the source to be deleted, or it can go
// into exile, and stay hidden inside the Matrix.
// Let's choose deletion.
delete newCookie;
aRow->GetUTF8String(9, aTuple.baseDomain);
// Create a new nsCookie and assign the data.
aTuple.cookie =
nsCookie::Create(name, value, host, path,
expiry,
lastAccessed,
creationID,
PR_FALSE,
isSecure,
isHttpOnly);
}
void
nsCookieService::ReadComplete()
{
for (PRUint32 i = 0; i < mDefaultDBState.hostArray.Length(); ++i) {
const CookieDomainTuple &tuple = mDefaultDBState.hostArray[i];
AddCookieToList(tuple.baseDomain, tuple.cookie, NULL, PR_FALSE);
}
COOKIE_LOGSTRING(PR_LOG_DEBUG, ("Read(): %ld cookies read", mDBState->cookieCount));
mDefaultDBState.hostArray.Clear();
mDefaultDBState.pendingRead = nsnull;
mDefaultDBState.readListener = nsnull;
return rv;
COOKIE_LOGSTRING(PR_LOG_DEBUG, ("Read(): %ld cookies read",
mDefaultDBState.cookieCount));
}
NS_IMETHODIMP
@ -3015,7 +3084,7 @@ bindCookieParameters(mozIStorageBindingParamsArray *aParamsArray,
NS_ASSERT_SUCCESS(rv);
}
PRBool
void
nsCookieService::AddCookieToList(const nsCString &aBaseDomain,
nsCookie *aCookie,
mozIStorageBindingParamsArray *aParamsArray,
@ -3027,10 +3096,7 @@ nsCookieService::AddCookieToList(const nsCString &aBaseDomain,
"Do not have a DB connection but have a params array?");
nsCookieEntry *entry = mDBState->hostTable.PutEntry(aBaseDomain);
if (!entry) {
NS_ERROR("can't insert element into a null entry!");
return PR_FALSE;
}
NS_ASSERTION(entry, "can't insert element into a null entry!");
entry->GetCookies().AppendElement(aCookie);
++mDBState->cookieCount;
@ -3058,8 +3124,6 @@ nsCookieService::AddCookieToList(const nsCString &aBaseDomain,
NS_ASSERT_SUCCESS(rv);
}
}
return PR_TRUE;
}
void

Просмотреть файл

@ -54,7 +54,9 @@
#include "nsHashKeys.h"
#include "nsTHashtable.h"
#include "mozIStorageStatement.h"
#include "mozIStoragePendingStatement.h"
#include "mozIStorageConnection.h"
#include "mozIStorageRow.h"
class nsICookiePermission;
class nsIEffectiveTLDService;
@ -63,9 +65,9 @@ class nsIPrefBranch;
class nsIObserverService;
class nsIURI;
class nsIChannel;
class DBListenerErrorHandler;
class mozIStorageStatementCallback;
class mozIStorageCompletionCallback;
class ReadCookieDBListener;
struct nsCookieAttributes;
struct nsListIter;
@ -135,6 +137,13 @@ class nsCookieEntry : public PLDHashEntryHdr
ArrayType mCookies;
};
// encapsulates a (baseDomain, nsCookie) tuple for temporary storage purposes.
struct CookieDomainTuple
{
nsCString baseDomain;
nsRefPtr<nsCookie> cookie;
};
// encapsulates in-memory and on-disk DB states, so we can
// conveniently switch state when entering or exiting private browsing.
struct DBState
@ -148,6 +157,10 @@ struct DBState
nsCOMPtr<mozIStorageStatement> stmtInsert;
nsCOMPtr<mozIStorageStatement> stmtDelete;
nsCOMPtr<mozIStorageStatement> stmtUpdate;
nsCOMPtr<mozIStoragePendingStatement> pendingRead;
ReadCookieDBListener* readListener; // weak ref
nsTArray<CookieDomainTuple> hostArray;
};
// these constants represent a decision about a cookie based on user prefs.
@ -193,6 +206,8 @@ class nsCookieService : public nsICookieService
nsresult CreateTable();
void CloseDB();
nsresult Read();
void ReadRow(mozIStorageRow *aRow, CookieDomainTuple &aCookeTuple);
void ReadComplete();
nsresult NormalizeHost(nsCString &aHost);
nsresult GetBaseDomain(nsIURI *aHostURI, nsCString &aBaseDomain, PRBool &aRequireHostMatch);
nsresult GetBaseDomainFromHost(const nsACString &aHost, nsCString &aBaseDomain);
@ -201,7 +216,7 @@ class nsCookieService : public nsICookieService
PRBool SetCookieInternal(nsIURI *aHostURI, const nsCString& aBaseDomain, PRBool aRequireHostMatch, CookieStatus aStatus, nsDependentCString &aCookieHeader, PRInt64 aServerTime, PRBool aFromHttp);
void AddInternal(const nsCString& aBaseDomain, nsCookie *aCookie, PRInt64 aCurrentTimeInUsec, nsIURI *aHostURI, const char *aCookieHeader, PRBool aFromHttp);
void RemoveCookieFromList(const nsListIter &aIter, mozIStorageBindingParamsArray *aParamsArray = NULL);
PRBool AddCookieToList(const nsCString& aBaseDomain, nsCookie *aCookie, mozIStorageBindingParamsArray *aParamsArray, PRBool aWriteToDB = PR_TRUE);
void AddCookieToList(const nsCString& aBaseDomain, nsCookie *aCookie, mozIStorageBindingParamsArray *aParamsArray, PRBool aWriteToDB = PR_TRUE);
void UpdateCookieInList(nsCookie *aCookie, PRInt64 aLastAccessed, mozIStorageBindingParamsArray *aParamsArray);
static PRBool GetTokenValue(nsASingleFragmentCString::const_char_iterator &aIter, nsASingleFragmentCString::const_char_iterator &aEndIter, nsDependentCSubstring &aTokenString, nsDependentCSubstring &aTokenValue, PRBool &aEqualsFound);
static PRBool ParseAttributes(nsDependentCString &aCookieHeader, nsCookieAttributes &aCookie);
@ -247,8 +262,9 @@ class nsCookieService : public nsICookieService
PRUint16 mMaxCookiesPerHost;
PRInt64 mCookiePurgeAge;
// this callback needs access to member functions
// friends!
friend PLDHashOperator purgeCookiesCallback(nsCookieEntry *aEntry, void *aArg);
friend class ReadCookieDBListener;
static nsCookieService* GetSingleton();
#ifdef MOZ_IPC