ServiceWorkerJobQueue.cpp (3437B)
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 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "ServiceWorkerJobQueue.h" 8 9 #include "ServiceWorkerJob.h" 10 #include "mozilla/dom/WorkerCommon.h" 11 #include "nsThreadUtils.h" 12 13 namespace mozilla::dom { 14 15 class ServiceWorkerJobQueue::Callback final 16 : public ServiceWorkerJob::Callback { 17 RefPtr<ServiceWorkerJobQueue> mQueue; 18 19 ~Callback() = default; 20 21 public: 22 explicit Callback(ServiceWorkerJobQueue* aQueue) : mQueue(aQueue) { 23 MOZ_ASSERT(NS_IsMainThread()); 24 MOZ_ASSERT(mQueue); 25 } 26 27 virtual void JobFinished(ServiceWorkerJob* aJob, 28 ErrorResult& aStatus) override { 29 MOZ_ASSERT(NS_IsMainThread()); 30 mQueue->JobFinished(aJob); 31 } 32 33 virtual void JobDiscarded(ErrorResult&) override { 34 // no-op; nothing to do. 35 } 36 37 NS_INLINE_DECL_REFCOUNTING(ServiceWorkerJobQueue::Callback, override) 38 }; 39 40 ServiceWorkerJobQueue::~ServiceWorkerJobQueue() { 41 MOZ_ASSERT(NS_IsMainThread()); 42 MOZ_ASSERT(mJobList.IsEmpty()); 43 } 44 45 void ServiceWorkerJobQueue::JobFinished(ServiceWorkerJob* aJob) { 46 MOZ_ASSERT(NS_IsMainThread()); 47 MOZ_ASSERT(aJob); 48 49 // XXX There are some corner cases where jobs can double-complete. Until 50 // we track all these down we do a non-fatal assert in debug builds and 51 // a runtime check to verify the queue is in the correct state. 52 NS_ASSERTION(!mJobList.IsEmpty(), 53 "Job queue should contain the job that just completed."); 54 NS_ASSERTION(mJobList.SafeElementAt(0, nullptr) == aJob, 55 "Job queue should contain the job that just completed."); 56 if (NS_WARN_IF(mJobList.SafeElementAt(0, nullptr) != aJob)) { 57 return; 58 } 59 60 mJobList.RemoveElementAt(0); 61 62 if (mJobList.IsEmpty()) { 63 return; 64 } 65 66 RunJob(); 67 } 68 69 void ServiceWorkerJobQueue::RunJob() { 70 MOZ_ASSERT(NS_IsMainThread()); 71 MOZ_ASSERT(!mJobList.IsEmpty()); 72 MOZ_ASSERT(mJobList[0]->GetState() == ServiceWorkerJob::State::Initial); 73 74 RefPtr<Callback> callback = new Callback(this); 75 mJobList[0]->Start(callback); 76 } 77 78 ServiceWorkerJobQueue::ServiceWorkerJobQueue() { 79 MOZ_ASSERT(NS_IsMainThread()); 80 } 81 82 void ServiceWorkerJobQueue::ScheduleJob(ServiceWorkerJob* aJob) { 83 MOZ_ASSERT(NS_IsMainThread()); 84 MOZ_ASSERT(aJob); 85 MOZ_ASSERT(!mJobList.Contains(aJob)); 86 87 if (mJobList.IsEmpty()) { 88 mJobList.AppendElement(aJob); 89 RunJob(); 90 return; 91 } 92 93 MOZ_ASSERT(mJobList[0]->GetState() == ServiceWorkerJob::State::Started); 94 95 RefPtr<ServiceWorkerJob>& tailJob = mJobList[mJobList.Length() - 1]; 96 if (!tailJob->ResultCallbacksInvoked() && aJob->IsEquivalentTo(tailJob)) { 97 tailJob->StealResultCallbacksFrom(aJob); 98 return; 99 } 100 101 mJobList.AppendElement(aJob); 102 } 103 104 void ServiceWorkerJobQueue::CancelAll() { 105 MOZ_ASSERT(NS_IsMainThread()); 106 107 for (RefPtr<ServiceWorkerJob>& job : mJobList) { 108 job->Cancel(); 109 } 110 111 // Remove jobs that are queued but not started since they should never 112 // run after being canceled. This means throwing away all jobs except 113 // for the job at the front of the list. 114 if (!mJobList.IsEmpty()) { 115 MOZ_ASSERT(mJobList[0]->GetState() == ServiceWorkerJob::State::Started); 116 mJobList.TruncateLength(1); 117 } 118 } 119 120 } // namespace mozilla::dom