1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "FileService.h"
9 #include "FileHandle.h"
10 #include "MainThreadUtils.h"
11 #include "mozilla/Assertions.h"
12 #include "MutableFile.h"
14 #include "nsIEventTarget.h"
15 #include "nsIObserverService.h"
16 #include "nsIOfflineStorage.h"
18 #include "nsServiceManagerUtils.h"
19 #include "nsThreadUtils.h"
26 FileService
* gInstance
= nullptr;
27 bool gShutdown
= false;
29 } // anonymous namespace
31 FileService::FileService()
33 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
34 NS_ASSERTION(!gInstance
, "More than one instance!");
37 FileService::~FileService()
39 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
40 NS_ASSERTION(!gInstance
, "More than one instance!");
47 mStreamTransportTarget
=
48 do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID
, &rv
);
54 FileService::Cleanup()
56 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
58 nsIThread
* thread
= NS_GetCurrentThread();
59 while (mStorageInfos
.Count()) {
60 if (!NS_ProcessNextEvent(thread
)) {
61 NS_ERROR("Failed to process next event!");
66 // Make sure the service is still accessible while any generated callbacks
68 nsresult rv
= NS_ProcessPendingEvents(thread
);
69 NS_ENSURE_SUCCESS(rv
, rv
);
71 if (!mCompleteCallbacks
.IsEmpty()) {
72 // Run all callbacks manually now.
73 for (uint32_t index
= 0; index
< mCompleteCallbacks
.Length(); index
++) {
74 mCompleteCallbacks
[index
].mCallback
->Run();
76 mCompleteCallbacks
.Clear();
78 // And make sure they get processed.
79 rv
= NS_ProcessPendingEvents(thread
);
80 NS_ENSURE_SUCCESS(rv
, rv
);
88 FileService::GetOrCreate()
90 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
93 NS_WARNING("Calling GetOrCreate() after shutdown!");
98 nsRefPtr
<FileService
> service(new FileService
);
100 nsresult rv
= service
->Init();
101 NS_ENSURE_SUCCESS(rv
, nullptr);
103 nsCOMPtr
<nsIObserverService
> obs
=
104 do_GetService(NS_OBSERVERSERVICE_CONTRACTID
, &rv
);
105 NS_ENSURE_SUCCESS(rv
, nullptr);
107 rv
= obs
->AddObserver(service
, "profile-before-change", false);
108 NS_ENSURE_SUCCESS(rv
, nullptr);
110 // The observer service now owns us.
121 // Does not return an owning reference.
127 FileService::Shutdown()
129 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
134 if (NS_FAILED(gInstance
->Cleanup())) {
135 NS_WARNING("Failed to shutdown file service!");
143 FileService::IsShuttingDown()
149 FileService::Enqueue(FileHandleBase
* aFileHandle
, FileHelper
* aFileHelper
)
151 MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
152 MOZ_ASSERT(aFileHandle
, "Null pointer!");
154 MutableFileBase
* mutableFile
= aFileHandle
->MutableFile();
156 if (mutableFile
->IsInvalid()) {
157 return NS_ERROR_NOT_AVAILABLE
;
160 const nsACString
& storageId
= mutableFile
->mStorageId
;
161 const nsAString
& fileName
= mutableFile
->mFileName
;
162 bool modeIsWrite
= aFileHandle
->mMode
== FileMode::Readwrite
;
164 StorageInfo
* storageInfo
;
165 if (!mStorageInfos
.Get(storageId
, &storageInfo
)) {
166 nsAutoPtr
<StorageInfo
> newStorageInfo(new StorageInfo());
168 mStorageInfos
.Put(storageId
, newStorageInfo
);
170 storageInfo
= newStorageInfo
.forget();
173 FileHandleQueue
* existingFileHandleQueue
=
174 storageInfo
->GetFileHandleQueue(aFileHandle
);
176 if (existingFileHandleQueue
) {
177 existingFileHandleQueue
->Enqueue(aFileHelper
);
181 bool lockedForReading
= storageInfo
->IsFileLockedForReading(fileName
);
182 bool lockedForWriting
= storageInfo
->IsFileLockedForWriting(fileName
);
185 if (!lockedForWriting
) {
186 storageInfo
->LockFileForWriting(fileName
);
190 if (!lockedForReading
) {
191 storageInfo
->LockFileForReading(fileName
);
195 if (lockedForWriting
|| (lockedForReading
&& modeIsWrite
)) {
196 storageInfo
->CreateDelayedEnqueueInfo(aFileHandle
, aFileHelper
);
199 FileHandleQueue
* fileHandleQueue
=
200 storageInfo
->CreateFileHandleQueue(aFileHandle
);
203 // Enqueue() will queue the file helper if there's already something
204 // running. That can't fail, so no need to eventually remove
205 // storageInfo from the hash table.
207 // If the file helper is free to run then AsyncRun() is called on the
208 // file helper. AsyncRun() is responsible for calling all necessary
209 // callbacks when something fails. We're propagating the error here,
210 // however there's no need to eventually remove storageInfo from
211 // the hash table. Code behind AsyncRun() will take care of it. The last
212 // item in the code path is NotifyFileHandleCompleted() which removes
213 // storageInfo from the hash table if there are no file handles for
215 nsresult rv
= fileHandleQueue
->Enqueue(aFileHelper
);
216 NS_ENSURE_SUCCESS(rv
, rv
);
224 FileService::NotifyFileHandleCompleted(FileHandleBase
* aFileHandle
)
226 MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
227 MOZ_ASSERT(aFileHandle
, "Null pointer!");
229 MutableFileBase
* mutableFile
= aFileHandle
->MutableFile();
230 const nsACString
& storageId
= mutableFile
->mStorageId
;
232 StorageInfo
* storageInfo
;
233 if (!mStorageInfos
.Get(storageId
, &storageInfo
)) {
234 NS_ERROR("We don't know anyting about this file handle?!");
238 storageInfo
->RemoveFileHandleQueue(aFileHandle
);
240 if (!storageInfo
->HasRunningFileHandles()) {
241 mStorageInfos
.Remove(storageId
);
243 // See if we need to fire any complete callbacks.
245 while (index
< mCompleteCallbacks
.Length()) {
246 if (MaybeFireCallback(mCompleteCallbacks
[index
])) {
247 mCompleteCallbacks
.RemoveElementAt(index
);
257 FileService::WaitForStoragesToComplete(
258 nsTArray
<nsCOMPtr
<nsIOfflineStorage
> >& aStorages
,
259 nsIRunnable
* aCallback
)
261 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
262 NS_ASSERTION(!aStorages
.IsEmpty(), "No databases to wait on!");
263 NS_ASSERTION(aCallback
, "Null pointer!");
265 StoragesCompleteCallback
* callback
= mCompleteCallbacks
.AppendElement();
266 callback
->mCallback
= aCallback
;
267 callback
->mStorages
.SwapElements(aStorages
);
269 if (MaybeFireCallback(*callback
)) {
270 mCompleteCallbacks
.RemoveElementAt(mCompleteCallbacks
.Length() - 1);
275 FileService::AbortFileHandlesForStorage(nsIOfflineStorage
* aStorage
)
277 MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
278 MOZ_ASSERT(aStorage
, "Null pointer!");
280 StorageInfo
* storageInfo
;
281 if (!mStorageInfos
.Get(aStorage
->Id(), &storageInfo
)) {
285 nsAutoTArray
<nsRefPtr
<FileHandleBase
>, 10> fileHandles
;
286 storageInfo
->CollectRunningAndDelayedFileHandles(aStorage
, fileHandles
);
288 for (uint32_t index
= 0; index
< fileHandles
.Length(); index
++) {
290 fileHandles
[index
]->Abort(ignored
);
295 FileService::HasFileHandlesForStorage(nsIOfflineStorage
* aStorage
)
297 MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
298 MOZ_ASSERT(aStorage
, "Null pointer!");
300 StorageInfo
* storageInfo
;
301 if (!mStorageInfos
.Get(aStorage
->Id(), &storageInfo
)) {
305 return storageInfo
->HasRunningFileHandles(aStorage
);
308 NS_IMPL_ISUPPORTS(FileService
, nsIObserver
)
311 FileService::Observe(nsISupports
* aSubject
, const char* aTopic
,
312 const char16_t
* aData
)
314 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
315 NS_ASSERTION(!strcmp(aTopic
, "profile-before-change"), "Wrong topic!");
323 FileService::MaybeFireCallback(StoragesCompleteCallback
& aCallback
)
325 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
327 for (uint32_t index
= 0; index
< aCallback
.mStorages
.Length(); index
++) {
328 if (mStorageInfos
.Get(aCallback
.mStorages
[index
]->Id(), nullptr)) {
333 aCallback
.mCallback
->Run();
337 FileService::FileHandleQueue::FileHandleQueue(FileHandleBase
* aFileHandle
)
338 : mFileHandle(aFileHandle
)
340 MOZ_ASSERT(aFileHandle
, "Null pointer!");
343 FileService::FileHandleQueue::~FileHandleQueue()
347 NS_IMPL_ADDREF(FileService::FileHandleQueue
)
348 NS_IMPL_RELEASE(FileService::FileHandleQueue
)
351 FileService::FileHandleQueue::Enqueue(FileHelper
* aFileHelper
)
353 mQueue
.AppendElement(aFileHelper
);
356 if (mFileHandle
->mRequestMode
== FileHandleBase::PARALLEL
) {
357 rv
= aFileHelper
->AsyncRun(this);
362 NS_ENSURE_SUCCESS(rv
, rv
);
369 FileHandleQueue::OnFileHelperComplete(FileHelper
* aFileHelper
)
371 if (mFileHandle
->mRequestMode
== FileHandleBase::PARALLEL
) {
372 int32_t index
= mQueue
.IndexOf(aFileHelper
);
373 NS_ASSERTION(index
!= -1, "We don't know anything about this helper!");
375 mQueue
.RemoveElementAt(index
);
378 NS_ASSERTION(mCurrentHelper
== aFileHelper
, "How can this happen?!");
380 mCurrentHelper
= nullptr;
382 nsresult rv
= ProcessQueue();
390 FileService::FileHandleQueue::ProcessQueue()
392 if (mQueue
.IsEmpty() || mCurrentHelper
) {
396 mCurrentHelper
= mQueue
[0];
397 mQueue
.RemoveElementAt(0);
399 nsresult rv
= mCurrentHelper
->AsyncRun(this);
400 NS_ENSURE_SUCCESS(rv
, rv
);
405 FileService::DelayedEnqueueInfo::DelayedEnqueueInfo()
409 FileService::DelayedEnqueueInfo::~DelayedEnqueueInfo()
413 FileService::FileHandleQueue
*
414 FileService::StorageInfo::CreateFileHandleQueue(FileHandleBase
* aFileHandle
)
416 nsRefPtr
<FileHandleQueue
>* fileHandleQueue
=
417 mFileHandleQueues
.AppendElement();
418 *fileHandleQueue
= new FileHandleQueue(aFileHandle
);
419 return fileHandleQueue
->get();
422 FileService::FileHandleQueue
*
423 FileService::StorageInfo::GetFileHandleQueue(FileHandleBase
* aFileHandle
)
425 uint32_t count
= mFileHandleQueues
.Length();
426 for (uint32_t index
= 0; index
< count
; index
++) {
427 nsRefPtr
<FileHandleQueue
>& fileHandleQueue
= mFileHandleQueues
[index
];
428 if (fileHandleQueue
->mFileHandle
== aFileHandle
) {
429 return fileHandleQueue
;
436 FileService::StorageInfo::RemoveFileHandleQueue(FileHandleBase
* aFileHandle
)
438 for (uint32_t index
= 0; index
< mDelayedEnqueueInfos
.Length(); index
++) {
439 if (mDelayedEnqueueInfos
[index
].mFileHandle
== aFileHandle
) {
440 MOZ_ASSERT(!mDelayedEnqueueInfos
[index
].mFileHelper
, "Should be null!");
441 mDelayedEnqueueInfos
.RemoveElementAt(index
);
446 uint32_t fileHandleCount
= mFileHandleQueues
.Length();
448 // We can't just remove entries from lock hash tables, we have to rebuild
449 // them instead. Multiple FileHandle objects may lock the same file
450 // (one entry can represent multiple locks).
452 mFilesReading
.Clear();
453 mFilesWriting
.Clear();
455 for (uint32_t index
= 0, count
= fileHandleCount
; index
< count
; index
++) {
456 FileHandleBase
* fileHandle
= mFileHandleQueues
[index
]->mFileHandle
;
457 if (fileHandle
== aFileHandle
) {
458 MOZ_ASSERT(count
== fileHandleCount
, "More than one match?!");
460 mFileHandleQueues
.RemoveElementAt(index
);
467 const nsAString
& fileName
= fileHandle
->MutableFile()->mFileName
;
469 if (fileHandle
->mMode
== FileMode::Readwrite
) {
470 if (!IsFileLockedForWriting(fileName
)) {
471 LockFileForWriting(fileName
);
475 if (!IsFileLockedForReading(fileName
)) {
476 LockFileForReading(fileName
);
481 MOZ_ASSERT(mFileHandleQueues
.Length() == fileHandleCount
- 1,
482 "Didn't find the file handle we were looking for!");
484 nsTArray
<DelayedEnqueueInfo
> delayedEnqueueInfos
;
485 delayedEnqueueInfos
.SwapElements(mDelayedEnqueueInfos
);
487 for (uint32_t index
= 0; index
< delayedEnqueueInfos
.Length(); index
++) {
488 DelayedEnqueueInfo
& delayedEnqueueInfo
= delayedEnqueueInfos
[index
];
489 if (NS_FAILED(gInstance
->Enqueue(delayedEnqueueInfo
.mFileHandle
,
490 delayedEnqueueInfo
.mFileHelper
))) {
491 NS_WARNING("Enqueue failed!");
497 FileService::StorageInfo::HasRunningFileHandles(nsIOfflineStorage
* aStorage
)
499 for (uint32_t index
= 0; index
< mFileHandleQueues
.Length(); index
++) {
500 FileHandleBase
* fileHandle
= mFileHandleQueues
[index
]->mFileHandle
;
501 if (fileHandle
->MutableFile()->Storage() == aStorage
) {
508 FileService::DelayedEnqueueInfo
*
509 FileService::StorageInfo::CreateDelayedEnqueueInfo(FileHandleBase
* aFileHandle
,
510 FileHelper
* aFileHelper
)
512 DelayedEnqueueInfo
* info
= mDelayedEnqueueInfos
.AppendElement();
513 info
->mFileHandle
= aFileHandle
;
514 info
->mFileHelper
= aFileHelper
;
519 FileService::StorageInfo::CollectRunningAndDelayedFileHandles(
520 nsIOfflineStorage
* aStorage
,
521 nsTArray
<nsRefPtr
<FileHandleBase
>>& aFileHandles
)
523 for (uint32_t index
= 0; index
< mFileHandleQueues
.Length(); index
++) {
524 FileHandleBase
* fileHandle
= mFileHandleQueues
[index
]->mFileHandle
;
525 if (fileHandle
->MutableFile()->Storage() == aStorage
) {
526 aFileHandles
.AppendElement(fileHandle
);
530 for (uint32_t index
= 0; index
< mDelayedEnqueueInfos
.Length(); index
++) {
531 FileHandleBase
* fileHandle
= mDelayedEnqueueInfos
[index
].mFileHandle
;
532 if (fileHandle
->MutableFile()->Storage() == aStorage
) {
533 aFileHandles
.AppendElement(fileHandle
);
539 } // namespace mozilla