1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/dom/file/FileService.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,522 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim: set ts=2 et sw=2 tw=80: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this file, 1.8 + * You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "FileService.h" 1.11 + 1.12 +#include "nsIFile.h" 1.13 +#include "nsIFileStorage.h" 1.14 +#include "nsIObserverService.h" 1.15 +#include "nsIStreamTransportService.h" 1.16 + 1.17 +#include "nsNetCID.h" 1.18 + 1.19 +#include "FileHandle.h" 1.20 +#include "FileRequest.h" 1.21 + 1.22 +USING_FILE_NAMESPACE 1.23 + 1.24 +namespace { 1.25 + 1.26 +FileService* gInstance = nullptr; 1.27 +bool gShutdown = false; 1.28 + 1.29 +} // anonymous namespace 1.30 + 1.31 +FileService::FileService() 1.32 +{ 1.33 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.34 + NS_ASSERTION(!gInstance, "More than one instance!"); 1.35 +} 1.36 + 1.37 +FileService::~FileService() 1.38 +{ 1.39 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.40 + NS_ASSERTION(!gInstance, "More than one instance!"); 1.41 +} 1.42 + 1.43 +nsresult 1.44 +FileService::Init() 1.45 +{ 1.46 + nsresult rv; 1.47 + mStreamTransportTarget = 1.48 + do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID, &rv); 1.49 + 1.50 + return rv; 1.51 +} 1.52 + 1.53 +nsresult 1.54 +FileService::Cleanup() 1.55 +{ 1.56 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.57 + 1.58 + nsIThread* thread = NS_GetCurrentThread(); 1.59 + while (mFileStorageInfos.Count()) { 1.60 + if (!NS_ProcessNextEvent(thread)) { 1.61 + NS_ERROR("Failed to process next event!"); 1.62 + break; 1.63 + } 1.64 + } 1.65 + 1.66 + // Make sure the service is still accessible while any generated callbacks 1.67 + // are processed. 1.68 + nsresult rv = NS_ProcessPendingEvents(thread); 1.69 + NS_ENSURE_SUCCESS(rv, rv); 1.70 + 1.71 + if (!mCompleteCallbacks.IsEmpty()) { 1.72 + // Run all callbacks manually now. 1.73 + for (uint32_t index = 0; index < mCompleteCallbacks.Length(); index++) { 1.74 + mCompleteCallbacks[index].mCallback->Run(); 1.75 + } 1.76 + mCompleteCallbacks.Clear(); 1.77 + 1.78 + // And make sure they get processed. 1.79 + rv = NS_ProcessPendingEvents(thread); 1.80 + NS_ENSURE_SUCCESS(rv, rv); 1.81 + } 1.82 + 1.83 + return NS_OK; 1.84 +} 1.85 + 1.86 +// static 1.87 +FileService* 1.88 +FileService::GetOrCreate() 1.89 +{ 1.90 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.91 + 1.92 + if (gShutdown) { 1.93 + NS_WARNING("Calling GetOrCreate() after shutdown!"); 1.94 + return nullptr; 1.95 + } 1.96 + 1.97 + if (!gInstance) { 1.98 + nsRefPtr<FileService> service(new FileService); 1.99 + 1.100 + nsresult rv = service->Init(); 1.101 + NS_ENSURE_SUCCESS(rv, nullptr); 1.102 + 1.103 + nsCOMPtr<nsIObserverService> obs = 1.104 + do_GetService(NS_OBSERVERSERVICE_CONTRACTID, &rv); 1.105 + NS_ENSURE_SUCCESS(rv, nullptr); 1.106 + 1.107 + rv = obs->AddObserver(service, "profile-before-change", false); 1.108 + NS_ENSURE_SUCCESS(rv, nullptr); 1.109 + 1.110 + // The observer service now owns us. 1.111 + gInstance = service; 1.112 + } 1.113 + 1.114 + return gInstance; 1.115 +} 1.116 + 1.117 +// static 1.118 +FileService* 1.119 +FileService::Get() 1.120 +{ 1.121 + // Does not return an owning reference. 1.122 + return gInstance; 1.123 +} 1.124 + 1.125 +// static 1.126 +void 1.127 +FileService::Shutdown() 1.128 +{ 1.129 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.130 + 1.131 + gShutdown = true; 1.132 + 1.133 + if (gInstance) { 1.134 + if (NS_FAILED(gInstance->Cleanup())) { 1.135 + NS_WARNING("Failed to shutdown file service!"); 1.136 + } 1.137 + gInstance = nullptr; 1.138 + } 1.139 +} 1.140 + 1.141 +// static 1.142 +bool 1.143 +FileService::IsShuttingDown() 1.144 +{ 1.145 + return gShutdown; 1.146 +} 1.147 + 1.148 +nsresult 1.149 +FileService::Enqueue(LockedFile* aLockedFile, FileHelper* aFileHelper) 1.150 +{ 1.151 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.152 + NS_ASSERTION(aLockedFile, "Null pointer!"); 1.153 + 1.154 + FileHandle* fileHandle = aLockedFile->mFileHandle; 1.155 + 1.156 + if (fileHandle->mFileStorage->IsInvalidated()) { 1.157 + return NS_ERROR_NOT_AVAILABLE; 1.158 + } 1.159 + 1.160 + const nsACString& storageId = fileHandle->mFileStorage->Id(); 1.161 + const nsAString& fileName = fileHandle->mFileName; 1.162 + bool modeIsWrite = aLockedFile->mMode == FileMode::Readwrite; 1.163 + 1.164 + FileStorageInfo* fileStorageInfo; 1.165 + if (!mFileStorageInfos.Get(storageId, &fileStorageInfo)) { 1.166 + nsAutoPtr<FileStorageInfo> newFileStorageInfo(new FileStorageInfo()); 1.167 + 1.168 + mFileStorageInfos.Put(storageId, newFileStorageInfo); 1.169 + 1.170 + fileStorageInfo = newFileStorageInfo.forget(); 1.171 + } 1.172 + 1.173 + LockedFileQueue* existingLockedFileQueue = 1.174 + fileStorageInfo->GetLockedFileQueue(aLockedFile); 1.175 + 1.176 + if (existingLockedFileQueue) { 1.177 + existingLockedFileQueue->Enqueue(aFileHelper); 1.178 + return NS_OK; 1.179 + } 1.180 + 1.181 + bool lockedForReading = fileStorageInfo->IsFileLockedForReading(fileName); 1.182 + bool lockedForWriting = fileStorageInfo->IsFileLockedForWriting(fileName); 1.183 + 1.184 + if (modeIsWrite) { 1.185 + if (!lockedForWriting) { 1.186 + fileStorageInfo->LockFileForWriting(fileName); 1.187 + } 1.188 + } 1.189 + else { 1.190 + if (!lockedForReading) { 1.191 + fileStorageInfo->LockFileForReading(fileName); 1.192 + } 1.193 + } 1.194 + 1.195 + if (lockedForWriting || (lockedForReading && modeIsWrite)) { 1.196 + fileStorageInfo->CreateDelayedEnqueueInfo(aLockedFile, aFileHelper); 1.197 + } 1.198 + else { 1.199 + LockedFileQueue* lockedFileQueue = 1.200 + fileStorageInfo->CreateLockedFileQueue(aLockedFile); 1.201 + 1.202 + if (aFileHelper) { 1.203 + // Enqueue() will queue the file helper if there's already something 1.204 + // running. That can't fail, so no need to eventually remove 1.205 + // fileStorageInfo from the hash table. 1.206 + // 1.207 + // If the file helper is free to run then AsyncRun() is called on the 1.208 + // file helper. AsyncRun() is responsible for calling all necessary 1.209 + // callbacks when something fails. We're propagating the error here, 1.210 + // however there's no need to eventually remove fileStorageInfo from 1.211 + // the hash table. Code behind AsyncRun() will take care of it. The last 1.212 + // item in the code path is NotifyLockedFileCompleted() which removes 1.213 + // fileStorageInfo from the hash table if there are no locked files for 1.214 + // the file storage. 1.215 + nsresult rv = lockedFileQueue->Enqueue(aFileHelper); 1.216 + NS_ENSURE_SUCCESS(rv, rv); 1.217 + } 1.218 + } 1.219 + 1.220 + return NS_OK; 1.221 +} 1.222 + 1.223 +void 1.224 +FileService::NotifyLockedFileCompleted(LockedFile* aLockedFile) 1.225 +{ 1.226 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.227 + NS_ASSERTION(aLockedFile, "Null pointer!"); 1.228 + 1.229 + FileHandle* fileHandle = aLockedFile->mFileHandle; 1.230 + const nsACString& storageId = fileHandle->mFileStorage->Id(); 1.231 + 1.232 + FileStorageInfo* fileStorageInfo; 1.233 + if (!mFileStorageInfos.Get(storageId, &fileStorageInfo)) { 1.234 + NS_ERROR("We don't know anyting about this locked file?!"); 1.235 + return; 1.236 + } 1.237 + 1.238 + fileStorageInfo->RemoveLockedFileQueue(aLockedFile); 1.239 + 1.240 + if (!fileStorageInfo->HasRunningLockedFiles()) { 1.241 + mFileStorageInfos.Remove(storageId); 1.242 + 1.243 + // See if we need to fire any complete callbacks. 1.244 + uint32_t index = 0; 1.245 + while (index < mCompleteCallbacks.Length()) { 1.246 + if (MaybeFireCallback(mCompleteCallbacks[index])) { 1.247 + mCompleteCallbacks.RemoveElementAt(index); 1.248 + } 1.249 + else { 1.250 + index++; 1.251 + } 1.252 + } 1.253 + } 1.254 +} 1.255 + 1.256 +void 1.257 +FileService::WaitForStoragesToComplete( 1.258 + nsTArray<nsCOMPtr<nsIFileStorage> >& aStorages, 1.259 + nsIRunnable* aCallback) 1.260 +{ 1.261 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.262 + NS_ASSERTION(!aStorages.IsEmpty(), "No databases to wait on!"); 1.263 + NS_ASSERTION(aCallback, "Null pointer!"); 1.264 + 1.265 + StoragesCompleteCallback* callback = mCompleteCallbacks.AppendElement(); 1.266 + callback->mCallback = aCallback; 1.267 + callback->mStorages.SwapElements(aStorages); 1.268 + 1.269 + if (MaybeFireCallback(*callback)) { 1.270 + mCompleteCallbacks.RemoveElementAt(mCompleteCallbacks.Length() - 1); 1.271 + } 1.272 +} 1.273 + 1.274 +void 1.275 +FileService::AbortLockedFilesForStorage(nsIFileStorage* aFileStorage) 1.276 +{ 1.277 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.278 + NS_ASSERTION(aFileStorage, "Null pointer!"); 1.279 + 1.280 + FileStorageInfo* fileStorageInfo; 1.281 + if (!mFileStorageInfos.Get(aFileStorage->Id(), &fileStorageInfo)) { 1.282 + return; 1.283 + } 1.284 + 1.285 + nsAutoTArray<nsRefPtr<LockedFile>, 10> lockedFiles; 1.286 + fileStorageInfo->CollectRunningAndDelayedLockedFiles(aFileStorage, 1.287 + lockedFiles); 1.288 + 1.289 + for (uint32_t index = 0; index < lockedFiles.Length(); index++) { 1.290 + ErrorResult ignored; 1.291 + lockedFiles[index]->Abort(ignored); 1.292 + } 1.293 +} 1.294 + 1.295 +bool 1.296 +FileService::HasLockedFilesForStorage(nsIFileStorage* aFileStorage) 1.297 +{ 1.298 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.299 + NS_ASSERTION(aFileStorage, "Null pointer!"); 1.300 + 1.301 + FileStorageInfo* fileStorageInfo; 1.302 + if (!mFileStorageInfos.Get(aFileStorage->Id(), &fileStorageInfo)) { 1.303 + return false; 1.304 + } 1.305 + 1.306 + return fileStorageInfo->HasRunningLockedFiles(aFileStorage); 1.307 +} 1.308 + 1.309 +NS_IMPL_ISUPPORTS(FileService, nsIObserver) 1.310 + 1.311 +NS_IMETHODIMP 1.312 +FileService::Observe(nsISupports* aSubject, const char* aTopic, 1.313 + const char16_t* aData) 1.314 +{ 1.315 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.316 + NS_ASSERTION(!strcmp(aTopic, "profile-before-change"), "Wrong topic!"); 1.317 + 1.318 + Shutdown(); 1.319 + 1.320 + return NS_OK; 1.321 +} 1.322 + 1.323 +bool 1.324 +FileService::MaybeFireCallback(StoragesCompleteCallback& aCallback) 1.325 +{ 1.326 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.327 + 1.328 + for (uint32_t index = 0; index < aCallback.mStorages.Length(); index++) { 1.329 + if (mFileStorageInfos.Get(aCallback.mStorages[index]->Id(), nullptr)) { 1.330 + return false; 1.331 + } 1.332 + } 1.333 + 1.334 + aCallback.mCallback->Run(); 1.335 + return true; 1.336 +} 1.337 + 1.338 +FileService::LockedFileQueue::LockedFileQueue(LockedFile* aLockedFile) 1.339 +: mLockedFile(aLockedFile) 1.340 +{ 1.341 + NS_ASSERTION(aLockedFile, "Null pointer!"); 1.342 +} 1.343 + 1.344 +NS_IMPL_ADDREF(FileService::LockedFileQueue) 1.345 +NS_IMPL_RELEASE(FileService::LockedFileQueue) 1.346 + 1.347 +nsresult 1.348 +FileService::LockedFileQueue::Enqueue(FileHelper* aFileHelper) 1.349 +{ 1.350 + mQueue.AppendElement(aFileHelper); 1.351 + 1.352 + nsresult rv; 1.353 + if (mLockedFile->mRequestMode == LockedFile::PARALLEL) { 1.354 + rv = aFileHelper->AsyncRun(this); 1.355 + } 1.356 + else { 1.357 + rv = ProcessQueue(); 1.358 + } 1.359 + NS_ENSURE_SUCCESS(rv, rv); 1.360 + 1.361 + return NS_OK; 1.362 +} 1.363 + 1.364 +void 1.365 +FileService::LockedFileQueue::OnFileHelperComplete(FileHelper* aFileHelper) 1.366 +{ 1.367 + if (mLockedFile->mRequestMode == LockedFile::PARALLEL) { 1.368 + int32_t index = mQueue.IndexOf(aFileHelper); 1.369 + NS_ASSERTION(index != -1, "We don't know anything about this helper!"); 1.370 + 1.371 + mQueue.RemoveElementAt(index); 1.372 + } 1.373 + else { 1.374 + NS_ASSERTION(mCurrentHelper == aFileHelper, "How can this happen?!"); 1.375 + 1.376 + mCurrentHelper = nullptr; 1.377 + 1.378 + nsresult rv = ProcessQueue(); 1.379 + if (NS_FAILED(rv)) { 1.380 + return; 1.381 + } 1.382 + } 1.383 +} 1.384 + 1.385 +nsresult 1.386 +FileService::LockedFileQueue::ProcessQueue() 1.387 +{ 1.388 + if (mQueue.IsEmpty() || mCurrentHelper) { 1.389 + return NS_OK; 1.390 + } 1.391 + 1.392 + mCurrentHelper = mQueue[0]; 1.393 + mQueue.RemoveElementAt(0); 1.394 + 1.395 + nsresult rv = mCurrentHelper->AsyncRun(this); 1.396 + NS_ENSURE_SUCCESS(rv, rv); 1.397 + 1.398 + return NS_OK; 1.399 +} 1.400 + 1.401 +FileService::LockedFileQueue* 1.402 +FileService::FileStorageInfo::CreateLockedFileQueue(LockedFile* aLockedFile) 1.403 +{ 1.404 + nsRefPtr<LockedFileQueue>* lockedFileQueue = 1.405 + mLockedFileQueues.AppendElement(); 1.406 + *lockedFileQueue = new LockedFileQueue(aLockedFile); 1.407 + return lockedFileQueue->get(); 1.408 +} 1.409 + 1.410 +FileService::LockedFileQueue* 1.411 +FileService::FileStorageInfo::GetLockedFileQueue(LockedFile* aLockedFile) 1.412 +{ 1.413 + uint32_t count = mLockedFileQueues.Length(); 1.414 + for (uint32_t index = 0; index < count; index++) { 1.415 + nsRefPtr<LockedFileQueue>& lockedFileQueue = mLockedFileQueues[index]; 1.416 + if (lockedFileQueue->mLockedFile == aLockedFile) { 1.417 + return lockedFileQueue; 1.418 + } 1.419 + } 1.420 + return nullptr; 1.421 +} 1.422 + 1.423 +void 1.424 +FileService::FileStorageInfo::RemoveLockedFileQueue(LockedFile* aLockedFile) 1.425 +{ 1.426 + for (uint32_t index = 0; index < mDelayedEnqueueInfos.Length(); index++) { 1.427 + if (mDelayedEnqueueInfos[index].mLockedFile == aLockedFile) { 1.428 + NS_ASSERTION(!mDelayedEnqueueInfos[index].mFileHelper, "Should be null!"); 1.429 + mDelayedEnqueueInfos.RemoveElementAt(index); 1.430 + return; 1.431 + } 1.432 + } 1.433 + 1.434 + uint32_t lockedFileCount = mLockedFileQueues.Length(); 1.435 + 1.436 + // We can't just remove entries from lock hash tables, we have to rebuild 1.437 + // them instead. Multiple LockedFile objects may lock the same file 1.438 + // (one entry can represent multiple locks). 1.439 + 1.440 + mFilesReading.Clear(); 1.441 + mFilesWriting.Clear(); 1.442 + 1.443 + for (uint32_t index = 0, count = lockedFileCount; index < count; index++) { 1.444 + LockedFile* lockedFile = mLockedFileQueues[index]->mLockedFile; 1.445 + if (lockedFile == aLockedFile) { 1.446 + NS_ASSERTION(count == lockedFileCount, "More than one match?!"); 1.447 + 1.448 + mLockedFileQueues.RemoveElementAt(index); 1.449 + index--; 1.450 + count--; 1.451 + 1.452 + continue; 1.453 + } 1.454 + 1.455 + const nsAString& fileName = lockedFile->mFileHandle->mFileName; 1.456 + 1.457 + if (lockedFile->mMode == FileMode::Readwrite) { 1.458 + if (!IsFileLockedForWriting(fileName)) { 1.459 + LockFileForWriting(fileName); 1.460 + } 1.461 + } 1.462 + else { 1.463 + if (!IsFileLockedForReading(fileName)) { 1.464 + LockFileForReading(fileName); 1.465 + } 1.466 + } 1.467 + } 1.468 + 1.469 + NS_ASSERTION(mLockedFileQueues.Length() == lockedFileCount - 1, 1.470 + "Didn't find the locked file we were looking for!"); 1.471 + 1.472 + nsTArray<DelayedEnqueueInfo> delayedEnqueueInfos; 1.473 + delayedEnqueueInfos.SwapElements(mDelayedEnqueueInfos); 1.474 + 1.475 + for (uint32_t index = 0; index < delayedEnqueueInfos.Length(); index++) { 1.476 + DelayedEnqueueInfo& delayedEnqueueInfo = delayedEnqueueInfos[index]; 1.477 + if (NS_FAILED(gInstance->Enqueue(delayedEnqueueInfo.mLockedFile, 1.478 + delayedEnqueueInfo.mFileHelper))) { 1.479 + NS_WARNING("Enqueue failed!"); 1.480 + } 1.481 + } 1.482 +} 1.483 + 1.484 +bool 1.485 +FileService::FileStorageInfo::HasRunningLockedFiles( 1.486 + nsIFileStorage* aFileStorage) 1.487 +{ 1.488 + for (uint32_t index = 0; index < mLockedFileQueues.Length(); index++) { 1.489 + LockedFile* lockedFile = mLockedFileQueues[index]->mLockedFile; 1.490 + if (lockedFile->mFileHandle->mFileStorage == aFileStorage) { 1.491 + return true; 1.492 + } 1.493 + } 1.494 + return false; 1.495 +} 1.496 + 1.497 +FileService::DelayedEnqueueInfo* 1.498 +FileService::FileStorageInfo::CreateDelayedEnqueueInfo(LockedFile* aLockedFile, 1.499 + FileHelper* aFileHelper) 1.500 +{ 1.501 + DelayedEnqueueInfo* info = mDelayedEnqueueInfos.AppendElement(); 1.502 + info->mLockedFile = aLockedFile; 1.503 + info->mFileHelper = aFileHelper; 1.504 + return info; 1.505 +} 1.506 + 1.507 +void 1.508 +FileService::FileStorageInfo::CollectRunningAndDelayedLockedFiles( 1.509 + nsIFileStorage* aFileStorage, 1.510 + nsTArray<nsRefPtr<LockedFile> >& aLockedFiles) 1.511 +{ 1.512 + for (uint32_t index = 0; index < mLockedFileQueues.Length(); index++) { 1.513 + LockedFile* lockedFile = mLockedFileQueues[index]->mLockedFile; 1.514 + if (lockedFile->mFileHandle->mFileStorage == aFileStorage) { 1.515 + aLockedFiles.AppendElement(lockedFile); 1.516 + } 1.517 + } 1.518 + 1.519 + for (uint32_t index = 0; index < mDelayedEnqueueInfos.Length(); index++) { 1.520 + LockedFile* lockedFile = mDelayedEnqueueInfos[index].mLockedFile; 1.521 + if (lockedFile->mFileHandle->mFileStorage == aFileStorage) { 1.522 + aLockedFiles.AppendElement(lockedFile); 1.523 + } 1.524 + } 1.525 +}