Line data Source code
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/. */
7 :
8 : #include "ChannelEventQueue.h"
9 :
10 : #include "mozilla/Assertions.h"
11 : #include "mozilla/Unused.h"
12 : #include "nsISupports.h"
13 : #include "nsThreadUtils.h"
14 :
15 : namespace mozilla {
16 : namespace net {
17 :
18 : ChannelEvent*
19 16 : ChannelEventQueue::TakeEvent()
20 : {
21 16 : mMutex.AssertCurrentThreadOwns();
22 16 : MOZ_ASSERT(mFlushing);
23 :
24 16 : if (mSuspended || mEventQueue.IsEmpty()) {
25 4 : return nullptr;
26 : }
27 :
28 24 : UniquePtr<ChannelEvent> event(Move(mEventQueue[0]));
29 12 : mEventQueue.RemoveElementAt(0);
30 :
31 12 : return event.release();
32 : }
33 :
34 : void
35 5 : ChannelEventQueue::FlushQueue()
36 : {
37 : // Events flushed could include destruction of channel (and our own
38 : // destructor) unless we make sure its refcount doesn't drop to 0 while this
39 : // method is running.
40 10 : nsCOMPtr<nsISupports> kungFuDeathGrip(mOwner);
41 : mozilla::Unused << kungFuDeathGrip; // Not used in this function
42 :
43 : // Prevent flushed events from flushing the queue recursively
44 : {
45 10 : MutexAutoLock lock(mMutex);
46 5 : MOZ_ASSERT(!mFlushing);
47 5 : mFlushing = true;
48 : }
49 :
50 5 : bool needResumeOnOtherThread = false;
51 :
52 : while (true) {
53 27 : UniquePtr<ChannelEvent> event;
54 : {
55 28 : MutexAutoLock lock(mMutex);
56 16 : event.reset(TakeEvent());
57 16 : if (!event) {
58 4 : MOZ_ASSERT(mFlushing);
59 4 : mFlushing = false;
60 4 : MOZ_ASSERT(mEventQueue.IsEmpty() || (mSuspended || !!mForcedCount));
61 4 : break;
62 : }
63 : }
64 :
65 23 : nsCOMPtr<nsIEventTarget> target = event->GetEventTarget();
66 12 : MOZ_ASSERT(target);
67 :
68 12 : bool isCurrentThread = false;
69 12 : nsresult rv = target->IsOnCurrentThread(&isCurrentThread);
70 12 : if (NS_WARN_IF(NS_FAILED(rv))) {
71 : // Simply run this event on current thread if we are not sure about it
72 : // in release channel, or assert in Aurora/Nightly channel.
73 0 : MOZ_DIAGNOSTIC_ASSERT(false);
74 : isCurrentThread = true;
75 : }
76 :
77 12 : if (!isCurrentThread) {
78 : // Next event needs to run on another thread. Put it back to
79 : // the front of the queue can try resume on that thread.
80 1 : Suspend();
81 1 : PrependEvent(event);
82 :
83 1 : needResumeOnOtherThread = true;
84 : {
85 2 : MutexAutoLock lock(mMutex);
86 1 : MOZ_ASSERT(mFlushing);
87 1 : mFlushing = false;
88 1 : MOZ_ASSERT(!mEventQueue.IsEmpty());
89 : }
90 1 : break;
91 : }
92 :
93 11 : event->Run();
94 11 : } // end of while(true)
95 :
96 : // The flush procedure is aborted because next event cannot be run on current
97 : // thread. We need to resume the event processing right after flush procedure
98 : // is finished.
99 : // Note: we cannot call Resume() while "mFlushing == true" because
100 : // CompleteResume will not trigger FlushQueue while there is an ongoing flush.
101 5 : if (needResumeOnOtherThread) {
102 1 : Resume();
103 : }
104 5 : }
105 :
106 : void
107 1 : ChannelEventQueue::Suspend()
108 : {
109 2 : MutexAutoLock lock(mMutex);
110 1 : SuspendInternal();
111 1 : }
112 :
113 : void
114 5 : ChannelEventQueue::SuspendInternal()
115 : {
116 5 : mMutex.AssertCurrentThreadOwns();
117 :
118 5 : mSuspended = true;
119 5 : mSuspendCount++;
120 5 : }
121 :
122 1 : void ChannelEventQueue::Resume()
123 : {
124 2 : MutexAutoLock lock(mMutex);
125 1 : ResumeInternal();
126 1 : }
127 :
128 : void
129 5 : ChannelEventQueue::ResumeInternal()
130 : {
131 5 : mMutex.AssertCurrentThreadOwns();
132 :
133 : // Resuming w/o suspend: error in debug mode, ignore in build
134 5 : MOZ_ASSERT(mSuspendCount > 0);
135 5 : if (mSuspendCount <= 0) {
136 0 : return;
137 : }
138 :
139 5 : if (!--mSuspendCount) {
140 5 : if (mEventQueue.IsEmpty()) {
141 : // Nothing in queue to flush, simply clear the flag.
142 0 : mSuspended = false;
143 0 : return;
144 : }
145 :
146 : // Hold a strong reference of mOwner to avoid the channel release
147 : // before CompleteResume was executed.
148 : class CompleteResumeRunnable : public CancelableRunnable
149 : {
150 : public:
151 5 : explicit CompleteResumeRunnable(ChannelEventQueue* aQueue, nsISupports* aOwner)
152 5 : : CancelableRunnable("CompleteResumeRunnable")
153 : , mQueue(aQueue)
154 5 : , mOwner(aOwner)
155 : {
156 5 : }
157 :
158 5 : NS_IMETHOD Run() override
159 : {
160 5 : mQueue->CompleteResume();
161 5 : return NS_OK;
162 : }
163 :
164 : private:
165 15 : virtual ~CompleteResumeRunnable() {}
166 :
167 : RefPtr<ChannelEventQueue> mQueue;
168 : nsCOMPtr<nsISupports> mOwner;
169 : };
170 :
171 : // Worker thread requires a CancelableRunnable.
172 10 : RefPtr<Runnable> event = new CompleteResumeRunnable(this, mOwner);
173 :
174 10 : nsCOMPtr<nsIEventTarget> target;
175 5 : target = mEventQueue[0]->GetEventTarget();
176 5 : MOZ_ASSERT(target);
177 :
178 5 : Unused << NS_WARN_IF(NS_FAILED(target->Dispatch(event.forget(),
179 : NS_DISPATCH_NORMAL)));
180 : }
181 : }
182 :
183 : } // namespace net
184 : } // namespace mozilla
|