Bug 1750871 - run mochitest-remote on fission everywhere. r=releng-reviewers,aki
[gecko.git] / dom / filehandle / ActorsParent.cpp
blob9ecd36f20ab5323a90e906212ff6f79619b6eb5b
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=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 "ActorsParent.h"
9 #include "mozilla/Assertions.h"
10 #include "mozilla/Atomics.h"
11 #include "mozilla/Attributes.h"
12 #include "mozilla/SpinEventLoopUntil.h"
13 #include "mozilla/Unused.h"
14 #include "mozilla/dom/BlobImpl.h"
15 #include "mozilla/dom/File.h"
16 #include "mozilla/dom/PBackgroundFileHandleParent.h"
17 #include "mozilla/dom/PBackgroundFileRequestParent.h"
18 #include "mozilla/dom/indexedDB/ActorsParent.h"
19 #include "mozilla/dom/indexedDB/PBackgroundIDBDatabaseParent.h"
20 #include "mozilla/dom/IPCBlobUtils.h"
21 #include "mozilla/dom/quota/MemoryOutputStream.h"
22 #include "nsComponentManagerUtils.h"
23 #include "nsDebug.h"
24 #include "nsError.h"
25 #include "nsIEventTarget.h"
26 #include "nsIFile.h"
27 #include "nsIFileStreams.h"
28 #include "nsIInputStream.h"
29 #include "nsIOutputStream.h"
30 #include "nsIRunnable.h"
31 #include "nsISeekableStream.h"
32 #include "nsIThread.h"
33 #include "nsIThreadPool.h"
34 #include "nsNetUtil.h"
35 #include "nsStreamUtils.h"
36 #include "nsStringStream.h"
37 #include "nsTArray.h"
38 #include "nsThreadPool.h"
39 #include "nsThreadUtils.h"
40 #include "nsXPCOMCIDInternal.h"
42 namespace mozilla::dom {
44 using namespace mozilla::dom::quota;
45 using namespace mozilla::ipc;
47 namespace {
49 /******************************************************************************
50 * Constants
51 ******************************************************************************/
53 const uint32_t kThreadLimit = 5;
54 const uint32_t kIdleThreadLimit = 1;
55 const uint32_t kIdleThreadTimeoutMs = 30000;
57 const uint32_t kStreamCopyBlockSize = 32768;
59 } // namespace
61 class FileHandleThreadPool::FileHandleQueue final : public Runnable {
62 friend class FileHandleThreadPool;
64 RefPtr<FileHandleThreadPool> mOwningFileHandleThreadPool;
65 RefPtr<FileHandle> mFileHandle;
66 nsTArray<RefPtr<FileHandleOp>> mQueue;
67 RefPtr<FileHandleOp> mCurrentOp;
68 bool mShouldFinish;
70 public:
71 explicit FileHandleQueue(FileHandleThreadPool* aFileHandleThreadPool,
72 FileHandle* aFileHandle);
74 void Enqueue(FileHandleOp* aFileHandleOp);
76 void Finish();
78 void ProcessQueue();
80 private:
81 ~FileHandleQueue() = default;
83 NS_DECL_NSIRUNNABLE
86 struct FileHandleThreadPool::DelayedEnqueueInfo {
87 RefPtr<FileHandle> mFileHandle;
88 RefPtr<FileHandleOp> mFileHandleOp;
89 bool mFinish;
92 class FileHandleThreadPool::DirectoryInfo {
93 friend class FileHandleThreadPool;
95 RefPtr<FileHandleThreadPool> mOwningFileHandleThreadPool;
96 nsTArray<RefPtr<FileHandleQueue>> mFileHandleQueues;
97 nsTArray<DelayedEnqueueInfo> mDelayedEnqueueInfos;
98 nsTHashSet<nsString> mFilesReading;
99 nsTHashSet<nsString> mFilesWriting;
101 public:
102 FileHandleQueue* CreateFileHandleQueue(FileHandle* aFileHandle);
104 FileHandleQueue* GetFileHandleQueue(FileHandle* aFileHandle);
106 void RemoveFileHandleQueue(FileHandle* aFileHandle);
108 bool HasRunningFileHandles() { return !mFileHandleQueues.IsEmpty(); }
110 DelayedEnqueueInfo* CreateDelayedEnqueueInfo(FileHandle* aFileHandle,
111 FileHandleOp* aFileHandleOp,
112 bool aFinish);
114 void LockFileForReading(const nsAString& aFileName) {
115 mFilesReading.Insert(aFileName);
118 void LockFileForWriting(const nsAString& aFileName) {
119 mFilesWriting.Insert(aFileName);
122 bool IsFileLockedForReading(const nsAString& aFileName) {
123 return mFilesReading.Contains(aFileName);
126 bool IsFileLockedForWriting(const nsAString& aFileName) {
127 return mFilesWriting.Contains(aFileName);
130 private:
131 explicit DirectoryInfo(FileHandleThreadPool* aFileHandleThreadPool)
132 : mOwningFileHandleThreadPool(aFileHandleThreadPool) {}
135 struct FileHandleThreadPool::StoragesCompleteCallback final {
136 friend class DefaultDelete<StoragesCompleteCallback>;
138 nsTArray<nsCString> mDirectoryIds;
139 nsCOMPtr<nsIRunnable> mCallback;
141 StoragesCompleteCallback(nsTArray<nsCString>&& aDatabaseIds,
142 nsIRunnable* aCallback);
144 private:
145 ~StoragesCompleteCallback();
148 /******************************************************************************
149 * Actor class declarations
150 ******************************************************************************/
152 class FileHandle : public PBackgroundFileHandleParent {
153 friend class BackgroundMutableFileParentBase;
155 class FinishOp;
157 RefPtr<BackgroundMutableFileParentBase> mMutableFile;
158 nsCOMPtr<nsISupports> mStream;
159 uint64_t mActiveRequestCount;
160 FileHandleStorage mStorage;
161 Atomic<bool> mInvalidatedOnAnyThread;
162 FileMode mMode;
163 bool mHasBeenActive;
164 bool mActorDestroyed;
165 bool mInvalidated;
166 bool mAborted;
167 bool mFinishOrAbortReceived;
168 bool mFinishedOrAborted;
169 bool mForceAborted;
171 #ifdef DEBUG
172 nsCOMPtr<nsIEventTarget> mThreadPoolEventTarget;
173 #endif
175 public:
176 void AssertIsOnThreadPool() const;
178 bool IsActorDestroyed() const {
179 AssertIsOnBackgroundThread();
181 return mActorDestroyed;
184 // Must be called on the background thread.
185 bool IsInvalidated() const {
186 MOZ_ASSERT(IsOnBackgroundThread(), "Use IsInvalidatedOnAnyThread()");
187 MOZ_ASSERT_IF(mInvalidated, mAborted);
189 return mInvalidated;
192 // May be called on any thread, but is more expensive than IsInvalidated().
193 bool IsInvalidatedOnAnyThread() const { return mInvalidatedOnAnyThread; }
195 void SetActive() {
196 AssertIsOnBackgroundThread();
198 mHasBeenActive = true;
201 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(mozilla::dom::FileHandle)
203 nsresult GetOrCreateStream(nsISupports** aStream);
205 void Abort(bool aForce);
207 FileHandleStorage Storage() const { return mStorage; }
209 FileMode Mode() const { return mMode; }
211 BackgroundMutableFileParentBase* GetMutableFile() const {
212 AssertIsOnBackgroundThread();
213 MOZ_ASSERT(mMutableFile);
215 return mMutableFile;
218 bool IsAborted() const {
219 AssertIsOnBackgroundThread();
221 return mAborted;
224 PBackgroundParent* GetBackgroundParent() const {
225 AssertIsOnBackgroundThread();
226 MOZ_ASSERT(!IsActorDestroyed());
228 return GetMutableFile()->GetBackgroundParent();
231 void NoteActiveRequest();
233 void NoteFinishedRequest();
235 void Invalidate();
237 private:
238 // This constructor is only called by BackgroundMutableFileParentBase.
239 FileHandle(BackgroundMutableFileParentBase* aMutableFile, FileMode aMode);
241 // Reference counted.
242 ~FileHandle();
244 void MaybeFinishOrAbort() {
245 AssertIsOnBackgroundThread();
247 // If we've already finished or aborted then there's nothing else to do.
248 if (mFinishedOrAborted) {
249 return;
252 // If there are active requests then we have to wait for those requests to
253 // complete (see NoteFinishedRequest).
254 if (mActiveRequestCount) {
255 return;
258 // If we haven't yet received a finish or abort message then there could be
259 // additional requests coming so we should wait unless we're being forced to
260 // abort.
261 if (!mFinishOrAbortReceived && !mForceAborted) {
262 return;
265 FinishOrAbort();
268 void SendCompleteNotification(bool aAborted);
270 bool VerifyRequestParams(const FileRequestParams& aParams) const;
272 bool VerifyRequestData(const FileRequestData& aData) const;
274 void FinishOrAbort();
276 // IPDL methods are only called by IPDL.
277 virtual void ActorDestroy(ActorDestroyReason aWhy) override;
279 virtual mozilla::ipc::IPCResult RecvDeleteMe() override;
281 virtual mozilla::ipc::IPCResult RecvFinish() override;
283 virtual mozilla::ipc::IPCResult RecvAbort() override;
285 virtual PBackgroundFileRequestParent* AllocPBackgroundFileRequestParent(
286 const FileRequestParams& aParams) override;
288 virtual mozilla::ipc::IPCResult RecvPBackgroundFileRequestConstructor(
289 PBackgroundFileRequestParent* aActor,
290 const FileRequestParams& aParams) override;
292 virtual bool DeallocPBackgroundFileRequestParent(
293 PBackgroundFileRequestParent* aActor) override;
296 class FileHandleOp {
297 protected:
298 nsCOMPtr<nsIEventTarget> mOwningEventTarget;
299 RefPtr<FileHandle> mFileHandle;
300 #ifdef DEBUG
301 bool mEnqueued;
302 #endif
304 public:
305 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FileHandleOp)
307 void AssertIsOnOwningThread() const {
308 AssertIsOnBackgroundThread();
309 MOZ_ASSERT(mOwningEventTarget);
310 DebugOnly<bool> current;
311 MOZ_ASSERT(NS_SUCCEEDED(mOwningEventTarget->IsOnCurrentThread(&current)));
312 MOZ_ASSERT(current);
315 nsIEventTarget* OwningThread() const { return mOwningEventTarget; }
317 void AssertIsOnThreadPool() const {
318 MOZ_ASSERT(mFileHandle);
319 mFileHandle->AssertIsOnThreadPool();
322 void Enqueue();
324 virtual void RunOnThreadPool() = 0;
326 virtual void RunOnOwningThread() = 0;
328 protected:
329 FileHandleOp(FileHandle* aFileHandle)
330 : mOwningEventTarget(GetCurrentSerialEventTarget()),
331 mFileHandle(aFileHandle)
332 #ifdef DEBUG
334 mEnqueued(false)
335 #endif
337 AssertIsOnOwningThread();
338 MOZ_ASSERT(aFileHandle);
341 virtual ~FileHandleOp() = default;
344 class FileHandle::FinishOp : public FileHandleOp {
345 friend class FileHandle;
347 bool mAborted;
349 private:
350 FinishOp(FileHandle* aFileHandle, bool aAborted)
351 : FileHandleOp(aFileHandle), mAborted(aAborted) {
352 MOZ_ASSERT(aFileHandle);
355 ~FinishOp() = default;
357 virtual void RunOnThreadPool() override;
359 virtual void RunOnOwningThread() override;
362 class NormalFileHandleOp : public FileHandleOp,
363 public PBackgroundFileRequestParent {
364 nsresult mResultCode;
365 Atomic<bool> mOperationMayProceed;
366 bool mActorDestroyed;
367 const bool mFileHandleIsAborted;
369 #ifdef DEBUG
370 bool mResponseSent;
371 #endif
373 protected:
374 nsCOMPtr<nsISupports> mFileStream;
376 public:
377 void NoteActorDestroyed() {
378 AssertIsOnOwningThread();
380 mActorDestroyed = true;
381 mOperationMayProceed = false;
384 bool IsActorDestroyed() const {
385 AssertIsOnOwningThread();
387 return mActorDestroyed;
390 // May be called on any thread, but you should call IsActorDestroyed() if
391 // you know you're on the background thread because it is slightly faster.
392 bool OperationMayProceed() const { return mOperationMayProceed; }
394 // May be overridden by subclasses if they need to perform work on the
395 // background thread before being enqueued. Returning false will kill the
396 // child actors and prevent enqueue.
397 virtual bool Init(FileHandle* aFileHandle);
399 // This callback will be called on the background thread before releasing the
400 // final reference to this request object. Subclasses may perform any
401 // additional cleanup here but must always call the base class implementation.
402 virtual void Cleanup();
404 protected:
405 NormalFileHandleOp(FileHandle* aFileHandle)
406 : FileHandleOp(aFileHandle),
407 mResultCode(NS_OK),
408 mOperationMayProceed(true),
409 mActorDestroyed(false),
410 mFileHandleIsAborted(aFileHandle->IsAborted())
411 #ifdef DEBUG
413 mResponseSent(false)
414 #endif
416 MOZ_ASSERT(aFileHandle);
419 virtual ~NormalFileHandleOp();
421 // Must be overridden in subclasses. Called on the target thread to allow the
422 // subclass to perform necessary file operations. A successful return value
423 // will trigger a SendSuccessResult callback on the background thread while
424 // a failure value will trigger a SendFailureResult callback.
425 virtual nsresult DoFileWork(FileHandle* aFileHandle) = 0;
427 // Subclasses use this override to set the IPDL response value.
428 virtual void GetResponse(FileRequestResponse& aResponse) = 0;
430 private:
431 nsresult SendSuccessResult();
433 bool SendFailureResult(nsresult aResultCode);
435 virtual void RunOnThreadPool() override;
437 virtual void RunOnOwningThread() override;
439 // IPDL methods.
440 virtual void ActorDestroy(ActorDestroyReason aWhy) override;
443 class CopyFileHandleOp : public NormalFileHandleOp {
444 class ProgressRunnable;
446 protected:
447 nsCOMPtr<nsISupports> mBufferStream;
449 uint64_t mOffset;
450 uint64_t mSize;
452 bool mRead;
454 protected:
455 CopyFileHandleOp(FileHandle* aFileHandle)
456 : NormalFileHandleOp(aFileHandle), mOffset(0), mSize(0), mRead(true) {}
458 virtual nsresult DoFileWork(FileHandle* aFileHandle) override;
460 virtual void Cleanup() override;
463 class CopyFileHandleOp::ProgressRunnable final : public Runnable {
464 RefPtr<CopyFileHandleOp> mCopyFileHandleOp;
465 uint64_t mProgress;
466 uint64_t mProgressMax;
468 public:
469 ProgressRunnable(CopyFileHandleOp* aCopyFileHandleOp, uint64_t aProgress,
470 uint64_t aProgressMax)
471 : Runnable("dom::CopyFileHandleOp::ProgressRunnable"),
472 mCopyFileHandleOp(aCopyFileHandleOp),
473 mProgress(aProgress),
474 mProgressMax(aProgressMax) {}
476 private:
477 ~ProgressRunnable() = default;
479 NS_DECL_NSIRUNNABLE
482 class GetMetadataOp : public NormalFileHandleOp {
483 friend class FileHandle;
485 const FileRequestGetMetadataParams mParams;
487 protected:
488 FileRequestMetadata mMetadata;
490 protected:
491 // Only created by FileHandle.
492 GetMetadataOp(FileHandle* aFileHandle, const FileRequestParams& aParams);
494 ~GetMetadataOp() = default;
496 virtual nsresult DoFileWork(FileHandle* aFileHandle) override;
498 virtual void GetResponse(FileRequestResponse& aResponse) override;
501 class ReadOp final : public CopyFileHandleOp {
502 friend class FileHandle;
504 const FileRequestReadParams mParams;
506 private:
507 // Only created by FileHandle.
508 ReadOp(FileHandle* aFileHandle, const FileRequestParams& aParams);
510 ~ReadOp() = default;
512 virtual bool Init(FileHandle* aFileHandle) override;
514 virtual void GetResponse(FileRequestResponse& aResponse) override;
517 class WriteOp final : public CopyFileHandleOp {
518 friend class FileHandle;
520 const FileRequestWriteParams mParams;
522 private:
523 // Only created by FileHandle.
524 WriteOp(FileHandle* aFileHandle, const FileRequestParams& aParams);
526 ~WriteOp() = default;
528 virtual bool Init(FileHandle* aFileHandle) override;
530 virtual void GetResponse(FileRequestResponse& aResponse) override;
533 class TruncateOp final : public NormalFileHandleOp {
534 friend class FileHandle;
536 const FileRequestTruncateParams mParams;
538 private:
539 // Only created by FileHandle.
540 TruncateOp(FileHandle* aFileHandle, const FileRequestParams& aParams);
542 ~TruncateOp() = default;
544 virtual nsresult DoFileWork(FileHandle* aFileHandle) override;
546 virtual void GetResponse(FileRequestResponse& aResponse) override;
549 class FlushOp final : public NormalFileHandleOp {
550 friend class FileHandle;
552 const FileRequestFlushParams mParams;
554 private:
555 // Only created by FileHandle.
556 FlushOp(FileHandle* aFileHandle, const FileRequestParams& aParams);
558 ~FlushOp() = default;
560 virtual nsresult DoFileWork(FileHandle* aFileHandle) override;
562 virtual void GetResponse(FileRequestResponse& aResponse) override;
565 namespace {
567 /*******************************************************************************
568 * Helper Functions
569 ******************************************************************************/
571 FileHandleThreadPool* GetFileHandleThreadPoolFor(FileHandleStorage aStorage) {
572 switch (aStorage) {
573 case FILE_HANDLE_STORAGE_IDB:
574 return mozilla::dom::indexedDB::GetFileHandleThreadPool();
576 default:
577 MOZ_CRASH("Bad file handle storage value!");
581 nsresult ClampResultCode(nsresult aResultCode) {
582 if (NS_SUCCEEDED(aResultCode) ||
583 NS_ERROR_GET_MODULE(aResultCode) == NS_ERROR_MODULE_DOM_FILEHANDLE) {
584 return aResultCode;
587 NS_WARNING(nsPrintfCString("Converting non-filehandle error code (0x%" PRIX32
588 ") to "
589 "NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR",
590 static_cast<uint32_t>(aResultCode))
591 .get());
593 return NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR;
596 } // namespace
598 /*******************************************************************************
599 * FileHandleThreadPool implementation
600 ******************************************************************************/
602 FileHandleThreadPool::FileHandleThreadPool()
603 : mOwningEventTarget(GetCurrentSerialEventTarget()),
604 mShutdownRequested(false),
605 mShutdownComplete(false) {
606 AssertIsOnBackgroundThread();
607 MOZ_ASSERT(mOwningEventTarget);
608 AssertIsOnOwningThread();
611 FileHandleThreadPool::~FileHandleThreadPool() {
612 AssertIsOnOwningThread();
613 MOZ_ASSERT(!mDirectoryInfos.Count());
614 MOZ_ASSERT(mCompleteCallbacks.IsEmpty());
615 MOZ_ASSERT(mShutdownRequested);
616 MOZ_ASSERT(mShutdownComplete);
619 // static
620 already_AddRefed<FileHandleThreadPool> FileHandleThreadPool::Create() {
621 AssertIsOnBackgroundThread();
623 RefPtr<FileHandleThreadPool> fileHandleThreadPool =
624 new FileHandleThreadPool();
625 fileHandleThreadPool->AssertIsOnOwningThread();
627 if (NS_WARN_IF(NS_FAILED(fileHandleThreadPool->Init()))) {
628 return nullptr;
631 return fileHandleThreadPool.forget();
634 #ifdef DEBUG
636 void FileHandleThreadPool::AssertIsOnOwningThread() const {
637 MOZ_ASSERT(mOwningEventTarget);
639 bool current;
640 MOZ_ALWAYS_SUCCEEDS(mOwningEventTarget->IsOnCurrentThread(&current));
641 MOZ_ASSERT(current);
644 nsIEventTarget* FileHandleThreadPool::GetThreadPoolEventTarget() const {
645 AssertIsOnOwningThread();
646 MOZ_ASSERT(mThreadPool);
648 return mThreadPool;
651 #endif // DEBUG
653 void FileHandleThreadPool::Enqueue(FileHandle* aFileHandle,
654 FileHandleOp* aFileHandleOp, bool aFinish) {
655 AssertIsOnOwningThread();
656 MOZ_ASSERT(aFileHandle);
657 MOZ_ASSERT(!mShutdownRequested);
659 BackgroundMutableFileParentBase* mutableFile = aFileHandle->GetMutableFile();
661 const nsACString& directoryId = mutableFile->DirectoryId();
662 const nsAString& fileName = mutableFile->FileName();
663 bool modeIsWrite = aFileHandle->Mode() == FileMode::Readwrite;
665 DirectoryInfo* directoryInfo =
666 mDirectoryInfos
667 .LookupOrInsertWith(
668 directoryId,
669 [&] { return UniquePtr<DirectoryInfo>(new DirectoryInfo(this)); })
670 .get();
672 FileHandleQueue* existingFileHandleQueue =
673 directoryInfo->GetFileHandleQueue(aFileHandle);
675 if (existingFileHandleQueue) {
676 existingFileHandleQueue->Enqueue(aFileHandleOp);
677 if (aFinish) {
678 existingFileHandleQueue->Finish();
680 return;
683 bool lockedForReading = directoryInfo->IsFileLockedForReading(fileName);
684 bool lockedForWriting = directoryInfo->IsFileLockedForWriting(fileName);
686 if (modeIsWrite) {
687 if (!lockedForWriting) {
688 directoryInfo->LockFileForWriting(fileName);
690 } else {
691 if (!lockedForReading) {
692 directoryInfo->LockFileForReading(fileName);
696 if (lockedForWriting || (lockedForReading && modeIsWrite)) {
697 directoryInfo->CreateDelayedEnqueueInfo(aFileHandle, aFileHandleOp,
698 aFinish);
699 } else {
700 FileHandleQueue* fileHandleQueue =
701 directoryInfo->CreateFileHandleQueue(aFileHandle);
703 if (aFileHandleOp) {
704 fileHandleQueue->Enqueue(aFileHandleOp);
705 if (aFinish) {
706 fileHandleQueue->Finish();
712 void FileHandleThreadPool::WaitForDirectoriesToComplete(
713 nsTArray<nsCString>&& aDirectoryIds, nsIRunnable* aCallback) {
714 AssertIsOnOwningThread();
715 MOZ_ASSERT(!aDirectoryIds.IsEmpty());
716 MOZ_ASSERT(aCallback);
718 auto callback =
719 MakeUnique<StoragesCompleteCallback>(std::move(aDirectoryIds), aCallback);
721 if (!MaybeFireCallback(callback.get())) {
722 mCompleteCallbacks.AppendElement(std::move(callback));
726 void FileHandleThreadPool::Shutdown() {
727 AssertIsOnOwningThread();
728 MOZ_ASSERT(!mShutdownRequested);
729 MOZ_ASSERT(!mShutdownComplete);
731 mShutdownRequested = true;
733 if (!mThreadPool) {
734 MOZ_ASSERT(!mDirectoryInfos.Count());
735 MOZ_ASSERT(mCompleteCallbacks.IsEmpty());
737 mShutdownComplete = true;
738 return;
741 if (!mDirectoryInfos.Count()) {
742 Cleanup();
744 MOZ_ASSERT(mShutdownComplete);
745 return;
748 MOZ_ALWAYS_TRUE(SpinEventLoopUntil("FileHandleThreadPool::Shutdown"_ns,
749 [&]() { return mShutdownComplete; }));
752 nsresult FileHandleThreadPool::Init() {
753 AssertIsOnOwningThread();
755 mThreadPool = new nsThreadPool();
757 nsresult rv = mThreadPool->SetName("FileHandles"_ns);
758 if (NS_WARN_IF(NS_FAILED(rv))) {
759 return rv;
762 rv = mThreadPool->SetThreadLimit(kThreadLimit);
763 if (NS_WARN_IF(NS_FAILED(rv))) {
764 return rv;
767 rv = mThreadPool->SetIdleThreadLimit(kIdleThreadLimit);
768 if (NS_WARN_IF(NS_FAILED(rv))) {
769 return rv;
772 rv = mThreadPool->SetIdleThreadTimeout(kIdleThreadTimeoutMs);
773 if (NS_WARN_IF(NS_FAILED(rv))) {
774 return rv;
777 return NS_OK;
780 void FileHandleThreadPool::Cleanup() {
781 AssertIsOnOwningThread();
782 MOZ_ASSERT(mThreadPool);
783 MOZ_ASSERT(mShutdownRequested);
784 MOZ_ASSERT(!mShutdownComplete);
785 MOZ_ASSERT(!mDirectoryInfos.Count());
787 MOZ_ALWAYS_SUCCEEDS(mThreadPool->Shutdown());
789 if (!mCompleteCallbacks.IsEmpty()) {
790 // Run all callbacks manually now.
791 for (uint32_t count = mCompleteCallbacks.Length(), index = 0; index < count;
792 index++) {
793 UniquePtr<StoragesCompleteCallback> completeCallback =
794 std::move(mCompleteCallbacks[index]);
795 MOZ_ASSERT(completeCallback);
796 MOZ_ASSERT(completeCallback->mCallback);
798 Unused << completeCallback->mCallback->Run();
801 mCompleteCallbacks.Clear();
803 // And make sure they get processed.
804 nsIThread* currentThread = NS_GetCurrentThread();
805 MOZ_ASSERT(currentThread);
807 MOZ_ALWAYS_SUCCEEDS(NS_ProcessPendingEvents(currentThread));
810 mShutdownComplete = true;
813 void FileHandleThreadPool::FinishFileHandle(FileHandle* aFileHandle) {
814 AssertIsOnOwningThread();
815 MOZ_ASSERT(aFileHandle);
817 BackgroundMutableFileParentBase* mutableFile = aFileHandle->GetMutableFile();
818 const nsACString& directoryId = mutableFile->DirectoryId();
820 DirectoryInfo* directoryInfo;
821 if (!mDirectoryInfos.Get(directoryId, &directoryInfo)) {
822 NS_ERROR("We don't know anyting about this directory?!");
823 return;
826 directoryInfo->RemoveFileHandleQueue(aFileHandle);
828 if (!directoryInfo->HasRunningFileHandles()) {
829 mDirectoryInfos.Remove(directoryId);
831 // See if we need to fire any complete callbacks.
832 mCompleteCallbacks.RemoveElementsBy(
833 [this](const UniquePtr<StoragesCompleteCallback>& callback) {
834 return MaybeFireCallback(callback.get());
837 if (mShutdownRequested && !mDirectoryInfos.Count()) {
838 Cleanup();
843 bool FileHandleThreadPool::MaybeFireCallback(
844 StoragesCompleteCallback* aCallback) {
845 AssertIsOnOwningThread();
846 MOZ_ASSERT(aCallback);
847 MOZ_ASSERT(!aCallback->mDirectoryIds.IsEmpty());
848 MOZ_ASSERT(aCallback->mCallback);
850 for (uint32_t count = aCallback->mDirectoryIds.Length(), index = 0;
851 index < count; index++) {
852 const nsCString& directoryId = aCallback->mDirectoryIds[index];
853 MOZ_ASSERT(!directoryId.IsEmpty());
855 if (mDirectoryInfos.Get(directoryId, nullptr)) {
856 return false;
860 aCallback->mCallback->Run();
861 return true;
864 FileHandleThreadPool::FileHandleQueue::FileHandleQueue(
865 FileHandleThreadPool* aFileHandleThreadPool, FileHandle* aFileHandle)
866 : Runnable("dom::FileHandleThreadPool::FileHandleQueue"),
867 mOwningFileHandleThreadPool(aFileHandleThreadPool),
868 mFileHandle(aFileHandle),
869 mShouldFinish(false) {
870 MOZ_ASSERT(aFileHandleThreadPool);
871 aFileHandleThreadPool->AssertIsOnOwningThread();
872 MOZ_ASSERT(aFileHandle);
875 void FileHandleThreadPool::FileHandleQueue::Enqueue(
876 FileHandleOp* aFileHandleOp) {
877 MOZ_ASSERT(!mShouldFinish, "Enqueue called after Finish!");
879 mQueue.AppendElement(aFileHandleOp);
881 ProcessQueue();
884 void FileHandleThreadPool::FileHandleQueue::Finish() {
885 MOZ_ASSERT(!mShouldFinish, "Finish called more than once!");
887 mShouldFinish = true;
890 void FileHandleThreadPool::FileHandleQueue::ProcessQueue() {
891 if (mCurrentOp) {
892 return;
895 if (mQueue.IsEmpty()) {
896 if (mShouldFinish) {
897 mOwningFileHandleThreadPool->FinishFileHandle(mFileHandle);
899 // Make sure this is released on this thread.
900 mOwningFileHandleThreadPool = nullptr;
903 return;
906 mCurrentOp = mQueue[0];
907 mQueue.RemoveElementAt(0);
909 nsCOMPtr<nsIThreadPool> threadPool = mOwningFileHandleThreadPool->mThreadPool;
910 MOZ_ASSERT(threadPool);
912 MOZ_ALWAYS_SUCCEEDS(threadPool->Dispatch(this, NS_DISPATCH_NORMAL));
915 NS_IMETHODIMP
916 FileHandleThreadPool::FileHandleQueue::Run() {
917 MOZ_ASSERT(mCurrentOp);
919 if (IsOnBackgroundThread()) {
920 RefPtr<FileHandleOp> currentOp;
922 mCurrentOp.swap(currentOp);
923 ProcessQueue();
925 currentOp->RunOnOwningThread();
926 } else {
927 mCurrentOp->RunOnThreadPool();
929 nsCOMPtr<nsIEventTarget> backgroundThread = mCurrentOp->OwningThread();
931 MOZ_ALWAYS_SUCCEEDS(backgroundThread->Dispatch(this, NS_DISPATCH_NORMAL));
934 return NS_OK;
937 auto FileHandleThreadPool::DirectoryInfo::CreateFileHandleQueue(
938 FileHandle* aFileHandle) -> FileHandleQueue* {
939 RefPtr<FileHandleQueue>* fileHandleQueue = mFileHandleQueues.AppendElement();
940 *fileHandleQueue =
941 new FileHandleQueue(mOwningFileHandleThreadPool, aFileHandle);
942 return fileHandleQueue->get();
945 auto FileHandleThreadPool::DirectoryInfo::GetFileHandleQueue(
946 FileHandle* aFileHandle) -> FileHandleQueue* {
947 uint32_t count = mFileHandleQueues.Length();
948 for (uint32_t index = 0; index < count; index++) {
949 RefPtr<FileHandleQueue>& fileHandleQueue = mFileHandleQueues[index];
950 if (fileHandleQueue->mFileHandle == aFileHandle) {
951 return fileHandleQueue;
954 return nullptr;
957 void FileHandleThreadPool::DirectoryInfo::RemoveFileHandleQueue(
958 FileHandle* aFileHandle) {
959 for (uint32_t index = 0; index < mDelayedEnqueueInfos.Length(); index++) {
960 if (mDelayedEnqueueInfos[index].mFileHandle == aFileHandle) {
961 MOZ_ASSERT(!mDelayedEnqueueInfos[index].mFileHandleOp, "Should be null!");
962 mDelayedEnqueueInfos.RemoveElementAt(index);
963 return;
967 uint32_t fileHandleCount = mFileHandleQueues.Length();
969 // We can't just remove entries from lock hash tables, we have to rebuild
970 // them instead. Multiple FileHandle objects may lock the same file
971 // (one entry can represent multiple locks).
973 mFilesReading.Clear();
974 mFilesWriting.Clear();
976 for (uint32_t index = 0, count = fileHandleCount; index < count; index++) {
977 FileHandle* fileHandle = mFileHandleQueues[index]->mFileHandle;
978 if (fileHandle == aFileHandle) {
979 MOZ_ASSERT(count == fileHandleCount, "More than one match?!");
981 mFileHandleQueues.RemoveElementAt(index);
982 index--;
983 count--;
985 continue;
988 const nsAString& fileName = fileHandle->GetMutableFile()->FileName();
990 if (fileHandle->Mode() == FileMode::Readwrite) {
991 if (!IsFileLockedForWriting(fileName)) {
992 LockFileForWriting(fileName);
994 } else {
995 if (!IsFileLockedForReading(fileName)) {
996 LockFileForReading(fileName);
1001 MOZ_ASSERT(mFileHandleQueues.Length() == fileHandleCount - 1,
1002 "Didn't find the file handle we were looking for!");
1004 nsTArray<DelayedEnqueueInfo> delayedEnqueueInfos =
1005 std::move(mDelayedEnqueueInfos);
1007 for (uint32_t index = 0; index < delayedEnqueueInfos.Length(); index++) {
1008 DelayedEnqueueInfo& delayedEnqueueInfo = delayedEnqueueInfos[index];
1009 mOwningFileHandleThreadPool->Enqueue(delayedEnqueueInfo.mFileHandle,
1010 delayedEnqueueInfo.mFileHandleOp,
1011 delayedEnqueueInfo.mFinish);
1015 auto FileHandleThreadPool::DirectoryInfo::CreateDelayedEnqueueInfo(
1016 FileHandle* aFileHandle, FileHandleOp* aFileHandleOp, bool aFinish)
1017 -> DelayedEnqueueInfo* {
1018 DelayedEnqueueInfo* info = mDelayedEnqueueInfos.AppendElement();
1019 info->mFileHandle = aFileHandle;
1020 info->mFileHandleOp = aFileHandleOp;
1021 info->mFinish = aFinish;
1022 return info;
1025 FileHandleThreadPool::StoragesCompleteCallback::StoragesCompleteCallback(
1026 nsTArray<nsCString>&& aDirectoryIds, nsIRunnable* aCallback)
1027 : mDirectoryIds(std::move(aDirectoryIds)), mCallback(aCallback) {
1028 AssertIsOnBackgroundThread();
1029 MOZ_ASSERT(!mDirectoryIds.IsEmpty());
1030 MOZ_ASSERT(aCallback);
1032 MOZ_COUNT_CTOR(FileHandleThreadPool::StoragesCompleteCallback);
1035 FileHandleThreadPool::StoragesCompleteCallback::~StoragesCompleteCallback() {
1036 AssertIsOnBackgroundThread();
1038 MOZ_COUNT_DTOR(FileHandleThreadPool::StoragesCompleteCallback);
1041 /*******************************************************************************
1042 * BackgroundMutableFileParentBase
1043 ******************************************************************************/
1045 BackgroundMutableFileParentBase::BackgroundMutableFileParentBase(
1046 FileHandleStorage aStorage, const nsACString& aDirectoryId,
1047 const nsAString& aFileName, nsIFile* aFile)
1048 : mDirectoryId(aDirectoryId),
1049 mFileName(aFileName),
1050 mStorage(aStorage),
1051 mInvalidated(false),
1052 mActorWasAlive(false),
1053 mActorDestroyed(false),
1054 mFile(aFile) {
1055 AssertIsOnBackgroundThread();
1056 MOZ_ASSERT(aStorage != FILE_HANDLE_STORAGE_MAX);
1057 MOZ_ASSERT(!aDirectoryId.IsEmpty());
1058 MOZ_ASSERT(!aFileName.IsEmpty());
1059 MOZ_ASSERT(aFile);
1062 BackgroundMutableFileParentBase::~BackgroundMutableFileParentBase() {
1063 MOZ_ASSERT_IF(mActorWasAlive, mActorDestroyed);
1066 void BackgroundMutableFileParentBase::Invalidate() {
1067 AssertIsOnBackgroundThread();
1069 class MOZ_STACK_CLASS Helper final {
1070 public:
1071 static bool InvalidateFileHandles(nsTHashSet<FileHandle*>& aTable) {
1072 AssertIsOnBackgroundThread();
1074 const uint32_t count = aTable.Count();
1075 if (!count) {
1076 return true;
1079 nsTArray<RefPtr<FileHandle>> fileHandles;
1080 if (NS_WARN_IF(!fileHandles.SetCapacity(count, fallible))) {
1081 return false;
1084 // This can't fail, since we already reserved the required capacity.
1085 std::copy(aTable.cbegin(), aTable.cend(), MakeBackInserter(fileHandles));
1087 for (uint32_t index = 0; index < count; index++) {
1088 RefPtr<FileHandle> fileHandle = std::move(fileHandles[index]);
1089 MOZ_ASSERT(fileHandle);
1091 fileHandle->Invalidate();
1094 return true;
1098 if (mInvalidated) {
1099 return;
1102 mInvalidated = true;
1104 if (!Helper::InvalidateFileHandles(mFileHandles)) {
1105 NS_WARNING("Failed to abort all file handles!");
1109 bool BackgroundMutableFileParentBase::RegisterFileHandle(
1110 FileHandle* aFileHandle) {
1111 AssertIsOnBackgroundThread();
1112 MOZ_ASSERT(aFileHandle);
1113 MOZ_ASSERT(!mFileHandles.Contains(aFileHandle));
1114 MOZ_ASSERT(!mInvalidated);
1116 if (NS_WARN_IF(!mFileHandles.Insert(aFileHandle, fallible))) {
1117 return false;
1120 if (mFileHandles.Count() == 1) {
1121 NoteActiveState();
1124 return true;
1127 void BackgroundMutableFileParentBase::UnregisterFileHandle(
1128 FileHandle* aFileHandle) {
1129 AssertIsOnBackgroundThread();
1130 MOZ_ASSERT(aFileHandle);
1131 MOZ_ASSERT(mFileHandles.Contains(aFileHandle));
1133 mFileHandles.Remove(aFileHandle);
1135 if (!mFileHandles.Count()) {
1136 NoteInactiveState();
1140 void BackgroundMutableFileParentBase::SetActorAlive() {
1141 AssertIsOnBackgroundThread();
1142 MOZ_ASSERT(!mActorWasAlive);
1143 MOZ_ASSERT(!mActorDestroyed);
1145 mActorWasAlive = true;
1147 // This reference will be absorbed by IPDL and released when the actor is
1148 // destroyed.
1149 AddRef();
1152 already_AddRefed<nsISupports> BackgroundMutableFileParentBase::CreateStream(
1153 bool aReadOnly) {
1154 AssertIsOnBackgroundThread();
1156 nsresult rv;
1158 if (aReadOnly) {
1159 nsCOMPtr<nsIInputStream> stream;
1160 rv = NS_NewLocalFileInputStream(getter_AddRefs(stream), mFile, -1, -1,
1161 nsIFileInputStream::DEFER_OPEN);
1162 if (NS_WARN_IF(NS_FAILED(rv))) {
1163 return nullptr;
1165 return stream.forget();
1168 nsCOMPtr<nsIFileStream> stream;
1169 rv = NS_NewLocalFileStream(getter_AddRefs(stream), mFile, -1, -1,
1170 nsIFileStream::DEFER_OPEN);
1171 if (NS_WARN_IF(NS_FAILED(rv))) {
1172 return nullptr;
1174 return stream.forget();
1177 void BackgroundMutableFileParentBase::ActorDestroy(ActorDestroyReason aWhy) {
1178 AssertIsOnBackgroundThread();
1179 MOZ_ASSERT(!mActorDestroyed);
1181 mActorDestroyed = true;
1183 if (!IsInvalidated()) {
1184 Invalidate();
1188 PBackgroundFileHandleParent*
1189 BackgroundMutableFileParentBase::AllocPBackgroundFileHandleParent(
1190 const FileMode& aMode) {
1191 AssertIsOnBackgroundThread();
1193 if (NS_WARN_IF(aMode != FileMode::Readonly && aMode != FileMode::Readwrite)) {
1194 MOZ_CRASH_UNLESS_FUZZING();
1195 return nullptr;
1198 RefPtr<FileHandle> fileHandle = new FileHandle(this, aMode);
1200 return fileHandle.forget().take();
1203 mozilla::ipc::IPCResult
1204 BackgroundMutableFileParentBase::RecvPBackgroundFileHandleConstructor(
1205 PBackgroundFileHandleParent* aActor, const FileMode& aMode) {
1206 AssertIsOnBackgroundThread();
1207 MOZ_ASSERT(aActor);
1208 MOZ_ASSERT(aMode == FileMode::Readonly || aMode == FileMode::Readwrite);
1210 FileHandleThreadPool* fileHandleThreadPool =
1211 GetFileHandleThreadPoolFor(mStorage);
1212 MOZ_ASSERT(fileHandleThreadPool);
1214 auto* fileHandle = static_cast<FileHandle*>(aActor);
1216 // Add a placeholder for this file handle immediately.
1217 fileHandleThreadPool->Enqueue(fileHandle, nullptr, false);
1219 fileHandle->SetActive();
1221 if (NS_WARN_IF(!RegisterFileHandle(fileHandle))) {
1222 fileHandle->Abort(/* aForce */ false);
1223 return IPC_OK();
1226 return IPC_OK();
1229 bool BackgroundMutableFileParentBase::DeallocPBackgroundFileHandleParent(
1230 PBackgroundFileHandleParent* aActor) {
1231 AssertIsOnBackgroundThread();
1232 MOZ_ASSERT(aActor);
1234 RefPtr<FileHandle> fileHandle = dont_AddRef(static_cast<FileHandle*>(aActor));
1235 return true;
1238 mozilla::ipc::IPCResult BackgroundMutableFileParentBase::RecvDeleteMe() {
1239 AssertIsOnBackgroundThread();
1240 MOZ_ASSERT(!mActorDestroyed);
1242 IProtocol* mgr = Manager();
1243 if (!PBackgroundMutableFileParent::Send__delete__(this)) {
1244 return IPC_FAIL_NO_REASON(mgr);
1246 return IPC_OK();
1249 mozilla::ipc::IPCResult BackgroundMutableFileParentBase::RecvGetFileId(
1250 int64_t* aFileId) {
1251 AssertIsOnBackgroundThread();
1253 *aFileId = -1;
1254 return IPC_OK();
1257 /*******************************************************************************
1258 * FileHandle
1259 ******************************************************************************/
1261 FileHandle::FileHandle(BackgroundMutableFileParentBase* aMutableFile,
1262 FileMode aMode)
1263 : mMutableFile(aMutableFile),
1264 mActiveRequestCount(0),
1265 mStorage(aMutableFile->Storage()),
1266 mInvalidatedOnAnyThread(false),
1267 mMode(aMode),
1268 mHasBeenActive(false),
1269 mActorDestroyed(false),
1270 mInvalidated(false),
1271 mAborted(false),
1272 mFinishOrAbortReceived(false),
1273 mFinishedOrAborted(false),
1274 mForceAborted(false) {
1275 AssertIsOnBackgroundThread();
1276 MOZ_ASSERT(aMutableFile);
1278 #ifdef DEBUG
1279 FileHandleThreadPool* fileHandleThreadPool =
1280 GetFileHandleThreadPoolFor(mStorage);
1281 MOZ_ASSERT(fileHandleThreadPool);
1283 mThreadPoolEventTarget = fileHandleThreadPool->GetThreadPoolEventTarget();
1284 #endif
1287 FileHandle::~FileHandle() {
1288 MOZ_ASSERT(!mActiveRequestCount);
1289 MOZ_ASSERT(mActorDestroyed);
1290 MOZ_ASSERT_IF(mHasBeenActive, mFinishedOrAborted);
1293 void FileHandle::AssertIsOnThreadPool() const {
1294 MOZ_ASSERT(mThreadPoolEventTarget);
1295 DebugOnly<bool> current;
1296 MOZ_ASSERT(NS_SUCCEEDED(mThreadPoolEventTarget->IsOnCurrentThread(&current)));
1297 MOZ_ASSERT(current);
1300 nsresult FileHandle::GetOrCreateStream(nsISupports** aStream) {
1301 AssertIsOnBackgroundThread();
1303 if (!mStream) {
1304 nsCOMPtr<nsISupports> stream =
1305 mMutableFile->CreateStream(mMode == FileMode::Readonly);
1306 if (NS_WARN_IF(!stream)) {
1307 return NS_ERROR_FAILURE;
1310 stream.swap(mStream);
1313 nsCOMPtr<nsISupports> stream(mStream);
1314 stream.forget(aStream);
1316 return NS_OK;
1319 void FileHandle::Abort(bool aForce) {
1320 AssertIsOnBackgroundThread();
1322 mAborted = true;
1324 if (aForce) {
1325 mForceAborted = true;
1328 MaybeFinishOrAbort();
1331 void FileHandle::NoteActiveRequest() {
1332 AssertIsOnBackgroundThread();
1333 MOZ_ASSERT(mActiveRequestCount < UINT64_MAX);
1335 mActiveRequestCount++;
1338 void FileHandle::NoteFinishedRequest() {
1339 AssertIsOnBackgroundThread();
1340 MOZ_ASSERT(mActiveRequestCount);
1342 mActiveRequestCount--;
1344 MaybeFinishOrAbort();
1347 void FileHandle::Invalidate() {
1348 AssertIsOnBackgroundThread();
1349 MOZ_ASSERT(mInvalidated == mInvalidatedOnAnyThread);
1351 if (!mInvalidated) {
1352 mInvalidated = true;
1353 mInvalidatedOnAnyThread = true;
1355 Abort(/* aForce */ true);
1359 void FileHandle::SendCompleteNotification(bool aAborted) {
1360 AssertIsOnBackgroundThread();
1362 if (!IsActorDestroyed()) {
1363 Unused << SendComplete(aAborted);
1367 bool FileHandle::VerifyRequestParams(const FileRequestParams& aParams) const {
1368 AssertIsOnBackgroundThread();
1369 MOZ_ASSERT(aParams.type() != FileRequestParams::T__None);
1371 switch (aParams.type()) {
1372 case FileRequestParams::TFileRequestGetMetadataParams: {
1373 const FileRequestGetMetadataParams& params =
1374 aParams.get_FileRequestGetMetadataParams();
1376 if (NS_WARN_IF(!params.size() && !params.lastModified())) {
1377 MOZ_CRASH_UNLESS_FUZZING();
1378 return false;
1381 break;
1384 case FileRequestParams::TFileRequestReadParams: {
1385 const FileRequestReadParams& params = aParams.get_FileRequestReadParams();
1387 if (NS_WARN_IF(params.offset() == UINT64_MAX)) {
1388 MOZ_CRASH_UNLESS_FUZZING();
1389 return false;
1392 if (NS_WARN_IF(!params.size())) {
1393 MOZ_CRASH_UNLESS_FUZZING();
1394 return false;
1397 if (NS_WARN_IF(params.size() > UINT32_MAX)) {
1398 MOZ_CRASH_UNLESS_FUZZING();
1399 return false;
1402 break;
1405 case FileRequestParams::TFileRequestWriteParams: {
1406 if (NS_WARN_IF(mMode != FileMode::Readwrite)) {
1407 MOZ_CRASH_UNLESS_FUZZING();
1408 return false;
1411 const FileRequestWriteParams& params =
1412 aParams.get_FileRequestWriteParams();
1414 if (NS_WARN_IF(!params.dataLength())) {
1415 MOZ_CRASH_UNLESS_FUZZING();
1416 return false;
1419 if (NS_WARN_IF(!VerifyRequestData(params.data()))) {
1420 MOZ_CRASH_UNLESS_FUZZING();
1421 return false;
1424 break;
1427 case FileRequestParams::TFileRequestTruncateParams: {
1428 if (NS_WARN_IF(mMode != FileMode::Readwrite)) {
1429 MOZ_CRASH_UNLESS_FUZZING();
1430 return false;
1433 const FileRequestTruncateParams& params =
1434 aParams.get_FileRequestTruncateParams();
1436 if (NS_WARN_IF(params.offset() == UINT64_MAX)) {
1437 MOZ_CRASH_UNLESS_FUZZING();
1438 return false;
1441 break;
1444 case FileRequestParams::TFileRequestFlushParams: {
1445 if (NS_WARN_IF(mMode != FileMode::Readwrite)) {
1446 MOZ_CRASH_UNLESS_FUZZING();
1447 return false;
1450 break;
1453 default:
1454 MOZ_CRASH("Should never get here!");
1457 return true;
1460 bool FileHandle::VerifyRequestData(const FileRequestData& aData) const {
1461 AssertIsOnBackgroundThread();
1462 MOZ_ASSERT(aData.type() != FileRequestData::T__None);
1464 switch (aData.type()) {
1465 case FileRequestData::TFileRequestStringData: {
1466 const FileRequestStringData& data = aData.get_FileRequestStringData();
1468 if (NS_WARN_IF(data.string().IsEmpty())) {
1469 MOZ_CRASH_UNLESS_FUZZING();
1470 return false;
1473 break;
1476 case FileRequestData::TFileRequestBlobData: {
1477 break;
1480 default:
1481 MOZ_CRASH("Should never get here!");
1484 return true;
1487 void FileHandle::FinishOrAbort() {
1488 AssertIsOnBackgroundThread();
1489 MOZ_ASSERT(!mFinishedOrAborted);
1491 mFinishedOrAborted = true;
1493 if (!mHasBeenActive) {
1494 return;
1497 RefPtr<FinishOp> finishOp = new FinishOp(this, mAborted);
1499 FileHandleThreadPool* fileHandleThreadPool =
1500 GetFileHandleThreadPoolFor(mStorage);
1501 MOZ_ASSERT(fileHandleThreadPool);
1503 fileHandleThreadPool->Enqueue(this, finishOp, true);
1506 void FileHandle::ActorDestroy(ActorDestroyReason aWhy) {
1507 AssertIsOnBackgroundThread();
1509 MOZ_ASSERT(!mActorDestroyed);
1511 mActorDestroyed = true;
1513 if (!mFinishedOrAborted) {
1514 mAborted = true;
1516 mForceAborted = true;
1518 MaybeFinishOrAbort();
1522 mozilla::ipc::IPCResult FileHandle::RecvDeleteMe() {
1523 AssertIsOnBackgroundThread();
1524 MOZ_ASSERT(!IsActorDestroyed());
1526 IProtocol* mgr = Manager();
1527 if (!PBackgroundFileHandleParent::Send__delete__(this)) {
1528 return IPC_FAIL_NO_REASON(mgr);
1530 return IPC_OK();
1533 mozilla::ipc::IPCResult FileHandle::RecvFinish() {
1534 AssertIsOnBackgroundThread();
1536 if (NS_WARN_IF(mFinishOrAbortReceived)) {
1537 MOZ_CRASH_UNLESS_FUZZING();
1538 return IPC_FAIL_NO_REASON(this);
1541 mFinishOrAbortReceived = true;
1543 MaybeFinishOrAbort();
1544 return IPC_OK();
1547 mozilla::ipc::IPCResult FileHandle::RecvAbort() {
1548 AssertIsOnBackgroundThread();
1550 if (NS_WARN_IF(mFinishOrAbortReceived)) {
1551 MOZ_CRASH_UNLESS_FUZZING();
1552 return IPC_FAIL_NO_REASON(this);
1555 mFinishOrAbortReceived = true;
1557 Abort(/* aForce */ false);
1558 return IPC_OK();
1561 PBackgroundFileRequestParent* FileHandle::AllocPBackgroundFileRequestParent(
1562 const FileRequestParams& aParams) {
1563 AssertIsOnBackgroundThread();
1564 MOZ_ASSERT(aParams.type() != FileRequestParams::T__None);
1566 #ifdef DEBUG
1567 // Always verify parameters in DEBUG builds!
1568 bool trustParams = false;
1569 #else
1570 PBackgroundParent* backgroundActor = GetBackgroundParent();
1571 MOZ_ASSERT(backgroundActor);
1573 bool trustParams = !BackgroundParent::IsOtherProcessActor(backgroundActor);
1574 #endif
1576 if (NS_WARN_IF(!trustParams && !VerifyRequestParams(aParams))) {
1577 MOZ_CRASH_UNLESS_FUZZING();
1578 return nullptr;
1581 if (NS_WARN_IF(mFinishOrAbortReceived)) {
1582 MOZ_CRASH_UNLESS_FUZZING();
1583 return nullptr;
1586 RefPtr<NormalFileHandleOp> actor;
1588 switch (aParams.type()) {
1589 case FileRequestParams::TFileRequestGetMetadataParams:
1590 actor = new GetMetadataOp(this, aParams);
1591 break;
1593 case FileRequestParams::TFileRequestReadParams:
1594 actor = new ReadOp(this, aParams);
1595 break;
1597 case FileRequestParams::TFileRequestWriteParams:
1598 actor = new WriteOp(this, aParams);
1599 break;
1601 case FileRequestParams::TFileRequestTruncateParams:
1602 actor = new TruncateOp(this, aParams);
1603 break;
1605 case FileRequestParams::TFileRequestFlushParams:
1606 actor = new FlushOp(this, aParams);
1607 break;
1609 default:
1610 MOZ_CRASH("Should never get here!");
1613 MOZ_ASSERT(actor);
1615 // Transfer ownership to IPDL.
1616 return actor.forget().take();
1619 mozilla::ipc::IPCResult FileHandle::RecvPBackgroundFileRequestConstructor(
1620 PBackgroundFileRequestParent* aActor, const FileRequestParams& aParams) {
1621 AssertIsOnBackgroundThread();
1622 MOZ_ASSERT(aActor);
1623 MOZ_ASSERT(aParams.type() != FileRequestParams::T__None);
1625 auto* op = static_cast<NormalFileHandleOp*>(aActor);
1627 if (NS_WARN_IF(!op->Init(this))) {
1628 op->Cleanup();
1629 return IPC_FAIL_NO_REASON(this);
1632 op->Enqueue();
1633 return IPC_OK();
1636 bool FileHandle::DeallocPBackgroundFileRequestParent(
1637 PBackgroundFileRequestParent* aActor) {
1638 AssertIsOnBackgroundThread();
1639 MOZ_ASSERT(aActor);
1641 // Transfer ownership back from IPDL.
1642 RefPtr<NormalFileHandleOp> actor =
1643 dont_AddRef(static_cast<NormalFileHandleOp*>(aActor));
1644 return true;
1647 /*******************************************************************************
1648 * Local class implementations
1649 ******************************************************************************/
1651 void FileHandleOp::Enqueue() {
1652 AssertIsOnOwningThread();
1654 FileHandleThreadPool* fileHandleThreadPool =
1655 GetFileHandleThreadPoolFor(mFileHandle->Storage());
1656 MOZ_ASSERT(fileHandleThreadPool);
1658 fileHandleThreadPool->Enqueue(mFileHandle, this, false);
1660 #ifdef DEBUG
1661 mEnqueued = true;
1662 #endif
1664 mFileHandle->NoteActiveRequest();
1667 void FileHandle::FinishOp::RunOnThreadPool() {
1668 AssertIsOnThreadPool();
1669 MOZ_ASSERT(mFileHandle);
1671 nsCOMPtr<nsISupports>& stream = mFileHandle->mStream;
1673 if (!stream) {
1674 return;
1677 nsCOMPtr<nsIInputStream> inputStream = do_QueryInterface(stream);
1678 MOZ_ASSERT(inputStream);
1680 MOZ_ALWAYS_SUCCEEDS(inputStream->Close());
1682 stream = nullptr;
1685 void FileHandle::FinishOp::RunOnOwningThread() {
1686 AssertIsOnOwningThread();
1687 MOZ_ASSERT(mFileHandle);
1689 mFileHandle->SendCompleteNotification(mAborted);
1691 mFileHandle->GetMutableFile()->UnregisterFileHandle(mFileHandle);
1693 mFileHandle = nullptr;
1696 NormalFileHandleOp::~NormalFileHandleOp() {
1697 MOZ_ASSERT(!mFileHandle,
1698 "NormalFileHandleOp::Cleanup() was not called by a subclass!");
1701 bool NormalFileHandleOp::Init(FileHandle* aFileHandle) {
1702 AssertIsOnOwningThread();
1703 MOZ_ASSERT(aFileHandle);
1705 nsresult rv = aFileHandle->GetOrCreateStream(getter_AddRefs(mFileStream));
1706 if (NS_WARN_IF(NS_FAILED(rv))) {
1707 return false;
1710 return true;
1713 void NormalFileHandleOp::Cleanup() {
1714 AssertIsOnOwningThread();
1715 MOZ_ASSERT(mFileHandle);
1716 MOZ_ASSERT_IF(mEnqueued && !IsActorDestroyed(), mResponseSent);
1718 mFileHandle = nullptr;
1721 nsresult NormalFileHandleOp::SendSuccessResult() {
1722 AssertIsOnOwningThread();
1724 if (!IsActorDestroyed()) {
1725 FileRequestResponse response;
1726 GetResponse(response);
1728 MOZ_ASSERT(response.type() != FileRequestResponse::T__None);
1730 if (response.type() == FileRequestResponse::Tnsresult) {
1731 MOZ_ASSERT(NS_FAILED(response.get_nsresult()));
1733 return response.get_nsresult();
1736 if (NS_WARN_IF(
1737 !PBackgroundFileRequestParent::Send__delete__(this, response))) {
1738 return NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR;
1742 #ifdef DEBUG
1743 mResponseSent = true;
1744 #endif
1746 return NS_OK;
1749 bool NormalFileHandleOp::SendFailureResult(nsresult aResultCode) {
1750 AssertIsOnBackgroundThread();
1751 MOZ_ASSERT(NS_FAILED(aResultCode));
1753 bool result = false;
1755 if (!IsActorDestroyed()) {
1756 result = PBackgroundFileRequestParent::Send__delete__(
1757 this, ClampResultCode(aResultCode));
1760 #ifdef DEBUG
1761 mResponseSent = true;
1762 #endif
1764 return result;
1767 void NormalFileHandleOp::RunOnThreadPool() {
1768 AssertIsOnThreadPool();
1769 MOZ_ASSERT(mFileHandle);
1770 MOZ_ASSERT(NS_SUCCEEDED(mResultCode));
1772 // There are several cases where we don't actually have to to any work here.
1774 if (mFileHandleIsAborted) {
1775 // This transaction is already set to be aborted.
1776 mResultCode = NS_ERROR_DOM_FILEHANDLE_ABORT_ERR;
1777 } else if (mFileHandle->IsInvalidatedOnAnyThread()) {
1778 // This file handle is being invalidated.
1779 mResultCode = NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR;
1780 } else if (!OperationMayProceed()) {
1781 // The operation was canceled in some way, likely because the child process
1782 // has crashed.
1783 mResultCode = NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR;
1784 } else {
1785 nsresult rv = DoFileWork(mFileHandle);
1786 if (NS_FAILED(rv)) {
1787 mResultCode = rv;
1792 void NormalFileHandleOp::RunOnOwningThread() {
1793 AssertIsOnOwningThread();
1794 MOZ_ASSERT(mFileHandle);
1796 if (NS_WARN_IF(IsActorDestroyed())) {
1797 // Don't send any notifications if the actor was destroyed already.
1798 if (NS_SUCCEEDED(mResultCode)) {
1799 mResultCode = NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR;
1801 } else {
1802 if (mFileHandle->IsInvalidated()) {
1803 mResultCode = NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR;
1804 } else if (mFileHandle->IsAborted()) {
1805 // Aborted file handles always see their requests fail with ABORT_ERR,
1806 // even if the request succeeded or failed with another error.
1807 mResultCode = NS_ERROR_DOM_FILEHANDLE_ABORT_ERR;
1808 } else if (NS_SUCCEEDED(mResultCode)) {
1809 // This may release the IPDL reference.
1810 mResultCode = SendSuccessResult();
1813 if (NS_FAILED(mResultCode)) {
1814 // This should definitely release the IPDL reference.
1815 if (!SendFailureResult(mResultCode)) {
1816 // Abort the file handle.
1817 mFileHandle->Abort(/* aForce */ false);
1822 mFileHandle->NoteFinishedRequest();
1824 Cleanup();
1827 void NormalFileHandleOp::ActorDestroy(ActorDestroyReason aWhy) {
1828 AssertIsOnOwningThread();
1830 NoteActorDestroyed();
1833 nsresult CopyFileHandleOp::DoFileWork(FileHandle* aFileHandle) {
1834 AssertIsOnThreadPool();
1836 nsCOMPtr<nsIInputStream> inputStream;
1837 nsCOMPtr<nsIOutputStream> outputStream;
1839 if (mRead) {
1840 inputStream = do_QueryInterface(mFileStream);
1841 outputStream = do_QueryInterface(mBufferStream);
1842 } else {
1843 inputStream = do_QueryInterface(mBufferStream);
1844 outputStream = do_QueryInterface(mFileStream);
1847 MOZ_ASSERT(inputStream);
1848 MOZ_ASSERT(outputStream);
1850 nsCOMPtr<nsISeekableStream> seekableStream = do_QueryInterface(mFileStream);
1852 nsresult rv;
1854 if (seekableStream) {
1855 if (mOffset == UINT64_MAX) {
1856 rv = seekableStream->Seek(nsISeekableStream::NS_SEEK_END, 0);
1857 } else {
1858 rv = seekableStream->Seek(nsISeekableStream::NS_SEEK_SET, mOffset);
1860 if (NS_WARN_IF(NS_FAILED(rv))) {
1861 return rv;
1865 mOffset = 0;
1867 do {
1868 char copyBuffer[kStreamCopyBlockSize];
1870 uint64_t max = mSize - mOffset;
1871 if (max == 0) {
1872 break;
1875 uint32_t count = sizeof(copyBuffer);
1876 if (count > max) {
1877 count = max;
1880 uint32_t numRead;
1881 rv = inputStream->Read(copyBuffer, count, &numRead);
1882 if (NS_WARN_IF(NS_FAILED(rv))) {
1883 return rv;
1886 if (!numRead) {
1887 break;
1890 uint32_t numWrite;
1891 rv = outputStream->Write(copyBuffer, numRead, &numWrite);
1892 if (rv == NS_ERROR_FILE_NO_DEVICE_SPACE) {
1893 rv = NS_ERROR_DOM_FILEHANDLE_QUOTA_ERR;
1895 if (NS_WARN_IF(NS_FAILED(rv))) {
1896 return rv;
1899 if (NS_WARN_IF(numWrite != numRead)) {
1900 return NS_ERROR_FAILURE;
1903 mOffset += numWrite;
1905 nsCOMPtr<nsIRunnable> runnable = new ProgressRunnable(this, mOffset, mSize);
1907 mOwningEventTarget->Dispatch(runnable, NS_DISPATCH_NORMAL);
1908 } while (true);
1910 if (mOffset < mSize) {
1911 // end-of-file reached
1912 return NS_ERROR_FAILURE;
1914 MOZ_ASSERT(mOffset == mSize);
1916 if (mRead) {
1917 MOZ_ALWAYS_SUCCEEDS(outputStream->Close());
1918 } else {
1919 MOZ_ALWAYS_SUCCEEDS(inputStream->Close());
1922 return NS_OK;
1925 void CopyFileHandleOp::Cleanup() {
1926 AssertIsOnOwningThread();
1928 mBufferStream = nullptr;
1930 NormalFileHandleOp::Cleanup();
1933 NS_IMETHODIMP
1934 CopyFileHandleOp::ProgressRunnable::Run() {
1935 AssertIsOnBackgroundThread();
1937 Unused << mCopyFileHandleOp->SendProgress(mProgress, mProgressMax);
1939 mCopyFileHandleOp = nullptr;
1941 return NS_OK;
1944 GetMetadataOp::GetMetadataOp(FileHandle* aFileHandle,
1945 const FileRequestParams& aParams)
1946 : NormalFileHandleOp(aFileHandle),
1947 mParams(aParams.get_FileRequestGetMetadataParams()) {
1948 MOZ_ASSERT(aParams.type() ==
1949 FileRequestParams::TFileRequestGetMetadataParams);
1952 nsresult GetMetadataOp::DoFileWork(FileHandle* aFileHandle) {
1953 AssertIsOnThreadPool();
1955 nsresult rv;
1957 if (mFileHandle->Mode() == FileMode::Readwrite) {
1958 // Force a flush (so all pending writes are flushed to the disk and file
1959 // metadata is updated too).
1961 nsCOMPtr<nsIOutputStream> ostream = do_QueryInterface(mFileStream);
1962 MOZ_ASSERT(ostream);
1964 rv = ostream->Flush();
1965 if (NS_WARN_IF(NS_FAILED(rv))) {
1966 return rv;
1970 nsCOMPtr<nsIFileMetadata> metadata = do_QueryInterface(mFileStream);
1971 MOZ_ASSERT(metadata);
1973 if (mParams.size()) {
1974 int64_t size;
1975 rv = metadata->GetSize(&size);
1976 if (NS_WARN_IF(NS_FAILED(rv))) {
1977 return rv;
1980 if (NS_WARN_IF(size < 0)) {
1981 return NS_ERROR_FAILURE;
1984 mMetadata.size() = Some(uint64_t(size));
1985 } else {
1986 mMetadata.size() = Nothing();
1989 if (mParams.lastModified()) {
1990 int64_t lastModified;
1991 rv = metadata->GetLastModified(&lastModified);
1992 if (NS_WARN_IF(NS_FAILED(rv))) {
1993 return rv;
1996 mMetadata.lastModified() = Some(lastModified);
1997 } else {
1998 mMetadata.lastModified() = Nothing();
2001 return NS_OK;
2004 void GetMetadataOp::GetResponse(FileRequestResponse& aResponse) {
2005 AssertIsOnOwningThread();
2007 aResponse = FileRequestGetMetadataResponse(mMetadata);
2010 ReadOp::ReadOp(FileHandle* aFileHandle, const FileRequestParams& aParams)
2011 : CopyFileHandleOp(aFileHandle),
2012 mParams(aParams.get_FileRequestReadParams()) {
2013 MOZ_ASSERT(aParams.type() == FileRequestParams::TFileRequestReadParams);
2016 bool ReadOp::Init(FileHandle* aFileHandle) {
2017 AssertIsOnOwningThread();
2018 MOZ_ASSERT(aFileHandle);
2020 if (NS_WARN_IF(!NormalFileHandleOp::Init(aFileHandle))) {
2021 return false;
2024 mBufferStream = MemoryOutputStream::Create(mParams.size());
2025 if (NS_WARN_IF(!mBufferStream)) {
2026 return false;
2029 mOffset = mParams.offset();
2030 mSize = mParams.size();
2031 mRead = true;
2033 return true;
2036 void ReadOp::GetResponse(FileRequestResponse& aResponse) {
2037 AssertIsOnOwningThread();
2039 auto* stream = static_cast<MemoryOutputStream*>(mBufferStream.get());
2041 aResponse = FileRequestReadResponse(stream->Data());
2044 WriteOp::WriteOp(FileHandle* aFileHandle, const FileRequestParams& aParams)
2045 : CopyFileHandleOp(aFileHandle),
2046 mParams(aParams.get_FileRequestWriteParams()) {
2047 MOZ_ASSERT(aParams.type() == FileRequestParams::TFileRequestWriteParams);
2050 bool WriteOp::Init(FileHandle* aFileHandle) {
2051 AssertIsOnOwningThread();
2052 MOZ_ASSERT(aFileHandle);
2054 if (NS_WARN_IF(!NormalFileHandleOp::Init(aFileHandle))) {
2055 return false;
2058 nsCOMPtr<nsIInputStream> inputStream;
2060 const FileRequestData& data = mParams.data();
2061 switch (data.type()) {
2062 case FileRequestData::TFileRequestStringData: {
2063 const FileRequestStringData& stringData =
2064 data.get_FileRequestStringData();
2066 const nsCString& string = stringData.string();
2068 nsresult rv =
2069 NS_NewCStringInputStream(getter_AddRefs(inputStream), string);
2070 if (NS_WARN_IF(NS_FAILED(rv))) {
2071 return false;
2074 break;
2076 case FileRequestData::TFileRequestBlobData: {
2077 const FileRequestBlobData& blobData = data.get_FileRequestBlobData();
2079 RefPtr<BlobImpl> blobImpl = IPCBlobUtils::Deserialize(blobData.blob());
2080 if (NS_WARN_IF(!blobImpl)) {
2081 return false;
2084 IgnoredErrorResult rv;
2085 blobImpl->CreateInputStream(getter_AddRefs(inputStream), rv);
2086 if (NS_WARN_IF(rv.Failed())) {
2087 return false;
2090 break;
2093 default:
2094 MOZ_CRASH("Should never get here!");
2097 mBufferStream = inputStream;
2098 mOffset = mParams.offset();
2099 mSize = mParams.dataLength();
2100 mRead = false;
2102 return true;
2105 void WriteOp::GetResponse(FileRequestResponse& aResponse) {
2106 AssertIsOnOwningThread();
2107 aResponse = FileRequestWriteResponse();
2110 TruncateOp::TruncateOp(FileHandle* aFileHandle,
2111 const FileRequestParams& aParams)
2112 : NormalFileHandleOp(aFileHandle),
2113 mParams(aParams.get_FileRequestTruncateParams()) {
2114 MOZ_ASSERT(aParams.type() == FileRequestParams::TFileRequestTruncateParams);
2117 nsresult TruncateOp::DoFileWork(FileHandle* aFileHandle) {
2118 AssertIsOnThreadPool();
2120 nsCOMPtr<nsISeekableStream> sstream = do_QueryInterface(mFileStream);
2121 MOZ_ASSERT(sstream);
2123 if (mParams.offset()) {
2124 nsCOMPtr<nsIFileMetadata> fileMetadata = do_QueryInterface(mFileStream);
2125 MOZ_ASSERT(fileMetadata);
2127 int64_t size;
2128 nsresult rv = fileMetadata->GetSize(&size);
2129 if (NS_WARN_IF(NS_FAILED(rv))) {
2130 return rv;
2132 MOZ_ASSERT(size >= 0);
2134 if (mParams.offset() > static_cast<uint64_t>(size)) {
2135 // Cannot extend the size of a file through truncate.
2136 return NS_ERROR_DOM_INVALID_MODIFICATION_ERR;
2140 // XXX If we allowed truncate to extend the file size, we would to ensure that
2141 // the quota limit is checked, e.g. by making FileQuotaStream override Seek.
2142 nsresult rv = sstream->Seek(nsISeekableStream::NS_SEEK_SET, mParams.offset());
2143 if (NS_WARN_IF(NS_FAILED(rv))) {
2144 return rv;
2147 rv = sstream->SetEOF();
2148 if (NS_WARN_IF(NS_FAILED(rv))) {
2149 return rv;
2152 return NS_OK;
2155 void TruncateOp::GetResponse(FileRequestResponse& aResponse) {
2156 AssertIsOnOwningThread();
2157 aResponse = FileRequestTruncateResponse();
2160 FlushOp::FlushOp(FileHandle* aFileHandle, const FileRequestParams& aParams)
2161 : NormalFileHandleOp(aFileHandle),
2162 mParams(aParams.get_FileRequestFlushParams()) {
2163 MOZ_ASSERT(aParams.type() == FileRequestParams::TFileRequestFlushParams);
2166 nsresult FlushOp::DoFileWork(FileHandle* aFileHandle) {
2167 AssertIsOnThreadPool();
2169 nsCOMPtr<nsIOutputStream> ostream = do_QueryInterface(mFileStream);
2170 MOZ_ASSERT(ostream);
2172 nsresult rv = ostream->Flush();
2173 if (NS_WARN_IF(NS_FAILED(rv))) {
2174 return rv;
2177 return NS_OK;
2180 void FlushOp::GetResponse(FileRequestResponse& aResponse) {
2181 AssertIsOnOwningThread();
2182 aResponse = FileRequestFlushResponse();
2185 } // namespace mozilla::dom