Bumping manifests a=b2g-bump
[gecko.git] / dom / filehandle / FileService.cpp
blobd1ead08391d0de73b65da44cbf2872c1c85f858f
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"
13 #include "nsError.h"
14 #include "nsIEventTarget.h"
15 #include "nsIObserverService.h"
16 #include "nsIOfflineStorage.h"
17 #include "nsNetCID.h"
18 #include "nsServiceManagerUtils.h"
19 #include "nsThreadUtils.h"
21 namespace mozilla {
22 namespace dom {
24 namespace {
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!");
43 nsresult
44 FileService::Init()
46 nsresult rv;
47 mStreamTransportTarget =
48 do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID, &rv);
50 return rv;
53 nsresult
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!");
62 break;
66 // Make sure the service is still accessible while any generated callbacks
67 // are processed.
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);
83 return NS_OK;
86 // static
87 FileService*
88 FileService::GetOrCreate()
90 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
92 if (gShutdown) {
93 NS_WARNING("Calling GetOrCreate() after shutdown!");
94 return nullptr;
97 if (!gInstance) {
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.
111 gInstance = service;
114 return gInstance;
117 // static
118 FileService*
119 FileService::Get()
121 // Does not return an owning reference.
122 return gInstance;
125 // static
126 void
127 FileService::Shutdown()
129 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
131 gShutdown = true;
133 if (gInstance) {
134 if (NS_FAILED(gInstance->Cleanup())) {
135 NS_WARNING("Failed to shutdown file service!");
137 gInstance = nullptr;
141 // static
142 bool
143 FileService::IsShuttingDown()
145 return gShutdown;
148 nsresult
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);
178 return NS_OK;
181 bool lockedForReading = storageInfo->IsFileLockedForReading(fileName);
182 bool lockedForWriting = storageInfo->IsFileLockedForWriting(fileName);
184 if (modeIsWrite) {
185 if (!lockedForWriting) {
186 storageInfo->LockFileForWriting(fileName);
189 else {
190 if (!lockedForReading) {
191 storageInfo->LockFileForReading(fileName);
195 if (lockedForWriting || (lockedForReading && modeIsWrite)) {
196 storageInfo->CreateDelayedEnqueueInfo(aFileHandle, aFileHelper);
198 else {
199 FileHandleQueue* fileHandleQueue =
200 storageInfo->CreateFileHandleQueue(aFileHandle);
202 if (aFileHelper) {
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
214 // the file storage.
215 nsresult rv = fileHandleQueue->Enqueue(aFileHelper);
216 NS_ENSURE_SUCCESS(rv, rv);
220 return NS_OK;
223 void
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?!");
235 return;
238 storageInfo->RemoveFileHandleQueue(aFileHandle);
240 if (!storageInfo->HasRunningFileHandles()) {
241 mStorageInfos.Remove(storageId);
243 // See if we need to fire any complete callbacks.
244 uint32_t index = 0;
245 while (index < mCompleteCallbacks.Length()) {
246 if (MaybeFireCallback(mCompleteCallbacks[index])) {
247 mCompleteCallbacks.RemoveElementAt(index);
249 else {
250 index++;
256 void
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);
274 void
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)) {
282 return;
285 nsAutoTArray<nsRefPtr<FileHandleBase>, 10> fileHandles;
286 storageInfo->CollectRunningAndDelayedFileHandles(aStorage, fileHandles);
288 for (uint32_t index = 0; index < fileHandles.Length(); index++) {
289 ErrorResult ignored;
290 fileHandles[index]->Abort(ignored);
294 bool
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)) {
302 return false;
305 return storageInfo->HasRunningFileHandles(aStorage);
308 NS_IMPL_ISUPPORTS(FileService, nsIObserver)
310 NS_IMETHODIMP
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!");
317 Shutdown();
319 return NS_OK;
322 bool
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)) {
329 return false;
333 aCallback.mCallback->Run();
334 return true;
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)
350 nsresult
351 FileService::FileHandleQueue::Enqueue(FileHelper* aFileHelper)
353 mQueue.AppendElement(aFileHelper);
355 nsresult rv;
356 if (mFileHandle->mRequestMode == FileHandleBase::PARALLEL) {
357 rv = aFileHelper->AsyncRun(this);
359 else {
360 rv = ProcessQueue();
362 NS_ENSURE_SUCCESS(rv, rv);
364 return NS_OK;
367 void
368 FileService::
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);
377 else {
378 NS_ASSERTION(mCurrentHelper == aFileHelper, "How can this happen?!");
380 mCurrentHelper = nullptr;
382 nsresult rv = ProcessQueue();
383 if (NS_FAILED(rv)) {
384 return;
389 nsresult
390 FileService::FileHandleQueue::ProcessQueue()
392 if (mQueue.IsEmpty() || mCurrentHelper) {
393 return NS_OK;
396 mCurrentHelper = mQueue[0];
397 mQueue.RemoveElementAt(0);
399 nsresult rv = mCurrentHelper->AsyncRun(this);
400 NS_ENSURE_SUCCESS(rv, rv);
402 return NS_OK;
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;
432 return nullptr;
435 void
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);
442 return;
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);
461 index--;
462 count--;
464 continue;
467 const nsAString& fileName = fileHandle->MutableFile()->mFileName;
469 if (fileHandle->mMode == FileMode::Readwrite) {
470 if (!IsFileLockedForWriting(fileName)) {
471 LockFileForWriting(fileName);
474 else {
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!");
496 bool
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) {
502 return true;
505 return false;
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;
515 return info;
518 void
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);
538 } // namespace dom
539 } // namespace mozilla