Bug 699468: Part 5 - Clone the canonical DatabaseInfo when a Database is closed. r=bent

This commit is contained in:
Kyle Huey 2011-11-07 19:15:45 -05:00
parent 2ff0266dc0
commit 6ecd76ea59
6 changed files with 131 additions and 74 deletions

View File

@ -64,11 +64,32 @@ EnumerateObjectStoreNames(const nsAString& aKey,
return PL_DHASH_NEXT;
}
PLDHashOperator
CloneObjectStoreInfo(const nsAString& aKey,
ObjectStoreInfo* aData,
void* aUserArg)
{
ObjectStoreInfoHash* hash = static_cast<ObjectStoreInfoHash*>(aUserArg);
nsAutoPtr<ObjectStoreInfo> newInfo(new ObjectStoreInfo(*aData));
if (!hash->Put(aKey, newInfo)) {
NS_WARNING("Out of memory?");
return PL_DHASH_STOP;
}
newInfo.forget();
return PL_DHASH_NEXT;
}
}
DatabaseInfo::~DatabaseInfo()
{
DatabaseInfo::Remove(id);
// Clones are never in the hash.
if (!cloned) {
DatabaseInfo::Remove(id);
}
}
#ifdef NS_BUILD_REFCNT_LOGGING
@ -81,6 +102,16 @@ IndexInfo::IndexInfo()
MOZ_COUNT_CTOR(IndexInfo);
}
IndexInfo::IndexInfo(const IndexInfo& aOther)
: id(aOther.id),
name(aOther.name),
keyPath(aOther.keyPath),
unique(aOther.unique),
autoIncrement(aOther.autoIncrement)
{
MOZ_COUNT_CTOR(IndexInfo);
}
IndexInfo::~IndexInfo()
{
MOZ_COUNT_DTOR(IndexInfo);
@ -94,6 +125,17 @@ ObjectStoreInfo::ObjectStoreInfo()
MOZ_COUNT_CTOR(ObjectStoreInfo);
}
ObjectStoreInfo::ObjectStoreInfo(ObjectStoreInfo& aOther)
: name(aOther.name),
id(aOther.id),
keyPath(aOther.keyPath),
autoIncrement(aOther.autoIncrement),
databaseId(aOther.databaseId),
indexes(aOther.indexes)
{
MOZ_COUNT_CTOR(ObjectStoreInfo);
}
ObjectStoreInfo::~ObjectStoreInfo()
{
MOZ_COUNT_DTOR(ObjectStoreInfo);
@ -120,6 +162,7 @@ DatabaseInfo::Get(nsIAtom* aId,
if (gDatabaseHash &&
gDatabaseHash->Get(aId, aInfo)) {
NS_IF_ADDREF(*aInfo);
return true;
}
return false;
@ -180,20 +223,10 @@ bool
DatabaseInfo::GetObjectStoreNames(nsTArray<nsString>& aNames)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(Get(id, nsnull), "Don't know anything about this one!");
if (!gDatabaseHash) {
return false;
}
DatabaseInfo* info;
if (!gDatabaseHash->Get(id, &info)) {
return false;
}
aNames.Clear();
if (info->objectStoreHash) {
info->objectStoreHash->EnumerateRead(EnumerateObjectStoreNames, &aNames);
if (objectStoreHash) {
objectStoreHash->EnumerateRead(EnumerateObjectStoreNames, &aNames);
}
return true;
}
@ -202,14 +235,8 @@ bool
DatabaseInfo::ContainsStoreName(const nsAString& aName)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(Get(id, nsnull), "Don't know anything about this one!");
DatabaseInfo* dbInfo;
return gDatabaseHash &&
gDatabaseHash->Get(id, &dbInfo) &&
dbInfo->objectStoreHash &&
dbInfo->objectStoreHash->Get(aName, nsnull);
return objectStoreHash && objectStoreHash->Get(aName, nsnull);
}
bool
@ -258,3 +285,31 @@ DatabaseInfo::RemoveObjectStore(const nsAString& aName)
objectStoreHash->Remove(aName);
}
}
already_AddRefed<DatabaseInfo>
DatabaseInfo::Clone()
{
NS_ASSERTION(!cloned, "Should never clone a clone!");
nsRefPtr<DatabaseInfo> dbInfo(new DatabaseInfo());
dbInfo->cloned = true;
dbInfo->name = name;
dbInfo->version = version;
dbInfo->id = id;
dbInfo->filePath = filePath;
dbInfo->nextObjectStoreId = nextObjectStoreId;
dbInfo->nextIndexId = nextIndexId;
if (objectStoreHash) {
dbInfo->objectStoreHash = new ObjectStoreInfoHash();
if (!dbInfo->objectStoreHash->Init()) {
return nsnull;
}
objectStoreHash->EnumerateRead(CloneObjectStoreInfo,
dbInfo->objectStoreHash);
}
return dbInfo.forget();
}

View File

@ -61,7 +61,7 @@ struct DatabaseInfo
DatabaseInfo()
: nextObjectStoreId(1),
nextIndexId(1),
runningVersionChange(false)
cloned(false)
{ }
~DatabaseInfo();
@ -82,13 +82,15 @@ struct DatabaseInfo
void RemoveObjectStore(const nsAString& aName);
already_AddRefed<DatabaseInfo> Clone();
nsString name;
PRUint64 version;
nsIAtom* id;
nsString filePath;
PRInt64 nextObjectStoreId;
PRInt64 nextIndexId;
bool runningVersionChange;
bool cloned;
nsAutoPtr<ObjectStoreInfoHash> objectStoreHash;
@ -99,6 +101,7 @@ struct IndexInfo
{
#ifdef NS_BUILD_REFCNT_LOGGING
IndexInfo();
IndexInfo(const IndexInfo& aOther);
~IndexInfo();
#else
IndexInfo()
@ -116,6 +119,7 @@ struct ObjectStoreInfo
{
#ifdef NS_BUILD_REFCNT_LOGGING
ObjectStoreInfo();
ObjectStoreInfo(ObjectStoreInfo& aOther);
~ObjectStoreInfo();
#else
ObjectStoreInfo()

View File

@ -158,21 +158,24 @@ private:
already_AddRefed<IDBDatabase>
IDBDatabase::Create(nsIScriptContext* aScriptContext,
nsPIDOMWindow* aOwner,
DatabaseInfo* aDatabaseInfo,
already_AddRefed<DatabaseInfo> aDatabaseInfo,
const nsACString& aASCIIOrigin)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(aDatabaseInfo, "Null pointer!");
NS_ASSERTION(!aASCIIOrigin.IsEmpty(), "Empty origin!");
nsRefPtr<DatabaseInfo> databaseInfo(aDatabaseInfo);
NS_ASSERTION(databaseInfo, "Null pointer!");
nsRefPtr<IDBDatabase> db(new IDBDatabase());
db->mScriptContext = aScriptContext;
db->mOwner = aOwner;
db->mDatabaseId = aDatabaseInfo->id;
db->mName = aDatabaseInfo->name;
db->mFilePath = aDatabaseInfo->filePath;
db->mDatabaseId = databaseInfo->id;
db->mName = databaseInfo->name;
db->mFilePath = databaseInfo->filePath;
databaseInfo.swap(db->mDatabaseInfo);
db->mASCIIOrigin = aASCIIOrigin;
IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
@ -190,7 +193,8 @@ IDBDatabase::IDBDatabase()
: mDatabaseId(0),
mInvalidated(0),
mRegistered(false),
mClosed(false)
mClosed(false),
mRunningVersionChange(false)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
@ -205,7 +209,7 @@ IDBDatabase::~IDBDatabase()
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
if (mRegistered) {
CloseInternal();
CloseInternal(true);
IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
if (mgr) {
@ -213,11 +217,6 @@ IDBDatabase::~IDBDatabase()
}
}
if (mDatabaseId && !mInvalidated) {
DatabaseInfo* info = Info();
NS_RELEASE(info);
}
if (mListenerManager) {
mListenerManager->Disconnect();
}
@ -233,17 +232,6 @@ IDBDatabase::~IDBDatabase()
}
}
DatabaseInfo*
IDBDatabase::Info() const
{
DatabaseInfo* dbInfo = nsnull;
DebugOnly<bool> got = DatabaseInfo::Get(Id(), &dbInfo);
NS_ASSERTION(got && dbInfo, "This should never fail!");
return dbInfo;
}
bool
IDBDatabase::IsQuotaDisabled()
{
@ -313,10 +301,7 @@ IDBDatabase::Invalidate()
}
}
if (!PR_ATOMIC_SET(&mInvalidated, 1)) {
DatabaseInfo* info = Info();
NS_RELEASE(info);
}
mInvalidated = true;
}
bool
@ -326,11 +311,23 @@ IDBDatabase::IsInvalidated()
}
void
IDBDatabase::CloseInternal()
IDBDatabase::CloseInternal(bool aIsDead)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
if (!mClosed) {
// If we're getting called from Unlink, avoid cloning the DatabaseInfo.
{
nsRefPtr<DatabaseInfo> previousInfo;
mDatabaseInfo.swap(previousInfo);
if (!aIsDead) {
nsRefPtr<DatabaseInfo> clonedInfo = previousInfo->Clone();
clonedInfo.swap(mDatabaseInfo);
}
}
IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
if (mgr) {
mgr->OnDatabaseClosed(this);
@ -349,19 +346,15 @@ IDBDatabase::IsClosed()
void
IDBDatabase::EnterSetVersionTransaction()
{
DatabaseInfo* dbInfo = Info();
NS_ASSERTION(!dbInfo->runningVersionChange, "How did that happen?");
dbInfo->runningVersionChange = true;
NS_ASSERTION(!mRunningVersionChange, "How did that happen?");
mRunningVersionChange = true;
}
void
IDBDatabase::ExitSetVersionTransaction()
{
DatabaseInfo* dbInfo = Info();
NS_ASSERTION(dbInfo->runningVersionChange, "How did that happen?");
dbInfo->runningVersionChange = false;
NS_ASSERTION(mRunningVersionChange, "How did that happen?");
mRunningVersionChange = false;
}
void
@ -372,7 +365,7 @@ IDBDatabase::OnUnlink()
// We've been unlinked, at the very least we should be able to prevent further
// transactions from starting and unblock any other SetVersion callers.
Close();
CloseInternal(true);
// No reason for the IndexedDatabaseManager to track us any longer.
IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
@ -611,9 +604,7 @@ IDBDatabase::Transaction(const jsval& aStoreNames,
return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR;
}
DatabaseInfo* info = Info();
if (info->runningVersionChange) {
if (mRunningVersionChange) {
return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR;
}
@ -719,6 +710,7 @@ IDBDatabase::Transaction(const jsval& aStoreNames,
}
// Now check to make sure the object store names we collected actually exist.
DatabaseInfo* info = Info();
for (PRUint32 index = 0; index < storesToOpen.Length(); index++) {
if (!info->ContainsStoreName(storesToOpen[index])) {
return NS_ERROR_DOM_INDEXEDDB_NOT_FOUND_ERR;
@ -739,7 +731,7 @@ IDBDatabase::Close()
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
CloseInternal();
CloseInternal(false);
NS_ASSERTION(mClosed, "Should have set the closed flag!");
return NS_OK;

View File

@ -77,7 +77,7 @@ public:
static already_AddRefed<IDBDatabase>
Create(nsIScriptContext* aScriptContext,
nsPIDOMWindow* aOwner,
DatabaseInfo* aDatabaseInfo,
already_AddRefed<DatabaseInfo> aDatabaseInfo,
const nsACString& aASCIIOrigin);
// nsIDOMEventTarget
@ -88,7 +88,10 @@ public:
return mDatabaseId;
}
DatabaseInfo* Info() const;
DatabaseInfo* Info() const
{
return mDatabaseInfo;
}
const nsString& Name()
{
@ -132,7 +135,7 @@ public:
// transactions for this database will be allowed to run.
bool IsInvalidated();
void CloseInternal();
void CloseInternal(bool aIsDead);
// Whether or not the database has had Close called on it.
bool IsClosed();
@ -146,6 +149,7 @@ private:
void OnUnlink();
nsRefPtr<DatabaseInfo> mDatabaseInfo;
nsCOMPtr<nsIAtom> mDatabaseId;
nsString mName;
nsString mFilePath;
@ -154,6 +158,7 @@ private:
PRInt32 mInvalidated;
bool mRegistered;
bool mClosed;
bool mRunningVersionChange;
// Only touched on the main thread.
nsRefPtr<nsDOMEventListenerWrapper> mOnErrorListener;

View File

@ -880,9 +880,8 @@ OpenDatabaseHelper::Run()
nsresult
OpenDatabaseHelper::EnsureSuccessResult()
{
DatabaseInfo* dbInfo;
if (DatabaseInfo::Get(mDatabaseId, &dbInfo)) {
NS_ADDREF(dbInfo);
nsRefPtr<DatabaseInfo> dbInfo;
if (DatabaseInfo::Get(mDatabaseId, getter_AddRefs(dbInfo))) {
#ifdef DEBUG
{
@ -944,7 +943,7 @@ OpenDatabaseHelper::EnsureSuccessResult()
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
}
newInfo.forget(&dbInfo);
newInfo.swap(dbInfo);
nsresult rv = IDBFactory::UpdateDatabaseMetadata(dbInfo, mCurrentVersion,
mObjectStores);
@ -958,7 +957,9 @@ OpenDatabaseHelper::EnsureSuccessResult()
nsRefPtr<IDBDatabase> database =
IDBDatabase::Create(mOpenDBRequest->ScriptContext(),
mOpenDBRequest->Owner(), dbInfo, mASCIIOrigin);
mOpenDBRequest->Owner(),
dbInfo.forget(),
mASCIIOrigin);
if (!database) {
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
}

View File

@ -142,9 +142,9 @@
event = yield;
ok(event.target.result instanceof IDBDatabase, "Expect a database here");
is(event.target.result.version, 4, "Right version");
todo_is(db3.version, 3, "After closing the version should not change!");
todo_is(db2.version, 2, "After closing the version should not change!");
todo_is(db1.version, 1, "After closing the version should not change!");
is(db3.version, 3, "After closing the version should not change!");
is(db2.version, 2, "After closing the version should not change!");
is(db1.version, 1, "After closing the version should not change!");
is(versionChangeEventCount, 3, "Saw all expected events");