summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorwolfbeast <mcwerewolf@gmail.com>2018-02-09 08:11:38 +0100
committerwolfbeast <mcwerewolf@gmail.com>2018-02-09 08:51:28 +0100
commit8cd777888a40e987ad536ab68421068f5c06d83b (patch)
treed046b56471878f2531f4c741db076f1622e3365f
parenta1a0b03046f68f4b0a22b9ec3086942551fbf469 (diff)
downloadUXP-8cd777888a40e987ad536ab68421068f5c06d83b.tar
UXP-8cd777888a40e987ad536ab68421068f5c06d83b.tar.gz
UXP-8cd777888a40e987ad536ab68421068f5c06d83b.tar.lz
UXP-8cd777888a40e987ad536ab68421068f5c06d83b.tar.xz
UXP-8cd777888a40e987ad536ab68421068f5c06d83b.zip
Fix a rare crash with asm.js caching.
-rw-r--r--dom/asmjscache/AsmJSCache.cpp336
-rw-r--r--dom/indexedDB/ActorsParent.cpp96
2 files changed, 316 insertions, 116 deletions
diff --git a/dom/asmjscache/AsmJSCache.cpp b/dom/asmjscache/AsmJSCache.cpp
index 4e1912f23..be54987a1 100644
--- a/dom/asmjscache/AsmJSCache.cpp
+++ b/dom/asmjscache/AsmJSCache.cpp
@@ -69,6 +69,8 @@ namespace asmjscache {
namespace {
+class ParentRunnable;
+
// Anything smaller should compile fast enough that caching will just add
// overhead.
static const size_t sMinCachedModuleLength = 10000;
@@ -76,6 +78,10 @@ static const size_t sMinCachedModuleLength = 10000;
// The number of characters to hash into the Metadata::Entry::mFastHash.
static const unsigned sNumFastHashChars = 4096;
+// Track all live parent actors.
+typedef nsTArray<const ParentRunnable*> ParentActorArray;
+StaticAutoPtr<ParentActorArray> sLiveParentActors;
+
nsresult
WriteMetadataFile(nsIFile* aMetadataFile, const Metadata& aMetadata)
{
@@ -236,6 +242,94 @@ EvictEntries(nsIFile* aDirectory, const nsACString& aGroup,
}
}
+/*******************************************************************************
+ * Client
+ ******************************************************************************/
+
+class Client
+ : public quota::Client
+{
+ static Client* sInstance;
+
+ bool mShutdownRequested;
+
+public:
+ Client();
+
+ static bool
+ IsShuttingDownOnBackgroundThread()
+ {
+ AssertIsOnBackgroundThread();
+
+ if (sInstance) {
+ return sInstance->IsShuttingDown();
+ }
+
+ return QuotaManager::IsShuttingDown();
+ }
+
+ static bool
+ IsShuttingDownOnNonBackgroundThread()
+ {
+ MOZ_ASSERT(!IsOnBackgroundThread());
+
+ return QuotaManager::IsShuttingDown();
+ }
+
+ bool
+ IsShuttingDown() const
+ {
+ AssertIsOnBackgroundThread();
+
+ return mShutdownRequested;
+ }
+
+ NS_INLINE_DECL_REFCOUNTING(Client, override)
+
+ virtual Type
+ GetType() override;
+
+ virtual nsresult
+ InitOrigin(PersistenceType aPersistenceType,
+ const nsACString& aGroup,
+ const nsACString& aOrigin,
+ const AtomicBool& aCanceled,
+ UsageInfo* aUsageInfo) override;
+
+ virtual nsresult
+ GetUsageForOrigin(PersistenceType aPersistenceType,
+ const nsACString& aGroup,
+ const nsACString& aOrigin,
+ const AtomicBool& aCanceled,
+ UsageInfo* aUsageInfo) override;
+
+ virtual void
+ OnOriginClearCompleted(PersistenceType aPersistenceType,
+ const nsACString& aOrigin)
+ override;
+
+ virtual void
+ ReleaseIOThreadObjects() override;
+
+ virtual void
+ AbortOperations(const nsACString& aOrigin) override;
+
+ virtual void
+ AbortOperationsForProcess(ContentParentId aContentParentId) override;
+
+ virtual void
+ StartIdleMaintenance() override;
+
+ virtual void
+ StopIdleMaintenance() override;
+
+ virtual void
+ ShutdownWorkThreads() override;
+
+private:
+ ~Client();
+};
+
// FileDescriptorHolder owns a file descriptor and its memory mapping.
// FileDescriptorHolder is derived by two runnable classes (that is,
// (Parent|Child)Runnable.
@@ -897,6 +991,13 @@ ParentRunnable::FinishOnOwningThread()
FileDescriptorHolder::Finish();
mDirectoryLock = nullptr;
+
+ MOZ_ASSERT(sLiveParentActors);
+ sLiveParentActors->RemoveElement(this);
+
+ if (sLiveParentActors->IsEmpty()) {
+ sLiveParentActors = nullptr;
+ }
}
NS_IMETHODIMP
@@ -1140,6 +1241,10 @@ AllocEntryParent(OpenMode aOpenMode,
{
AssertIsOnBackgroundThread();
+ if (NS_WARN_IF(Client::IsShuttingDownOnBackgroundThread())) {
+ return nullptr;
+ }
+
if (NS_WARN_IF(aPrincipalInfo.type() == PrincipalInfo::TNullPrincipalInfo)) {
MOZ_ASSERT(false);
return nullptr;
@@ -1148,6 +1253,12 @@ AllocEntryParent(OpenMode aOpenMode,
RefPtr<ParentRunnable> runnable =
new ParentRunnable(aPrincipalInfo, aOpenMode, aWriteParams);
+ if (!sLiveParentActors) {
+ sLiveParentActors = new ParentActorArray();
+ }
+
+ sLiveParentActors->AppendElement(runnable);
+
nsresult rv = NS_DispatchToMainThread(runnable);
NS_ENSURE_SUCCESS(rv, nullptr);
@@ -1712,128 +1823,163 @@ CloseEntryForWrite(size_t aSize,
}
}
-class Client : public quota::Client
+/*******************************************************************************
+ * Client
+ ******************************************************************************/
+
+Client* Client::sInstance = nullptr;
+
+Client::Client()
+ : mShutdownRequested(false)
{
- ~Client() {}
+ AssertIsOnBackgroundThread();
+ MOZ_ASSERT(!sInstance, "We expect this to be a singleton!");
-public:
- NS_IMETHOD_(MozExternalRefCountType)
- AddRef() override;
+ sInstance = this;
+}
- NS_IMETHOD_(MozExternalRefCountType)
- Release() override;
+Client::~Client()
+{
+ AssertIsOnBackgroundThread();
+ MOZ_ASSERT(sInstance == this, "We expect this to be a singleton!");
- virtual Type
- GetType() override
- {
- return ASMJS;
- }
+ sInstance = nullptr;
+}
- virtual nsresult
- InitOrigin(PersistenceType aPersistenceType,
- const nsACString& aGroup,
- const nsACString& aOrigin,
- const AtomicBool& aCanceled,
- UsageInfo* aUsageInfo) override
- {
- if (!aUsageInfo) {
- return NS_OK;
- }
- return GetUsageForOrigin(aPersistenceType,
- aGroup,
- aOrigin,
- aCanceled,
- aUsageInfo);
- }
+Client::Type
+Client::GetType()
+{
+ return ASMJS;
+}
- virtual nsresult
- GetUsageForOrigin(PersistenceType aPersistenceType,
- const nsACString& aGroup,
- const nsACString& aOrigin,
- const AtomicBool& aCanceled,
- UsageInfo* aUsageInfo) override
- {
- QuotaManager* qm = QuotaManager::Get();
- MOZ_ASSERT(qm, "We were being called by the QuotaManager");
+nsresult
+Client::InitOrigin(PersistenceType aPersistenceType,
+ const nsACString& aGroup,
+ const nsACString& aOrigin,
+ const AtomicBool& aCanceled,
+ UsageInfo* aUsageInfo)
+{
+ if (!aUsageInfo) {
+ return NS_OK;
+ }
+ return GetUsageForOrigin(aPersistenceType,
+ aGroup,
+ aOrigin,
+ aCanceled,
+ aUsageInfo);
+}
- nsCOMPtr<nsIFile> directory;
- nsresult rv = qm->GetDirectoryForOrigin(aPersistenceType, aOrigin,
- getter_AddRefs(directory));
- NS_ENSURE_SUCCESS(rv, rv);
- MOZ_ASSERT(directory, "We're here because the origin directory exists");
+nsresult
+Client::GetUsageForOrigin(PersistenceType aPersistenceType,
+ const nsACString& aGroup,
+ const nsACString& aOrigin,
+ const AtomicBool& aCanceled,
+ UsageInfo* aUsageInfo)
+{
+ QuotaManager* qm = QuotaManager::Get();
+ MOZ_ASSERT(qm, "We were being called by the QuotaManager");
- rv = directory->Append(NS_LITERAL_STRING(ASMJSCACHE_DIRECTORY_NAME));
- NS_ENSURE_SUCCESS(rv, rv);
+ nsCOMPtr<nsIFile> directory;
+ nsresult rv = qm->GetDirectoryForOrigin(aPersistenceType, aOrigin,
+ getter_AddRefs(directory));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
- DebugOnly<bool> exists;
- MOZ_ASSERT(NS_SUCCEEDED(directory->Exists(&exists)) && exists);
+ MOZ_ASSERT(directory, "We're here because the origin directory exists");
- nsCOMPtr<nsISimpleEnumerator> entries;
- rv = directory->GetDirectoryEntries(getter_AddRefs(entries));
- NS_ENSURE_SUCCESS(rv, rv);
+ rv = directory->Append(NS_LITERAL_STRING(ASMJSCACHE_DIRECTORY_NAME));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
- bool hasMore;
- while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) &&
- hasMore && !aCanceled) {
- nsCOMPtr<nsISupports> entry;
- rv = entries->GetNext(getter_AddRefs(entry));
- NS_ENSURE_SUCCESS(rv, rv);
+ DebugOnly<bool> exists;
+ MOZ_ASSERT(NS_SUCCEEDED(directory->Exists(&exists)) && exists);
- nsCOMPtr<nsIFile> file = do_QueryInterface(entry);
- NS_ENSURE_TRUE(file, NS_NOINTERFACE);
+ nsCOMPtr<nsISimpleEnumerator> entries;
+ rv = directory->GetDirectoryEntries(getter_AddRefs(entries));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
- int64_t fileSize;
- rv = file->GetFileSize(&fileSize);
- NS_ENSURE_SUCCESS(rv, rv);
+ bool hasMore;
+ while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) &&
+ hasMore && !aCanceled) {
+ nsCOMPtr<nsISupports> entry;
+ rv = entries->GetNext(getter_AddRefs(entry));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
- MOZ_ASSERT(fileSize >= 0, "Negative size?!");
+ nsCOMPtr<nsIFile> file = do_QueryInterface(entry);
+ if (NS_WARN_IF(!file)) {
+ return NS_NOINTERFACE;
+ }
- // Since the client is not explicitly storing files, append to database
- // usage which represents implicit storage allocation.
- aUsageInfo->AppendToDatabaseUsage(uint64_t(fileSize));
+ int64_t fileSize;
+ rv = file->GetFileSize(&fileSize);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
}
- NS_ENSURE_SUCCESS(rv, rv);
- return NS_OK;
+ MOZ_ASSERT(fileSize >= 0, "Negative size?!");
+
+ // Since the client is not explicitly storing files, append to database
+ // usage which represents implicit storage allocation.
+ aUsageInfo->AppendToDatabaseUsage(uint64_t(fileSize));
+ }
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
}
- virtual void
- OnOriginClearCompleted(PersistenceType aPersistenceType,
- const nsACString& aOrigin)
- override
- { }
+ return NS_OK;
+}
- virtual void
- ReleaseIOThreadObjects() override
- { }
+void
+Client::OnOriginClearCompleted(PersistenceType aPersistenceType,
+ const nsACString& aOrigin)
+{
+}
- virtual void
- AbortOperations(const nsACString& aOrigin) override
- { }
+void
+Client::ReleaseIOThreadObjects()
+{
+}
- virtual void
- AbortOperationsForProcess(ContentParentId aContentParentId) override
- { }
+void
+Client::AbortOperations(const nsACString& aOrigin)
+{
+}
- virtual void
- StartIdleMaintenance() override
- { }
+void
+Client::AbortOperationsForProcess(ContentParentId aContentParentId)
+{
+}
- virtual void
- StopIdleMaintenance() override
- { }
+void
+Client::StartIdleMaintenance()
+{
+}
- virtual void
- ShutdownWorkThreads() override
- { }
+void
+Client::StopIdleMaintenance()
+{
+}
-private:
- nsAutoRefCnt mRefCnt;
- NS_DECL_OWNINGTHREAD
-};
+void
+Client::ShutdownWorkThreads()
+{
+ AssertIsOnBackgroundThread();
+
+ if (sLiveParentActors) {
+ nsIThread* currentThread = NS_GetCurrentThread();
+ MOZ_ASSERT(currentThread);
-NS_IMPL_ADDREF(asmjscache::Client)
-NS_IMPL_RELEASE(asmjscache::Client)
+ do {
+ MOZ_ALWAYS_TRUE(NS_ProcessNextEvent(currentThread));
+ } while (!sLiveParentActors);
+ }
+}
quota::Client*
CreateClient()
diff --git a/dom/indexedDB/ActorsParent.cpp b/dom/indexedDB/ActorsParent.cpp
index c0cb69149..9d4e72f2f 100644
--- a/dom/indexedDB/ActorsParent.cpp
+++ b/dom/indexedDB/ActorsParent.cpp
@@ -18185,11 +18185,25 @@ QuotaClient::ShutdownWorkThreads()
mShutdownRequested = true;
+ // Shutdown maintenance thread pool (this spins the event loop until all
+ // threads are gone). This should release any maintenance related quota
+ // objects.
if (mMaintenanceThreadPool) {
mMaintenanceThreadPool->Shutdown();
mMaintenanceThreadPool = nullptr;
}
+ // Let any runnables dispatched from dying maintenance threads to be
+ // processed. This should release any maintenance related directory locks.
+ if (mCurrentMaintenance) {
+ nsIThread* currentThread = NS_GetCurrentThread();
+ MOZ_ASSERT(currentThread);
+
+ do {
+ MOZ_ALWAYS_TRUE(NS_ProcessNextEvent(currentThread));
+ } while (!mCurrentMaintenance);
+ }
+
RefPtr<ConnectionPool> connectionPool = gConnectionPool.get();
if (connectionPool) {
connectionPool->Shutdown();
@@ -18409,7 +18423,8 @@ Maintenance::Start()
AssertIsOnBackgroundThread();
MOZ_ASSERT(mState == State::Initial);
- if (IsAborted()) {
+ if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
+ IsAborted()) {
return NS_ERROR_ABORT;
}
@@ -18433,7 +18448,8 @@ Maintenance::CreateIndexedDatabaseManager()
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mState == State::CreateIndexedDatabaseManager);
- if (IsAborted()) {
+ if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonBackgroundThread()) ||
+ IsAborted()) {
return NS_ERROR_ABORT;
}
@@ -18458,7 +18474,8 @@ Maintenance::OpenDirectory()
MOZ_ASSERT(!mDirectoryLock);
MOZ_ASSERT(QuotaManager::Get());
- if (IsAborted()) {
+ if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
+ IsAborted()) {
return NS_ERROR_ABORT;
}
@@ -18482,7 +18499,8 @@ Maintenance::DirectoryOpen()
MOZ_ASSERT(mState == State::DirectoryOpenPending);
MOZ_ASSERT(mDirectoryLock);
- if (IsAborted()) {
+ if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
+ IsAborted()) {
return NS_ERROR_ABORT;
}
@@ -18512,7 +18530,8 @@ Maintenance::DirectoryWork()
// We have to find all database files that match any persistence type and any
// origin. We ignore anything out of the ordinary for now.
- if (IsAborted()) {
+ if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonBackgroundThread()) ||
+ IsAborted()) {
return NS_ERROR_ABORT;
}
@@ -18699,8 +18718,10 @@ Maintenance::DirectoryWork()
continue;
}
+ nsCString suffix;
nsCString group;
nsCString origin;
+ bool isApp;
nsTArray<nsString> databasePaths;
while (true) {
@@ -18756,19 +18777,18 @@ Maintenance::DirectoryWork()
// Found a database.
if (databasePaths.IsEmpty()) {
+ MOZ_ASSERT(suffix.IsEmpty());
MOZ_ASSERT(group.IsEmpty());
MOZ_ASSERT(origin.IsEmpty());
int64_t dummyTimeStamp;
- nsCString dummySuffix;
- bool dummyIsApp;
if (NS_WARN_IF(NS_FAILED(
quotaManager->GetDirectoryMetadata2(originDir,
&dummyTimeStamp,
- dummySuffix,
+ suffix,
group,
origin,
- &dummyIsApp)))) {
+ &isApp)))) {
// Not much we can do here...
continue;
}
@@ -18784,6 +18804,22 @@ Maintenance::DirectoryWork()
group,
origin,
Move(databasePaths)));
+
+ nsCOMPtr<nsIFile> directory;
+
+ // Idle maintenance may occur before origin is initailized.
+ // Ensure origin is initialized first. It will initialize all origins
+ // for temporary storage including IDB origins.
+ rv = quotaManager->EnsureOriginIsInitialized(persistenceType,
+ suffix,
+ group,
+ origin,
+ isApp,
+ getter_AddRefs(directory));
+
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
}
}
}
@@ -18835,6 +18871,11 @@ Maintenance::BeginDatabaseMaintenance()
}
};
+ if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
+ IsAborted()) {
+ return NS_ERROR_ABORT;
+ }
+
RefPtr<nsThreadPool> threadPool;
for (DirectoryInfo& directoryInfo : mDirectoryInfos) {
@@ -19021,6 +19062,11 @@ DatabaseMaintenance::PerformMaintenanceOnDatabase()
}
};
+ if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonBackgroundThread()) ||
+ mMaintenance->IsAborted()) {
+ return;
+ }
+
nsCOMPtr<nsIFile> databaseFile = GetFileForPath(mDatabasePath);
MOZ_ASSERT(databaseFile);
@@ -19037,10 +19083,6 @@ DatabaseMaintenance::PerformMaintenanceOnDatabase()
AutoClose autoClose(connection);
- if (mMaintenance->IsAborted()) {
- return;
- }
-
AutoProgressHandler progressHandler(mMaintenance);
if (NS_WARN_IF(NS_FAILED(progressHandler.Register(connection)))) {
return;
@@ -19059,20 +19101,12 @@ DatabaseMaintenance::PerformMaintenanceOnDatabase()
return;
}
- if (mMaintenance->IsAborted()) {
- return;
- }
-
MaintenanceAction maintenanceAction;
rv = DetermineMaintenanceAction(connection, databaseFile, &maintenanceAction);
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
}
- if (mMaintenance->IsAborted()) {
- return;
- }
-
switch (maintenanceAction) {
case MaintenanceAction::Nothing:
break;
@@ -19099,6 +19133,11 @@ DatabaseMaintenance::CheckIntegrity(mozIStorageConnection* aConnection,
MOZ_ASSERT(aConnection);
MOZ_ASSERT(aOk);
+ if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonBackgroundThread()) ||
+ mMaintenance->IsAborted()) {
+ return NS_ERROR_ABORT;
+ }
+
nsresult rv;
// First do a full integrity_check. Scope statements tightly here because
@@ -19216,6 +19255,11 @@ DatabaseMaintenance::DetermineMaintenanceAction(
MOZ_ASSERT(aDatabaseFile);
MOZ_ASSERT(aMaintenanceAction);
+ if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonBackgroundThread()) ||
+ mMaintenance->IsAborted()) {
+ return NS_ERROR_ABORT;
+ }
+
int32_t schemaVersion;
nsresult rv = aConnection->GetSchemaVersion(&schemaVersion);
if (NS_WARN_IF(NS_FAILED(rv))) {
@@ -19425,6 +19469,11 @@ DatabaseMaintenance::IncrementalVacuum(mozIStorageConnection* aConnection)
MOZ_ASSERT(!IsOnBackgroundThread());
MOZ_ASSERT(aConnection);
+ if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonBackgroundThread()) ||
+ mMaintenance->IsAborted()) {
+ return;
+ }
+
nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
"PRAGMA incremental_vacuum;"
));
@@ -19442,6 +19491,11 @@ DatabaseMaintenance::FullVacuum(mozIStorageConnection* aConnection,
MOZ_ASSERT(aConnection);
MOZ_ASSERT(aDatabaseFile);
+ if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonBackgroundThread()) ||
+ mMaintenance->IsAborted()) {
+ return;
+ }
+
nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
"VACUUM;"
));