dom/file/FileService.cpp

branch
TOR_BUG_9701
changeset 15
b8a032363ba2
equal deleted inserted replaced
-1:000000000000 0:06b2f0a24d43
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/. */
6
7 #include "FileService.h"
8
9 #include "nsIFile.h"
10 #include "nsIFileStorage.h"
11 #include "nsIObserverService.h"
12 #include "nsIStreamTransportService.h"
13
14 #include "nsNetCID.h"
15
16 #include "FileHandle.h"
17 #include "FileRequest.h"
18
19 USING_FILE_NAMESPACE
20
21 namespace {
22
23 FileService* gInstance = nullptr;
24 bool gShutdown = false;
25
26 } // anonymous namespace
27
28 FileService::FileService()
29 {
30 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
31 NS_ASSERTION(!gInstance, "More than one instance!");
32 }
33
34 FileService::~FileService()
35 {
36 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
37 NS_ASSERTION(!gInstance, "More than one instance!");
38 }
39
40 nsresult
41 FileService::Init()
42 {
43 nsresult rv;
44 mStreamTransportTarget =
45 do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID, &rv);
46
47 return rv;
48 }
49
50 nsresult
51 FileService::Cleanup()
52 {
53 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
54
55 nsIThread* thread = NS_GetCurrentThread();
56 while (mFileStorageInfos.Count()) {
57 if (!NS_ProcessNextEvent(thread)) {
58 NS_ERROR("Failed to process next event!");
59 break;
60 }
61 }
62
63 // Make sure the service is still accessible while any generated callbacks
64 // are processed.
65 nsresult rv = NS_ProcessPendingEvents(thread);
66 NS_ENSURE_SUCCESS(rv, rv);
67
68 if (!mCompleteCallbacks.IsEmpty()) {
69 // Run all callbacks manually now.
70 for (uint32_t index = 0; index < mCompleteCallbacks.Length(); index++) {
71 mCompleteCallbacks[index].mCallback->Run();
72 }
73 mCompleteCallbacks.Clear();
74
75 // And make sure they get processed.
76 rv = NS_ProcessPendingEvents(thread);
77 NS_ENSURE_SUCCESS(rv, rv);
78 }
79
80 return NS_OK;
81 }
82
83 // static
84 FileService*
85 FileService::GetOrCreate()
86 {
87 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
88
89 if (gShutdown) {
90 NS_WARNING("Calling GetOrCreate() after shutdown!");
91 return nullptr;
92 }
93
94 if (!gInstance) {
95 nsRefPtr<FileService> service(new FileService);
96
97 nsresult rv = service->Init();
98 NS_ENSURE_SUCCESS(rv, nullptr);
99
100 nsCOMPtr<nsIObserverService> obs =
101 do_GetService(NS_OBSERVERSERVICE_CONTRACTID, &rv);
102 NS_ENSURE_SUCCESS(rv, nullptr);
103
104 rv = obs->AddObserver(service, "profile-before-change", false);
105 NS_ENSURE_SUCCESS(rv, nullptr);
106
107 // The observer service now owns us.
108 gInstance = service;
109 }
110
111 return gInstance;
112 }
113
114 // static
115 FileService*
116 FileService::Get()
117 {
118 // Does not return an owning reference.
119 return gInstance;
120 }
121
122 // static
123 void
124 FileService::Shutdown()
125 {
126 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
127
128 gShutdown = true;
129
130 if (gInstance) {
131 if (NS_FAILED(gInstance->Cleanup())) {
132 NS_WARNING("Failed to shutdown file service!");
133 }
134 gInstance = nullptr;
135 }
136 }
137
138 // static
139 bool
140 FileService::IsShuttingDown()
141 {
142 return gShutdown;
143 }
144
145 nsresult
146 FileService::Enqueue(LockedFile* aLockedFile, FileHelper* aFileHelper)
147 {
148 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
149 NS_ASSERTION(aLockedFile, "Null pointer!");
150
151 FileHandle* fileHandle = aLockedFile->mFileHandle;
152
153 if (fileHandle->mFileStorage->IsInvalidated()) {
154 return NS_ERROR_NOT_AVAILABLE;
155 }
156
157 const nsACString& storageId = fileHandle->mFileStorage->Id();
158 const nsAString& fileName = fileHandle->mFileName;
159 bool modeIsWrite = aLockedFile->mMode == FileMode::Readwrite;
160
161 FileStorageInfo* fileStorageInfo;
162 if (!mFileStorageInfos.Get(storageId, &fileStorageInfo)) {
163 nsAutoPtr<FileStorageInfo> newFileStorageInfo(new FileStorageInfo());
164
165 mFileStorageInfos.Put(storageId, newFileStorageInfo);
166
167 fileStorageInfo = newFileStorageInfo.forget();
168 }
169
170 LockedFileQueue* existingLockedFileQueue =
171 fileStorageInfo->GetLockedFileQueue(aLockedFile);
172
173 if (existingLockedFileQueue) {
174 existingLockedFileQueue->Enqueue(aFileHelper);
175 return NS_OK;
176 }
177
178 bool lockedForReading = fileStorageInfo->IsFileLockedForReading(fileName);
179 bool lockedForWriting = fileStorageInfo->IsFileLockedForWriting(fileName);
180
181 if (modeIsWrite) {
182 if (!lockedForWriting) {
183 fileStorageInfo->LockFileForWriting(fileName);
184 }
185 }
186 else {
187 if (!lockedForReading) {
188 fileStorageInfo->LockFileForReading(fileName);
189 }
190 }
191
192 if (lockedForWriting || (lockedForReading && modeIsWrite)) {
193 fileStorageInfo->CreateDelayedEnqueueInfo(aLockedFile, aFileHelper);
194 }
195 else {
196 LockedFileQueue* lockedFileQueue =
197 fileStorageInfo->CreateLockedFileQueue(aLockedFile);
198
199 if (aFileHelper) {
200 // Enqueue() will queue the file helper if there's already something
201 // running. That can't fail, so no need to eventually remove
202 // fileStorageInfo from the hash table.
203 //
204 // If the file helper is free to run then AsyncRun() is called on the
205 // file helper. AsyncRun() is responsible for calling all necessary
206 // callbacks when something fails. We're propagating the error here,
207 // however there's no need to eventually remove fileStorageInfo from
208 // the hash table. Code behind AsyncRun() will take care of it. The last
209 // item in the code path is NotifyLockedFileCompleted() which removes
210 // fileStorageInfo from the hash table if there are no locked files for
211 // the file storage.
212 nsresult rv = lockedFileQueue->Enqueue(aFileHelper);
213 NS_ENSURE_SUCCESS(rv, rv);
214 }
215 }
216
217 return NS_OK;
218 }
219
220 void
221 FileService::NotifyLockedFileCompleted(LockedFile* aLockedFile)
222 {
223 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
224 NS_ASSERTION(aLockedFile, "Null pointer!");
225
226 FileHandle* fileHandle = aLockedFile->mFileHandle;
227 const nsACString& storageId = fileHandle->mFileStorage->Id();
228
229 FileStorageInfo* fileStorageInfo;
230 if (!mFileStorageInfos.Get(storageId, &fileStorageInfo)) {
231 NS_ERROR("We don't know anyting about this locked file?!");
232 return;
233 }
234
235 fileStorageInfo->RemoveLockedFileQueue(aLockedFile);
236
237 if (!fileStorageInfo->HasRunningLockedFiles()) {
238 mFileStorageInfos.Remove(storageId);
239
240 // See if we need to fire any complete callbacks.
241 uint32_t index = 0;
242 while (index < mCompleteCallbacks.Length()) {
243 if (MaybeFireCallback(mCompleteCallbacks[index])) {
244 mCompleteCallbacks.RemoveElementAt(index);
245 }
246 else {
247 index++;
248 }
249 }
250 }
251 }
252
253 void
254 FileService::WaitForStoragesToComplete(
255 nsTArray<nsCOMPtr<nsIFileStorage> >& aStorages,
256 nsIRunnable* aCallback)
257 {
258 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
259 NS_ASSERTION(!aStorages.IsEmpty(), "No databases to wait on!");
260 NS_ASSERTION(aCallback, "Null pointer!");
261
262 StoragesCompleteCallback* callback = mCompleteCallbacks.AppendElement();
263 callback->mCallback = aCallback;
264 callback->mStorages.SwapElements(aStorages);
265
266 if (MaybeFireCallback(*callback)) {
267 mCompleteCallbacks.RemoveElementAt(mCompleteCallbacks.Length() - 1);
268 }
269 }
270
271 void
272 FileService::AbortLockedFilesForStorage(nsIFileStorage* aFileStorage)
273 {
274 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
275 NS_ASSERTION(aFileStorage, "Null pointer!");
276
277 FileStorageInfo* fileStorageInfo;
278 if (!mFileStorageInfos.Get(aFileStorage->Id(), &fileStorageInfo)) {
279 return;
280 }
281
282 nsAutoTArray<nsRefPtr<LockedFile>, 10> lockedFiles;
283 fileStorageInfo->CollectRunningAndDelayedLockedFiles(aFileStorage,
284 lockedFiles);
285
286 for (uint32_t index = 0; index < lockedFiles.Length(); index++) {
287 ErrorResult ignored;
288 lockedFiles[index]->Abort(ignored);
289 }
290 }
291
292 bool
293 FileService::HasLockedFilesForStorage(nsIFileStorage* aFileStorage)
294 {
295 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
296 NS_ASSERTION(aFileStorage, "Null pointer!");
297
298 FileStorageInfo* fileStorageInfo;
299 if (!mFileStorageInfos.Get(aFileStorage->Id(), &fileStorageInfo)) {
300 return false;
301 }
302
303 return fileStorageInfo->HasRunningLockedFiles(aFileStorage);
304 }
305
306 NS_IMPL_ISUPPORTS(FileService, nsIObserver)
307
308 NS_IMETHODIMP
309 FileService::Observe(nsISupports* aSubject, const char* aTopic,
310 const char16_t* aData)
311 {
312 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
313 NS_ASSERTION(!strcmp(aTopic, "profile-before-change"), "Wrong topic!");
314
315 Shutdown();
316
317 return NS_OK;
318 }
319
320 bool
321 FileService::MaybeFireCallback(StoragesCompleteCallback& aCallback)
322 {
323 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
324
325 for (uint32_t index = 0; index < aCallback.mStorages.Length(); index++) {
326 if (mFileStorageInfos.Get(aCallback.mStorages[index]->Id(), nullptr)) {
327 return false;
328 }
329 }
330
331 aCallback.mCallback->Run();
332 return true;
333 }
334
335 FileService::LockedFileQueue::LockedFileQueue(LockedFile* aLockedFile)
336 : mLockedFile(aLockedFile)
337 {
338 NS_ASSERTION(aLockedFile, "Null pointer!");
339 }
340
341 NS_IMPL_ADDREF(FileService::LockedFileQueue)
342 NS_IMPL_RELEASE(FileService::LockedFileQueue)
343
344 nsresult
345 FileService::LockedFileQueue::Enqueue(FileHelper* aFileHelper)
346 {
347 mQueue.AppendElement(aFileHelper);
348
349 nsresult rv;
350 if (mLockedFile->mRequestMode == LockedFile::PARALLEL) {
351 rv = aFileHelper->AsyncRun(this);
352 }
353 else {
354 rv = ProcessQueue();
355 }
356 NS_ENSURE_SUCCESS(rv, rv);
357
358 return NS_OK;
359 }
360
361 void
362 FileService::LockedFileQueue::OnFileHelperComplete(FileHelper* aFileHelper)
363 {
364 if (mLockedFile->mRequestMode == LockedFile::PARALLEL) {
365 int32_t index = mQueue.IndexOf(aFileHelper);
366 NS_ASSERTION(index != -1, "We don't know anything about this helper!");
367
368 mQueue.RemoveElementAt(index);
369 }
370 else {
371 NS_ASSERTION(mCurrentHelper == aFileHelper, "How can this happen?!");
372
373 mCurrentHelper = nullptr;
374
375 nsresult rv = ProcessQueue();
376 if (NS_FAILED(rv)) {
377 return;
378 }
379 }
380 }
381
382 nsresult
383 FileService::LockedFileQueue::ProcessQueue()
384 {
385 if (mQueue.IsEmpty() || mCurrentHelper) {
386 return NS_OK;
387 }
388
389 mCurrentHelper = mQueue[0];
390 mQueue.RemoveElementAt(0);
391
392 nsresult rv = mCurrentHelper->AsyncRun(this);
393 NS_ENSURE_SUCCESS(rv, rv);
394
395 return NS_OK;
396 }
397
398 FileService::LockedFileQueue*
399 FileService::FileStorageInfo::CreateLockedFileQueue(LockedFile* aLockedFile)
400 {
401 nsRefPtr<LockedFileQueue>* lockedFileQueue =
402 mLockedFileQueues.AppendElement();
403 *lockedFileQueue = new LockedFileQueue(aLockedFile);
404 return lockedFileQueue->get();
405 }
406
407 FileService::LockedFileQueue*
408 FileService::FileStorageInfo::GetLockedFileQueue(LockedFile* aLockedFile)
409 {
410 uint32_t count = mLockedFileQueues.Length();
411 for (uint32_t index = 0; index < count; index++) {
412 nsRefPtr<LockedFileQueue>& lockedFileQueue = mLockedFileQueues[index];
413 if (lockedFileQueue->mLockedFile == aLockedFile) {
414 return lockedFileQueue;
415 }
416 }
417 return nullptr;
418 }
419
420 void
421 FileService::FileStorageInfo::RemoveLockedFileQueue(LockedFile* aLockedFile)
422 {
423 for (uint32_t index = 0; index < mDelayedEnqueueInfos.Length(); index++) {
424 if (mDelayedEnqueueInfos[index].mLockedFile == aLockedFile) {
425 NS_ASSERTION(!mDelayedEnqueueInfos[index].mFileHelper, "Should be null!");
426 mDelayedEnqueueInfos.RemoveElementAt(index);
427 return;
428 }
429 }
430
431 uint32_t lockedFileCount = mLockedFileQueues.Length();
432
433 // We can't just remove entries from lock hash tables, we have to rebuild
434 // them instead. Multiple LockedFile objects may lock the same file
435 // (one entry can represent multiple locks).
436
437 mFilesReading.Clear();
438 mFilesWriting.Clear();
439
440 for (uint32_t index = 0, count = lockedFileCount; index < count; index++) {
441 LockedFile* lockedFile = mLockedFileQueues[index]->mLockedFile;
442 if (lockedFile == aLockedFile) {
443 NS_ASSERTION(count == lockedFileCount, "More than one match?!");
444
445 mLockedFileQueues.RemoveElementAt(index);
446 index--;
447 count--;
448
449 continue;
450 }
451
452 const nsAString& fileName = lockedFile->mFileHandle->mFileName;
453
454 if (lockedFile->mMode == FileMode::Readwrite) {
455 if (!IsFileLockedForWriting(fileName)) {
456 LockFileForWriting(fileName);
457 }
458 }
459 else {
460 if (!IsFileLockedForReading(fileName)) {
461 LockFileForReading(fileName);
462 }
463 }
464 }
465
466 NS_ASSERTION(mLockedFileQueues.Length() == lockedFileCount - 1,
467 "Didn't find the locked file we were looking for!");
468
469 nsTArray<DelayedEnqueueInfo> delayedEnqueueInfos;
470 delayedEnqueueInfos.SwapElements(mDelayedEnqueueInfos);
471
472 for (uint32_t index = 0; index < delayedEnqueueInfos.Length(); index++) {
473 DelayedEnqueueInfo& delayedEnqueueInfo = delayedEnqueueInfos[index];
474 if (NS_FAILED(gInstance->Enqueue(delayedEnqueueInfo.mLockedFile,
475 delayedEnqueueInfo.mFileHelper))) {
476 NS_WARNING("Enqueue failed!");
477 }
478 }
479 }
480
481 bool
482 FileService::FileStorageInfo::HasRunningLockedFiles(
483 nsIFileStorage* aFileStorage)
484 {
485 for (uint32_t index = 0; index < mLockedFileQueues.Length(); index++) {
486 LockedFile* lockedFile = mLockedFileQueues[index]->mLockedFile;
487 if (lockedFile->mFileHandle->mFileStorage == aFileStorage) {
488 return true;
489 }
490 }
491 return false;
492 }
493
494 FileService::DelayedEnqueueInfo*
495 FileService::FileStorageInfo::CreateDelayedEnqueueInfo(LockedFile* aLockedFile,
496 FileHelper* aFileHelper)
497 {
498 DelayedEnqueueInfo* info = mDelayedEnqueueInfos.AppendElement();
499 info->mLockedFile = aLockedFile;
500 info->mFileHelper = aFileHelper;
501 return info;
502 }
503
504 void
505 FileService::FileStorageInfo::CollectRunningAndDelayedLockedFiles(
506 nsIFileStorage* aFileStorage,
507 nsTArray<nsRefPtr<LockedFile> >& aLockedFiles)
508 {
509 for (uint32_t index = 0; index < mLockedFileQueues.Length(); index++) {
510 LockedFile* lockedFile = mLockedFileQueues[index]->mLockedFile;
511 if (lockedFile->mFileHandle->mFileStorage == aFileStorage) {
512 aLockedFiles.AppendElement(lockedFile);
513 }
514 }
515
516 for (uint32_t index = 0; index < mDelayedEnqueueInfos.Length(); index++) {
517 LockedFile* lockedFile = mDelayedEnqueueInfos[index].mLockedFile;
518 if (lockedFile->mFileHandle->mFileStorage == aFileStorage) {
519 aLockedFiles.AppendElement(lockedFile);
520 }
521 }
522 }

mercurial