Line data Source code
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 "mozilla/ThreadEventQueue.h"
8 : #include "mozilla/EventQueue.h"
9 : #include "LabeledEventQueue.h"
10 :
11 : #include "LeakRefPtr.h"
12 : #include "nsComponentManagerUtils.h"
13 : #include "nsIThreadInternal.h"
14 : #include "nsThreadUtils.h"
15 : #include "PrioritizedEventQueue.h"
16 : #include "ThreadEventTarget.h"
17 :
18 : using namespace mozilla;
19 :
20 : template<class InnerQueueT>
21 0 : class ThreadEventQueue<InnerQueueT>::NestedSink : public ThreadTargetSink
22 : {
23 : public:
24 0 : NestedSink(EventQueue* aQueue, ThreadEventQueue* aOwner)
25 : : mQueue(aQueue)
26 0 : , mOwner(aOwner)
27 : {
28 0 : }
29 :
30 0 : bool PutEvent(already_AddRefed<nsIRunnable>&& aEvent,
31 : EventPriority aPriority) final
32 : {
33 0 : return mOwner->PutEventInternal(std::move(aEvent), aPriority, this);
34 : }
35 :
36 0 : void Disconnect(const MutexAutoLock& aProofOfLock) final
37 : {
38 0 : mQueue = nullptr;
39 0 : }
40 :
41 : private:
42 : friend class ThreadEventQueue;
43 :
44 : // This is a non-owning reference. It must live at least until Disconnect is
45 : // called to clear it out.
46 : EventQueue* mQueue;
47 : RefPtr<ThreadEventQueue> mOwner;
48 : };
49 :
50 : template<class InnerQueueT>
51 0 : ThreadEventQueue<InnerQueueT>::ThreadEventQueue(UniquePtr<InnerQueueT> aQueue)
52 0 : : mBaseQueue(std::move(aQueue))
53 : , mLock("ThreadEventQueue")
54 0 : , mEventsAvailable(mLock, "EventsAvail")
55 : {
56 : static_assert(IsBaseOf<AbstractEventQueue, InnerQueueT>::value,
57 : "InnerQueueT must be an AbstractEventQueue subclass");
58 0 : }
59 :
60 : template<class InnerQueueT>
61 0 : ThreadEventQueue<InnerQueueT>::~ThreadEventQueue()
62 : {
63 0 : MOZ_ASSERT(mNestedQueues.IsEmpty());
64 0 : }
65 :
66 : template<class InnerQueueT>
67 : bool
68 0 : ThreadEventQueue<InnerQueueT>::PutEvent(already_AddRefed<nsIRunnable>&& aEvent,
69 : EventPriority aPriority)
70 : {
71 0 : return PutEventInternal(std::move(aEvent), aPriority, nullptr);
72 : }
73 :
74 : template<class InnerQueueT>
75 : bool
76 0 : ThreadEventQueue<InnerQueueT>::PutEventInternal(already_AddRefed<nsIRunnable>&& aEvent,
77 : EventPriority aPriority,
78 : NestedSink* aSink)
79 : {
80 : // We want to leak the reference when we fail to dispatch it, so that
81 : // we won't release the event in a wrong thread.
82 0 : LeakRefPtr<nsIRunnable> event(std::move(aEvent));
83 0 : nsCOMPtr<nsIThreadObserver> obs;
84 :
85 : {
86 : // Check if the runnable wants to override the passed-in priority.
87 : // Do this outside the lock, so runnables implemented in JS can QI
88 : // (and possibly GC) outside of the lock.
89 : if (InnerQueueT::SupportsPrioritization) {
90 0 : auto* e = event.get(); // can't do_QueryInterface on LeakRefPtr.
91 0 : if (nsCOMPtr<nsIRunnablePriority> runnablePrio = do_QueryInterface(e)) {
92 0 : uint32_t prio = nsIRunnablePriority::PRIORITY_NORMAL;
93 0 : runnablePrio->GetPriority(&prio);
94 0 : if (prio == nsIRunnablePriority::PRIORITY_HIGH) {
95 : aPriority = EventPriority::High;
96 0 : } else if (prio == nsIRunnablePriority::PRIORITY_INPUT) {
97 0 : aPriority = EventPriority::Input;
98 : }
99 : }
100 : }
101 :
102 0 : MutexAutoLock lock(mLock);
103 :
104 0 : if (mEventsAreDoomed) {
105 0 : return false;
106 : }
107 :
108 0 : if (aSink) {
109 0 : if (!aSink->mQueue) {
110 : return false;
111 : }
112 :
113 0 : aSink->mQueue->PutEvent(event.take(), aPriority, lock);
114 : } else {
115 0 : mBaseQueue->PutEvent(event.take(), aPriority, lock);
116 : }
117 :
118 0 : mEventsAvailable.Notify();
119 :
120 : // Make sure to grab the observer before dropping the lock, otherwise the
121 : // event that we just placed into the queue could run and eventually delete
122 : // this nsThread before the calling thread is scheduled again. We would then
123 : // crash while trying to access a dead nsThread.
124 0 : obs = mObserver;
125 : }
126 :
127 0 : if (obs) {
128 0 : obs->OnDispatchedEvent();
129 : }
130 :
131 : return true;
132 : }
133 :
134 : template<class InnerQueueT>
135 : already_AddRefed<nsIRunnable>
136 0 : ThreadEventQueue<InnerQueueT>::GetEvent(bool aMayWait,
137 : EventPriority* aPriority)
138 : {
139 0 : MutexAutoLock lock(mLock);
140 :
141 0 : nsCOMPtr<nsIRunnable> event;
142 223 : for (;;) {
143 0 : if (mNestedQueues.IsEmpty()) {
144 0 : event = mBaseQueue->GetEvent(aPriority, lock);
145 : } else {
146 : // We always get events from the topmost queue when there are nested
147 : // queues.
148 0 : event = mNestedQueues.LastElement().mQueue->GetEvent(aPriority, lock);
149 : }
150 :
151 0 : if (event || !aMayWait) {
152 : break;
153 : }
154 :
155 0 : AUTO_PROFILER_LABEL("ThreadEventQueue::GetEvent::Wait", IDLE);
156 240 : mEventsAvailable.Wait();
157 : }
158 :
159 5252 : return event.forget();
160 : }
161 :
162 : template<class InnerQueueT>
163 : bool
164 3045 : ThreadEventQueue<InnerQueueT>::HasPendingEvent()
165 : {
166 6090 : MutexAutoLock lock(mLock);
167 :
168 : // We always get events from the topmost queue when there are nested queues.
169 0 : if (mNestedQueues.IsEmpty()) {
170 2435 : return mBaseQueue->HasReadyEvent(lock);
171 : } else {
172 610 : return mNestedQueues.LastElement().mQueue->HasReadyEvent(lock);
173 : }
174 : }
175 :
176 : template<class InnerQueueT>
177 : bool
178 37 : ThreadEventQueue<InnerQueueT>::ShutdownIfNoPendingEvents()
179 : {
180 0 : MutexAutoLock lock(mLock);
181 0 : if (mNestedQueues.IsEmpty() && mBaseQueue->IsEmpty(lock)) {
182 0 : mEventsAreDoomed = true;
183 37 : return true;
184 : }
185 : return false;
186 : }
187 :
188 : template<class InnerQueueT>
189 : void
190 0 : ThreadEventQueue<InnerQueueT>::EnableInputEventPrioritization()
191 : {
192 0 : MutexAutoLock lock(mLock);
193 0 : mBaseQueue->EnableInputEventPrioritization(lock);
194 0 : }
195 :
196 : template<class InnerQueueT>
197 : void
198 0 : ThreadEventQueue<InnerQueueT>::FlushInputEventPrioritization()
199 : {
200 0 : MutexAutoLock lock(mLock);
201 0 : mBaseQueue->FlushInputEventPrioritization(lock);
202 0 : }
203 :
204 : template<class InnerQueueT>
205 : void
206 0 : ThreadEventQueue<InnerQueueT>::SuspendInputEventPrioritization()
207 : {
208 0 : MutexAutoLock lock(mLock);
209 0 : mBaseQueue->SuspendInputEventPrioritization(lock);
210 0 : }
211 :
212 : template<class InnerQueueT>
213 : void
214 0 : ThreadEventQueue<InnerQueueT>::ResumeInputEventPrioritization()
215 : {
216 0 : MutexAutoLock lock(mLock);
217 0 : mBaseQueue->ResumeInputEventPrioritization(lock);
218 0 : }
219 :
220 : template<class InnerQueueT>
221 : already_AddRefed<nsISerialEventTarget>
222 91 : ThreadEventQueue<InnerQueueT>::PushEventQueue()
223 : {
224 0 : auto queue = MakeUnique<EventQueue>();
225 0 : RefPtr<NestedSink> sink = new NestedSink(queue.get(), this);
226 364 : RefPtr<ThreadEventTarget> eventTarget = new ThreadEventTarget(sink, NS_IsMainThread());
227 :
228 182 : MutexAutoLock lock(mLock);
229 :
230 0 : mNestedQueues.AppendElement(NestedQueueItem(std::move(queue), eventTarget));
231 273 : return eventTarget.forget();
232 : }
233 :
234 : template<class InnerQueueT>
235 : void
236 87 : ThreadEventQueue<InnerQueueT>::PopEventQueue(nsIEventTarget* aTarget)
237 : {
238 174 : MutexAutoLock lock(mLock);
239 :
240 174 : MOZ_ASSERT(!mNestedQueues.IsEmpty());
241 :
242 87 : NestedQueueItem& item = mNestedQueues.LastElement();
243 :
244 174 : MOZ_ASSERT(aTarget == item.mEventTarget);
245 :
246 : // Disconnect the event target that will be popped.
247 87 : item.mEventTarget->Disconnect(lock);
248 :
249 : AbstractEventQueue* prevQueue =
250 0 : mNestedQueues.Length() == 1
251 0 : ? static_cast<AbstractEventQueue*>(mBaseQueue.get())
252 257 : : static_cast<AbstractEventQueue*>(mNestedQueues[mNestedQueues.Length() - 2].mQueue.get());
253 :
254 : // Move events from the old queue to the new one.
255 87 : nsCOMPtr<nsIRunnable> event;
256 : EventPriority prio;
257 1 : while ((event = item.mQueue->GetEvent(&prio, lock))) {
258 0 : prevQueue->PutEvent(event.forget(), prio, lock);
259 : }
260 :
261 0 : mNestedQueues.RemoveLastElement();
262 87 : }
263 :
264 : template<class InnerQueueT>
265 : already_AddRefed<nsIThreadObserver>
266 37 : ThreadEventQueue<InnerQueueT>::GetObserver()
267 : {
268 0 : MutexAutoLock lock(mLock);
269 74 : return do_AddRef(mObserver);
270 : }
271 :
272 : template<class InnerQueueT>
273 : already_AddRefed<nsIThreadObserver>
274 2577 : ThreadEventQueue<InnerQueueT>::GetObserverOnThread()
275 : {
276 2577 : return do_AddRef(mObserver);
277 : }
278 :
279 : template<class InnerQueueT>
280 : void
281 41 : ThreadEventQueue<InnerQueueT>::SetObserver(nsIThreadObserver* aObserver)
282 : {
283 0 : MutexAutoLock lock(mLock);
284 0 : mObserver = aObserver;
285 : }
286 :
287 : namespace mozilla {
288 : template class ThreadEventQueue<EventQueue>;
289 : template class ThreadEventQueue<PrioritizedEventQueue<EventQueue>>;
290 : template class ThreadEventQueue<PrioritizedEventQueue<LabeledEventQueue>>;
291 : }
|