Backed out changeset b71c8c052463 (bug 1943846) for causing mass failures. CLOSED...
[gecko.git] / netwerk / ipc / ChannelEventQueue.cpp
blobf58548f08f322a7703f6aff3dd584def93de6138
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set sw=2 ts=8 et tw=80 :
3 */
4 /* This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
8 #include "ChannelEventQueue.h"
10 #include "mozilla/Assertions.h"
11 #include "mozilla/Unused.h"
12 #include "nsIChannel.h"
13 #include "mozilla/dom/Document.h"
14 #include "nsThreadUtils.h"
16 namespace mozilla {
17 namespace net {
19 ChannelEvent* ChannelEventQueue::TakeEvent() {
20 mMutex.AssertCurrentThreadOwns();
21 MOZ_ASSERT(mFlushing);
23 if (mSuspended || mEventQueue.IsEmpty()) {
24 return nullptr;
27 UniquePtr<ChannelEvent> event(std::move(mEventQueue[0]));
28 mEventQueue.RemoveElementAt(0);
30 return event.release();
33 void ChannelEventQueue::FlushQueue() {
34 mMutex.AssertCurrentThreadOwns();
35 // Events flushed could include destruction of channel (and our own
36 // destructor) unless we make sure its refcount doesn't drop to 0 while this
37 // method is running.
38 nsCOMPtr<nsISupports> kungFuDeathGrip;
39 kungFuDeathGrip = mOwner;
40 mozilla::Unused << kungFuDeathGrip; // Not used in this function
42 MOZ_ASSERT(mFlushing);
44 bool needResumeOnOtherThread = false;
46 while (true) {
47 UniquePtr<ChannelEvent> event;
48 event.reset(TakeEvent());
49 if (!event) {
50 MOZ_ASSERT(mFlushing);
51 mFlushing = false;
52 MOZ_ASSERT(mEventQueue.IsEmpty() || (mSuspended || !!mForcedCount));
53 break;
56 nsCOMPtr<nsIEventTarget> target = event->GetEventTarget();
57 MOZ_ASSERT(target);
59 bool isCurrentThread = false;
60 nsresult rv = target->IsOnCurrentThread(&isCurrentThread);
61 if (NS_WARN_IF(NS_FAILED(rv))) {
62 // Simply run this event on current thread if we are not sure about it
63 // in release channel, or assert in Aurora/Nightly channel.
64 MOZ_DIAGNOSTIC_CRASH("IsOnCurrentThread failed");
65 isCurrentThread = true;
68 if (!isCurrentThread) {
69 // Next event needs to run on another thread. Put it back to
70 // the front of the queue can try resume on that thread.
71 SuspendInternal();
72 PrependEventInternal(std::move(event));
74 needResumeOnOtherThread = true;
75 MOZ_ASSERT(mFlushing);
76 mFlushing = false;
77 MOZ_ASSERT(!mEventQueue.IsEmpty());
78 break;
81 MutexAutoUnlock unlock(mMutex);
82 event->Run();
84 } // end of while(true)
86 // The flush procedure is aborted because next event cannot be run on current
87 // thread. We need to resume the event processing right after flush procedure
88 // is finished.
89 // Note: we cannot call Resume() while "mFlushing == true" because
90 // CompleteResume will not trigger FlushQueue while there is an ongoing flush.
91 if (needResumeOnOtherThread) {
92 ResumeInternal();
96 void ChannelEventQueue::Suspend() {
97 MutexAutoLock lock(mMutex);
98 SuspendInternal();
101 void ChannelEventQueue::SuspendInternal() {
102 mMutex.AssertCurrentThreadOwns();
104 mSuspended = true;
105 mSuspendCount++;
108 void ChannelEventQueue::Resume() {
109 MutexAutoLock lock(mMutex);
110 ResumeInternal();
113 void ChannelEventQueue::ResumeInternal() {
114 mMutex.AssertCurrentThreadOwns();
116 // Resuming w/o suspend: error in debug mode, ignore in build
117 MOZ_ASSERT(mSuspendCount > 0);
118 if (mSuspendCount <= 0) {
119 return;
122 if (!--mSuspendCount) {
123 if (mEventQueue.IsEmpty() || !!mForcedCount) {
124 // Nothing in queue to flush or waiting for AutoEventEnqueuer to
125 // finish the force enqueue period, simply clear the flag.
126 mSuspended = false;
127 return;
130 // Hold a strong reference of mOwner to avoid the channel release
131 // before CompleteResume was executed.
132 class CompleteResumeRunnable : public Runnable {
133 public:
134 explicit CompleteResumeRunnable(ChannelEventQueue* aQueue,
135 nsISupports* aOwner)
136 : Runnable("CompleteResumeRunnable"),
137 mQueue(aQueue),
138 mOwner(aOwner) {}
140 NS_IMETHOD Run() override {
141 mQueue->CompleteResume();
142 return NS_OK;
145 private:
146 virtual ~CompleteResumeRunnable() = default;
148 RefPtr<ChannelEventQueue> mQueue;
149 nsCOMPtr<nsISupports> mOwner;
152 if (!mOwner) {
153 return;
156 // Worker thread requires a CancelableRunnable.
157 RefPtr<Runnable> event = new CompleteResumeRunnable(this, mOwner);
159 nsCOMPtr<nsIEventTarget> target;
160 target = mEventQueue[0]->GetEventTarget();
161 MOZ_ASSERT(target);
163 Unused << NS_WARN_IF(
164 NS_FAILED(target->Dispatch(event.forget(), NS_DISPATCH_NORMAL)));
168 bool ChannelEventQueue::MaybeSuspendIfEventsAreSuppressed() {
169 // We only ever need to suppress events on the main thread, since this is
170 // where content scripts can run.
171 if (!NS_IsMainThread()) {
172 return false;
175 // Only suppress events for queues associated with XHRs, as these can cause
176 // content scripts to run.
177 if (mHasCheckedForAsyncXMLHttpRequest && !mForAsyncXMLHttpRequest) {
178 return false;
181 mMutex.AssertCurrentThreadOwns();
182 nsCOMPtr<nsIChannel> channel(do_QueryInterface(mOwner));
183 if (!channel) {
184 return false;
187 nsCOMPtr<nsILoadInfo> loadInfo = channel->LoadInfo();
188 // Figure out if this is for an Async XHR, if we haven't done so already.
189 // We don't want to suspend Sync XHRs, as they'll always suspend event
190 // handling on the document, but we still need to process events for them.
191 if (!mHasCheckedForAsyncXMLHttpRequest) {
192 nsContentPolicyType contentType = loadInfo->InternalContentPolicyType();
193 mForAsyncXMLHttpRequest =
194 contentType == nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST_ASYNC;
195 mHasCheckedForAsyncXMLHttpRequest = true;
197 if (!mForAsyncXMLHttpRequest) {
198 return false;
202 // Suspend the queue if the associated document has suppressed event handling.
203 RefPtr<dom::Document> document;
204 loadInfo->GetLoadingDocument(getter_AddRefs(document));
205 if (document && document->EventHandlingSuppressed()) {
206 document->AddSuspendedChannelEventQueue(this);
207 SuspendInternal();
208 return true;
211 return false;
214 } // namespace net
215 } // namespace mozilla