Bug 1617170 - Make FileInfo more testable by removing the dependency on FileManager. r=dom-workers-and-storage-reviewers,janv

Also removes the dependency of FileManager(Base)/FileInfo on
IndexedDatabaseManager, based on contributions by janv@mozilla.com

Differential Revision: https://phabricator.services.mozilla.com/D64347
This commit is contained in:
Simon Giesecke
2020-03-17 11:50:58 +00:00
parent 9754419e89
commit f8e62fefd1
17 changed files with 280 additions and 258 deletions

View File

@@ -10,7 +10,6 @@
#include <numeric>
#include <stdint.h> // UINTPTR_MAX, uintptr_t
#include <utility>
#include "FileInfo.h"
#include "FileManager.h"
#include "IDBCursorType.h"
#include "IDBObjectStore.h"
@@ -16456,6 +16455,8 @@ mozilla::ipc::IPCResult Cursor<CursorType>::RecvContinue(
* FileManager
******************************************************************************/
FileManager::MutexType FileManager::sMutex;
FileManager::FileManager(PersistenceType aPersistenceType,
const nsACString& aGroup, const nsACString& aOrigin,
const nsAString& aDatabaseName, bool aEnforcingQuota)
@@ -16566,7 +16567,8 @@ nsresult FileManager::Init(nsIFile* aDirectory,
// 0, but the dbRefCnt is non-zero, which will keep the FileInfo object
// alive.
MOZ_ASSERT(dbRefCnt > 0);
mFileInfos.Put(id, new FileInfo(this, id, static_cast<nsrefcnt>(dbRefCnt)));
mFileInfos.Put(id, new FileInfo(FileManagerGuard{}, this, id,
static_cast<nsrefcnt>(dbRefCnt)));
mLastFileId = std::max(id, mLastFileId);
}

View File

@@ -4,141 +4,14 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "FileInfo.h"
#include "ActorsParent.h"
#include "FileInfoTImpl.h"
#include "FileManager.h"
#include "IndexedDatabaseManager.h"
#include "mozilla/Assertions.h"
#include "mozilla/Attributes.h"
#include "mozilla/Mutex.h"
#include "mozilla/dom/quota/QuotaManager.h"
#include "mozilla/ipc/BackgroundParent.h"
#include "nsError.h"
#include "nsThreadUtils.h"
namespace mozilla {
namespace dom {
namespace indexedDB {
using namespace mozilla::dom::quota;
using namespace mozilla::ipc;
void FileInfo::GetReferences(int32_t* aRefCnt, int32_t* aDBRefCnt,
int32_t* aSliceRefCnt) {
MOZ_ASSERT(!IndexedDatabaseManager::IsClosed());
MutexAutoLock lock(IndexedDatabaseManager::FileMutex());
if (aRefCnt) {
*aRefCnt = mRefCnt;
}
if (aDBRefCnt) {
*aDBRefCnt = mDBRefCnt;
}
if (aSliceRefCnt) {
*aSliceRefCnt = mSliceRefCnt;
}
}
void FileInfo::UpdateReferences(ThreadSafeAutoRefCnt& aRefCount, int32_t aDelta,
bool aSyncDeleteFile) {
// XXX This can go away once DOM objects no longer hold FileInfo objects...
// Looking at you, BlobImplBase...
// BlobImplBase is being addressed in bug 1068975.
if (IndexedDatabaseManager::IsClosed()) {
MOZ_ASSERT(&aRefCount == &mRefCnt);
MOZ_ASSERT(aDelta == 1 || aDelta == -1);
if (aDelta > 0) {
++aRefCount;
} else {
nsrefcnt count = --aRefCount;
if (!count) {
mRefCnt = 1;
delete this;
}
}
return;
}
MOZ_ASSERT(!IndexedDatabaseManager::IsClosed());
bool needsCleanup;
{
MutexAutoLock lock(IndexedDatabaseManager::FileMutex());
aRefCount = aRefCount + aDelta;
if (mRefCnt + mDBRefCnt + mSliceRefCnt > 0) {
return;
}
mFileManager->RemoveFileInfo(Id(), lock);
needsCleanup = !mFileManager->Invalidated();
}
if (needsCleanup) {
if (aSyncDeleteFile) {
nsresult rv = mFileManager->SyncDeleteFile(Id());
if (NS_FAILED(rv)) {
NS_WARNING("FileManager cleanup failed!");
}
} else {
Cleanup();
}
}
delete this;
}
bool FileInfo::LockedClearDBRefs() {
MOZ_ASSERT(!IndexedDatabaseManager::IsClosed());
IndexedDatabaseManager::FileMutex().AssertCurrentThreadOwns();
mDBRefCnt = 0;
if (mRefCnt || mSliceRefCnt) {
return true;
}
// In this case, we are not responsible for removing the file info from the
// hashtable. It's up to FileManager which is the only caller of this method.
MOZ_ASSERT(mFileManager->Invalidated());
delete this;
return false;
}
void FileInfo::Cleanup() {
AssertIsOnBackgroundThread();
int64_t id = Id();
if (NS_FAILED(mFileManager->AsyncDeleteFile(id))) {
NS_WARNING("Failed to delete file asynchronously!");
}
}
nsCOMPtr<nsIFile> FileInfo::GetFileForFileInfo() const {
const nsCOMPtr<nsIFile> directory = Manager()->GetDirectory();
if (NS_WARN_IF(!directory)) {
return nullptr;
}
nsCOMPtr<nsIFile> file = FileManager::GetFileForId(directory, Id());
if (NS_WARN_IF(!file)) {
return nullptr;
}
return file;
}
template class FileInfoT<FileManager>;
} // namespace indexedDB
} // namespace dom

View File

@@ -1,74 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_dom_indexeddb_fileinfo_h__
#define mozilla_dom_indexeddb_fileinfo_h__
#include "nsISupportsImpl.h"
namespace mozilla {
namespace dom {
namespace indexedDB {
class FileManager;
class FileInfo final {
template <typename FileManager, typename IndexedDatabaseManager>
friend class FileManagerBase;
const int64_t mFileId;
ThreadSafeAutoRefCnt mRefCnt;
ThreadSafeAutoRefCnt mDBRefCnt;
ThreadSafeAutoRefCnt mSliceRefCnt;
const RefPtr<FileManager> mFileManager;
public:
FileInfo(RefPtr<FileManager> aFileManager, const int64_t aFileId,
const nsrefcnt aInitialDBRefCnt = 0)
: mFileId(aFileId),
mDBRefCnt(aInitialDBRefCnt),
mFileManager(std::move(aFileManager)) {
MOZ_ASSERT(mFileManager);
MOZ_ASSERT(mFileId > 0);
}
void AddRef() { UpdateReferences(mRefCnt, 1); }
void Release(const bool aSyncDeleteFile = false) {
UpdateReferences(mRefCnt, -1, aSyncDeleteFile);
}
void UpdateDBRefs(int32_t aDelta) { UpdateReferences(mDBRefCnt, aDelta); }
void UpdateSliceRefs(int32_t aDelta) {
UpdateReferences(mSliceRefCnt, aDelta);
}
void GetReferences(int32_t* aRefCnt, int32_t* aDBRefCnt,
int32_t* aSliceRefCnt);
FileManager* Manager() const { return mFileManager; }
int64_t Id() const { return mFileId; }
nsCOMPtr<nsIFile> GetFileForFileInfo() const;
private:
void UpdateReferences(ThreadSafeAutoRefCnt& aRefCount, int32_t aDelta,
bool aSyncDeleteFile = false);
bool LockedClearDBRefs();
void Cleanup();
};
} // namespace indexedDB
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_indexeddb_fileinfo_h__

View File

@@ -0,0 +1,20 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_dom_indexeddb_fileinfofwd_h__
#define mozilla_dom_indexeddb_fileinfofwd_h__
namespace mozilla::dom::indexedDB {
class FileManager;
template <typename FileManager>
class FileInfoT;
using FileInfo = FileInfoT<indexedDB::FileManager>;
} // namespace mozilla::dom::indexedDB
#endif // mozilla_dom_indexeddb_fileinfofwd_h__

62
dom/indexedDB/FileInfoT.h Normal file
View File

@@ -0,0 +1,62 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_dom_indexeddb_fileinfot_h__
#define mozilla_dom_indexeddb_fileinfot_h__
#include "nsISupportsImpl.h"
#include "nsCOMPtr.h"
namespace mozilla {
namespace dom {
namespace indexedDB {
template <typename FileManager>
class FileInfoT final {
public:
using AutoLock = typename FileManager::AutoLock;
FileInfoT(const typename FileManager::FileManagerGuard& aGuard,
RefPtr<FileManager> aFileManager, const int64_t aFileId,
const nsrefcnt aInitialDBRefCnt = 0);
void AddRef();
void Release(const bool aSyncDeleteFile = false);
void UpdateDBRefs(int32_t aDelta);
void UpdateSliceRefs(int32_t aDelta);
void GetReferences(int32_t* aRefCnt, int32_t* aDBRefCnt,
int32_t* aSliceRefCnt);
FileManager* Manager() const;
int64_t Id() const;
nsCOMPtr<nsIFile> GetFileForFileInfo() const;
bool LockedClearDBRefs(const typename FileManager::FileManagerGuard& aGuard);
private:
void UpdateReferences(ThreadSafeAutoRefCnt& aRefCount, int32_t aDelta,
bool aSyncDeleteFile = false);
void Cleanup();
const int64_t mFileId;
ThreadSafeAutoRefCnt mRefCnt;
ThreadSafeAutoRefCnt mDBRefCnt;
ThreadSafeAutoRefCnt mSliceRefCnt;
const RefPtr<FileManager> mFileManager;
};
} // namespace indexedDB
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_indexeddb_fileinfot_h__

View File

@@ -0,0 +1,162 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_dom_indexeddb_fileinfotimpl_h__
#define mozilla_dom_indexeddb_fileinfotimpl_h__
#include "FileInfoT.h"
#include "mozilla/Mutex.h"
#include "nsIFile.h"
namespace mozilla {
namespace dom {
namespace indexedDB {
template <typename FileManager>
FileInfoT<FileManager>::FileInfoT(
const typename FileManager::FileManagerGuard& aGuard,
RefPtr<FileManager> aFileManager, const int64_t aFileId,
const nsrefcnt aInitialDBRefCnt)
: mFileId(aFileId),
mDBRefCnt(aInitialDBRefCnt),
mFileManager(std::move(aFileManager)) {
MOZ_ASSERT(mFileId > 0);
MOZ_ASSERT(mFileManager);
}
template <typename FileManager>
void FileInfoT<FileManager>::AddRef() {
UpdateReferences(mRefCnt, 1);
}
template <typename FileManager>
void FileInfoT<FileManager>::Release(const bool aSyncDeleteFile) {
UpdateReferences(mRefCnt, -1, aSyncDeleteFile);
}
template <typename FileManager>
void FileInfoT<FileManager>::UpdateDBRefs(int32_t aDelta) {
UpdateReferences(mDBRefCnt, aDelta);
}
template <typename FileManager>
void FileInfoT<FileManager>::UpdateSliceRefs(int32_t aDelta) {
UpdateReferences(mSliceRefCnt, aDelta);
}
template <typename FileManager>
void FileInfoT<FileManager>::GetReferences(int32_t* const aRefCnt,
int32_t* const aDBRefCnt,
int32_t* const aSliceRefCnt) {
AutoLock lock(FileManager::Mutex());
if (aRefCnt) {
*aRefCnt = mRefCnt;
}
if (aDBRefCnt) {
*aDBRefCnt = mDBRefCnt;
}
if (aSliceRefCnt) {
*aSliceRefCnt = mSliceRefCnt;
}
}
template <typename FileManager>
FileManager* FileInfoT<FileManager>::Manager() const {
return mFileManager;
}
template <typename FileManager>
int64_t FileInfoT<FileManager>::Id() const {
return mFileId;
}
template <typename FileManager>
void FileInfoT<FileManager>::UpdateReferences(ThreadSafeAutoRefCnt& aRefCount,
const int32_t aDelta,
const bool aSyncDeleteFile) {
bool needsCleanup;
{
AutoLock lock(FileManager::Mutex());
aRefCount = aRefCount + aDelta;
if (mRefCnt + mDBRefCnt + mSliceRefCnt > 0) {
return;
}
mFileManager->RemoveFileInfo(Id(), lock);
needsCleanup = !mFileManager->Invalidated();
}
if (needsCleanup) {
if (aSyncDeleteFile) {
nsresult rv = mFileManager->SyncDeleteFile(Id());
if (NS_FAILED(rv)) {
NS_WARNING("FileManager cleanup failed!");
}
} else {
Cleanup();
}
}
delete this;
}
template <typename FileManager>
bool FileInfoT<FileManager>::LockedClearDBRefs(
const typename FileManager::FileManagerGuard&) {
FileManager::Mutex().AssertCurrentThreadOwns();
mDBRefCnt = 0;
if (mRefCnt || mSliceRefCnt) {
return true;
}
// In this case, we are not responsible for removing the file info from the
// hashtable. It's up to FileManager which is the only caller of this method.
MOZ_ASSERT(mFileManager->Invalidated());
delete this;
return false;
}
template <typename FileManager>
void FileInfoT<FileManager>::Cleanup() {
int64_t id = Id();
if (NS_FAILED(mFileManager->AsyncDeleteFile(id))) {
NS_WARNING("Failed to delete file asynchronously!");
}
}
template <typename FileManager>
nsCOMPtr<nsIFile> FileInfoT<FileManager>::GetFileForFileInfo() const {
const nsCOMPtr<nsIFile> directory = Manager()->GetDirectory();
if (NS_WARN_IF(!directory)) {
return nullptr;
}
nsCOMPtr<nsIFile> file = FileManager::GetFileForId(directory, Id());
if (NS_WARN_IF(!file)) {
return nullptr;
}
return file;
}
} // namespace indexedDB
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_indexeddb_fileinfotimpl_h__

View File

@@ -9,7 +9,6 @@
#include "mozilla/dom/quota/PersistenceType.h"
#include "FileManagerBase.h"
#include "IndexedDatabaseManager.h"
#include "InitializedOnce.h"
class nsIFile;
@@ -20,9 +19,9 @@ namespace dom {
namespace indexedDB {
// Implemented in ActorsParent.cpp.
class FileManager final
: public FileManagerBase<FileManager, dom::IndexedDatabaseManager> {
typedef mozilla::dom::quota::PersistenceType PersistenceType;
class FileManager final : public FileManagerBase<FileManager> {
using PersistenceType = mozilla::dom::quota::PersistenceType;
using FileManagerBase<FileManager>::MutexType;
const PersistenceType mPersistenceType;
const nsCString mGroup;
@@ -34,6 +33,11 @@ class FileManager final
const bool mEnforcingQuota;
// Lock protecting FileManager.mFileInfos.
// It's s also used to atomically update FileInfo.mRefCnt and
// FileInfo.mDBRefCnt
static MutexType sMutex;
public:
static MOZ_MUST_USE nsCOMPtr<nsIFile> GetFileForId(nsIFile* aDirectory,
int64_t aId);
@@ -79,6 +83,8 @@ class FileManager final
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FileManager)
static StaticMutex& Mutex() { return sMutex; }
private:
~FileManager() = default;
};

View File

@@ -9,36 +9,31 @@
#include "mozilla/Attributes.h"
#include "mozilla/Mutex.h"
#include "mozilla/StaticMutex.h"
#include "nsDataHashtable.h"
#include "nsHashKeys.h"
#include "nsISupportsImpl.h"
#include "FileInfoT.h"
#include "FlippedOnce.h"
namespace mozilla {
namespace dom {
namespace indexedDB {
class FileInfo;
template <typename FileManager, typename IndexedDatabaseManager>
template <typename FileManager>
class FileManagerBase {
public:
using FileInfo = indexedDB::FileInfo;
using MutexType = decltype(IndexedDatabaseManager::FileMutex());
using AutoLock = mozilla::detail::BaseAutoLock<MutexType>;
using FileInfo = FileInfoT<FileManager>;
using MutexType = StaticMutex;
using AutoLock = mozilla::detail::BaseAutoLock<MutexType&>;
MOZ_MUST_USE RefPtr<FileInfo> GetFileInfo(int64_t aId) const {
if (IndexedDatabaseManager::IsClosed()) {
MOZ_ASSERT(false, "Shouldn't be called after shutdown!");
return nullptr;
}
// TODO: We cannot simply change this to RefPtr<FileInfo>, because
// FileInfo::AddRef also acquires the IndexedDatabaseManager::FileMutex.
// FileInfo::AddRef also acquires the FileManager::Mutex.
// This looks quirky at least.
FileInfo* fileInfo;
{
AutoLock lock(IndexedDatabaseManager::FileMutex());
AutoLock lock(FileManager::Mutex());
fileInfo = mFileInfos.Get(aId);
}
@@ -46,18 +41,17 @@ class FileManagerBase {
}
MOZ_MUST_USE RefPtr<FileInfo> CreateFileInfo() {
MOZ_ASSERT(!IndexedDatabaseManager::IsClosed());
// TODO: We cannot simply change this to RefPtr<FileInfo>, because
// FileInfo::AddRef also acquires the IndexedDatabaseManager::FileMutex.
// FileInfo::AddRef also acquires the FileManager::Mutex.
// This looks quirky at least.
FileInfo* fileInfo;
{
AutoLock lock(IndexedDatabaseManager::FileMutex());
AutoLock lock(FileManager::Mutex());
const int64_t id = ++mLastFileId;
fileInfo = new FileInfo(static_cast<FileManager*>(this), id);
fileInfo =
new FileInfo(FileManagerGuard{}, static_cast<FileManager*>(this), id);
mFileInfos.Put(id, fileInfo);
}
@@ -65,20 +59,15 @@ class FileManagerBase {
return fileInfo;
}
void RemoveFileInfo(const int64_t aId, const AutoLock& aFilesMutexLock) {
void RemoveFileInfo(const int64_t aId, const AutoLock& aFileMutexLock) {
#ifdef DEBUG
aFilesMutexLock.AssertOwns(IndexedDatabaseManager::FileMutex());
aFileMutexLock.AssertOwns(FileManager::Mutex());
#endif
mFileInfos.Remove(aId);
}
nsresult Invalidate() {
if (IndexedDatabaseManager::IsClosed()) {
MOZ_ASSERT(false, "Shouldn't be called after shutdown!");
return NS_ERROR_UNEXPECTED;
}
AutoLock lock(IndexedDatabaseManager::FileMutex());
AutoLock lock(FileManager::Mutex());
mInvalidated.Flip();
@@ -86,7 +75,7 @@ class FileManagerBase {
FileInfo* info = iter.Data();
MOZ_ASSERT(info);
return !info->LockedClearDBRefs();
return !info->LockedClearDBRefs(FileManagerGuard{});
});
return NS_OK;
@@ -102,7 +91,7 @@ class FileManagerBase {
~FileManagerBase() = default;
// Access to the following fields must be protected by
// IndexedDatabaseManager::FileMutex()
// FileManager::Mutex()
int64_t mLastFileId = 0;
nsDataHashtable<nsUint64HashKey, FileInfo*> mFileInfos;

View File

@@ -6,7 +6,6 @@
#include "IDBDatabase.h"
#include "FileInfo.h"
#include "IDBEvents.h"
#include "IDBFactory.h"
#include "IDBIndex.h"

View File

@@ -6,7 +6,6 @@
#include "IDBIndex.h"
#include "FileInfo.h"
#include "IDBCursorType.h"
#include "IDBEvents.h"
#include "IDBKeyRange.h"

View File

@@ -7,7 +7,6 @@
#include "IDBMutableFile.h"
#include "ActorsChild.h"
#include "FileInfo.h"
#include "IDBDatabase.h"
#include "IDBFactory.h"
#include "IDBFileHandle.h"

View File

@@ -9,7 +9,6 @@
#include <numeric>
#include <utility>
#include "FileInfo.h"
#include "IDBCursorType.h"
#include "IDBDatabase.h"
#include "IDBEvents.h"

View File

@@ -12,6 +12,7 @@
#include "nsCOMPtr.h"
#include "nsTArray.h"
#include "InitializedOnce.h"
#include "FileInfoFwd.h"
namespace mozilla {
namespace dom {
@@ -22,7 +23,6 @@ class IDBMutableFile;
namespace indexedDB {
class FileInfo;
class SerializedStructuredCloneReadInfo;
struct StructuredCloneFile {

View File

@@ -11,7 +11,6 @@
# error Must include IndexedDatabase.h first
#endif
#include "FileInfo.h"
#include "FileManager.h"
#include "IDBMutableFile.h"
#include "mozilla/dom/indexedDB/PBackgroundIDBSharedTypes.h"

View File

@@ -215,9 +215,7 @@ auto DatabaseNameMatchPredicate(const nsAString* const aName) {
} // namespace
IndexedDatabaseManager::IndexedDatabaseManager()
: mFileMutex("IndexedDatabaseManager.mFileMutex"),
mBackgroundActor(nullptr) {
IndexedDatabaseManager::IndexedDatabaseManager() : mBackgroundActor(nullptr) {
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
}

View File

@@ -132,13 +132,6 @@ class IndexedDatabaseManager final {
static const nsCString& GetLocale();
static mozilla::Mutex& FileMutex() {
IndexedDatabaseManager* mgr = Get();
NS_ASSERTION(mgr, "Must have a manager here!");
return mgr->mFileMutex;
}
static nsresult CommonPostHandleEvent(EventChainPostVisitor& aVisitor,
IDBFactory* aFactory);
@@ -164,11 +157,6 @@ class IndexedDatabaseManager final {
nsClassHashtable<nsRefPtrHashKey<FileManager>, nsTArray<int64_t>>
mPendingDeleteInfos;
// Lock protecting FileManager.mFileInfos.
// It's s also used to atomically update FileInfo.mRefCnt, FileInfo.mDBRefCnt
// and FileInfo.mSliceRefCnt
mozilla::Mutex mFileMutex;
nsCString mLocale;
indexedDB::BackgroundUtilsChild* mBackgroundActor;

View File

@@ -24,6 +24,7 @@ XPCSHELL_TESTS_MANIFESTS += [
TEST_DIRS += ['test/gtest']
EXPORTS.mozilla.dom += [
'FileInfoFwd.h',
'FlippedOnce.h',
'IDBCursor.h',
'IDBCursorType.h',