Bug 1755481: correct documentation of `nsIClipboard::getData`. r=mccr8
[gecko.git] / xpcom / threads / ThreadEventQueue.cpp
blob782b39aa2cde25af8ea292390ae630fabe8b488b
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/. */
7 #include "mozilla/ThreadEventQueue.h"
8 #include "mozilla/EventQueue.h"
10 #include "LeakRefPtr.h"
11 #include "nsComponentManagerUtils.h"
12 #include "nsIThreadInternal.h"
13 #include "nsThreadUtils.h"
14 #include "nsThread.h"
15 #include "ThreadEventTarget.h"
16 #include "mozilla/ProfilerLabels.h"
17 #include "mozilla/TaskController.h"
18 #include "mozilla/StaticPrefs_threads.h"
20 using namespace mozilla;
22 class ThreadEventQueue::NestedSink : public ThreadTargetSink {
23 public:
24 NestedSink(EventQueue* aQueue, ThreadEventQueue* aOwner)
25 : mQueue(aQueue), mOwner(aOwner) {}
27 bool PutEvent(already_AddRefed<nsIRunnable>&& aEvent,
28 EventQueuePriority aPriority) final {
29 return mOwner->PutEventInternal(std::move(aEvent), aPriority, this);
32 void Disconnect(const MutexAutoLock& aProofOfLock) final { mQueue = nullptr; }
34 size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) {
35 if (mQueue) {
36 return mQueue->SizeOfIncludingThis(aMallocSizeOf);
38 return 0;
41 private:
42 friend class ThreadEventQueue;
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;
50 ThreadEventQueue::ThreadEventQueue(UniquePtr<EventQueue> aQueue,
51 bool aIsMainThread)
52 : mBaseQueue(std::move(aQueue)),
53 mLock("ThreadEventQueue"),
54 mEventsAvailable(mLock, "EventsAvail"),
55 mIsMainThread(aIsMainThread) {
56 if (aIsMainThread) {
57 TaskController::Get()->SetConditionVariable(&mEventsAvailable);
61 ThreadEventQueue::~ThreadEventQueue() { MOZ_ASSERT(mNestedQueues.IsEmpty()); }
63 bool ThreadEventQueue::PutEvent(already_AddRefed<nsIRunnable>&& aEvent,
64 EventQueuePriority aPriority) {
65 return PutEventInternal(std::move(aEvent), aPriority, nullptr);
68 bool ThreadEventQueue::PutEventInternal(already_AddRefed<nsIRunnable>&& aEvent,
69 EventQueuePriority aPriority,
70 NestedSink* aSink) {
71 // We want to leak the reference when we fail to dispatch it, so that
72 // we won't release the event in a wrong thread.
73 LeakRefPtr<nsIRunnable> event(std::move(aEvent));
74 nsCOMPtr<nsIThreadObserver> obs;
77 // Check if the runnable wants to override the passed-in priority.
78 // Do this outside the lock, so runnables implemented in JS can QI
79 // (and possibly GC) outside of the lock.
80 if (mIsMainThread) {
81 auto* e = event.get(); // can't do_QueryInterface on LeakRefPtr.
82 if (nsCOMPtr<nsIRunnablePriority> runnablePrio = do_QueryInterface(e)) {
83 uint32_t prio = nsIRunnablePriority::PRIORITY_NORMAL;
84 runnablePrio->GetPriority(&prio);
85 if (prio == nsIRunnablePriority::PRIORITY_CONTROL) {
86 aPriority = EventQueuePriority::Control;
87 } else if (prio == nsIRunnablePriority::PRIORITY_RENDER_BLOCKING) {
88 aPriority = EventQueuePriority::RenderBlocking;
89 } else if (prio == nsIRunnablePriority::PRIORITY_VSYNC) {
90 aPriority = EventQueuePriority::Vsync;
91 } else if (prio == nsIRunnablePriority::PRIORITY_INPUT_HIGH) {
92 aPriority = EventQueuePriority::InputHigh;
93 } else if (prio == nsIRunnablePriority::PRIORITY_MEDIUMHIGH) {
94 aPriority = EventQueuePriority::MediumHigh;
95 } else if (prio == nsIRunnablePriority::PRIORITY_DEFERRED_TIMERS) {
96 aPriority = EventQueuePriority::DeferredTimers;
97 } else if (prio == nsIRunnablePriority::PRIORITY_IDLE) {
98 aPriority = EventQueuePriority::Idle;
102 if (aPriority == EventQueuePriority::Control &&
103 !StaticPrefs::threads_control_event_queue_enabled()) {
104 aPriority = EventQueuePriority::MediumHigh;
108 MutexAutoLock lock(mLock);
110 if (mEventsAreDoomed) {
111 return false;
114 if (aSink) {
115 if (!aSink->mQueue) {
116 return false;
119 aSink->mQueue->PutEvent(event.take(), aPriority, lock);
120 } else {
121 mBaseQueue->PutEvent(event.take(), aPriority, lock);
124 mEventsAvailable.Notify();
126 // Make sure to grab the observer before dropping the lock, otherwise the
127 // event that we just placed into the queue could run and eventually delete
128 // this nsThread before the calling thread is scheduled again. We would then
129 // crash while trying to access a dead nsThread.
130 obs = mObserver;
133 if (obs) {
134 obs->OnDispatchedEvent();
137 return true;
140 already_AddRefed<nsIRunnable> ThreadEventQueue::GetEvent(
141 bool aMayWait, mozilla::TimeDuration* aLastEventDelay) {
142 nsCOMPtr<nsIRunnable> event;
144 // Scope for lock. When we are about to return, we will exit this
145 // scope so we can do some work after releasing the lock but
146 // before returning.
147 MutexAutoLock lock(mLock);
149 for (;;) {
150 const bool noNestedQueue = mNestedQueues.IsEmpty();
151 if (noNestedQueue) {
152 event = mBaseQueue->GetEvent(lock, aLastEventDelay);
153 } else {
154 // We always get events from the topmost queue when there are nested
155 // queues.
156 event =
157 mNestedQueues.LastElement().mQueue->GetEvent(lock, aLastEventDelay);
160 if (event) {
161 break;
164 // No runnable available. Sleep waiting for one if if we're supposed to.
165 // Otherwise just go ahead and return null.
166 if (!aMayWait) {
167 break;
170 AUTO_PROFILER_LABEL("ThreadEventQueue::GetEvent::Wait", IDLE);
171 mEventsAvailable.Wait();
175 return event.forget();
178 bool ThreadEventQueue::HasPendingEvent() {
179 MutexAutoLock lock(mLock);
181 // We always get events from the topmost queue when there are nested queues.
182 if (mNestedQueues.IsEmpty()) {
183 return mBaseQueue->HasReadyEvent(lock);
184 } else {
185 return mNestedQueues.LastElement().mQueue->HasReadyEvent(lock);
189 bool ThreadEventQueue::ShutdownIfNoPendingEvents() {
190 MutexAutoLock lock(mLock);
191 if (mNestedQueues.IsEmpty() && mBaseQueue->IsEmpty(lock)) {
192 mEventsAreDoomed = true;
193 return true;
195 return false;
198 already_AddRefed<nsISerialEventTarget> ThreadEventQueue::PushEventQueue() {
199 auto queue = MakeUnique<EventQueue>();
200 RefPtr<NestedSink> sink = new NestedSink(queue.get(), this);
201 RefPtr<ThreadEventTarget> eventTarget =
202 new ThreadEventTarget(sink, NS_IsMainThread());
204 MutexAutoLock lock(mLock);
206 mNestedQueues.AppendElement(NestedQueueItem(std::move(queue), eventTarget));
207 return eventTarget.forget();
210 void ThreadEventQueue::PopEventQueue(nsIEventTarget* aTarget) {
211 MutexAutoLock lock(mLock);
213 MOZ_ASSERT(!mNestedQueues.IsEmpty());
215 NestedQueueItem& item = mNestedQueues.LastElement();
217 MOZ_ASSERT(aTarget == item.mEventTarget);
219 // Disconnect the event target that will be popped.
220 item.mEventTarget->Disconnect(lock);
222 EventQueue* prevQueue =
223 mNestedQueues.Length() == 1
224 ? mBaseQueue.get()
225 : mNestedQueues[mNestedQueues.Length() - 2].mQueue.get();
227 // Move events from the old queue to the new one.
228 nsCOMPtr<nsIRunnable> event;
229 TimeDuration delay;
230 while ((event = item.mQueue->GetEvent(lock, &delay))) {
231 // preserve the event delay so far
232 prevQueue->PutEvent(event.forget(), EventQueuePriority::Normal, lock,
233 &delay);
236 mNestedQueues.RemoveLastElement();
239 size_t ThreadEventQueue::SizeOfExcludingThis(
240 mozilla::MallocSizeOf aMallocSizeOf) {
241 size_t n = 0;
243 n += mBaseQueue->SizeOfIncludingThis(aMallocSizeOf);
246 MutexAutoLock lock(mLock);
247 n += mNestedQueues.ShallowSizeOfExcludingThis(aMallocSizeOf);
248 for (auto& queue : mNestedQueues) {
249 n += queue.mEventTarget->SizeOfIncludingThis(aMallocSizeOf);
253 return SynchronizedEventQueue::SizeOfExcludingThis(aMallocSizeOf) + n;
256 already_AddRefed<nsIThreadObserver> ThreadEventQueue::GetObserver() {
257 MutexAutoLock lock(mLock);
258 return do_AddRef(mObserver);
261 already_AddRefed<nsIThreadObserver> ThreadEventQueue::GetObserverOnThread() {
262 // only written on this thread
263 return do_AddRef(mObserver);
266 void ThreadEventQueue::SetObserver(nsIThreadObserver* aObserver) {
267 // Always called from the thread - single writer
268 MutexAutoLock lock(mLock);
269 nsCOMPtr observer = aObserver;
270 mObserver.swap(observer);
271 if (NS_IsMainThread()) {
272 TaskController::Get()->SetThreadObserver(aObserver);
276 ThreadEventQueue::NestedQueueItem::NestedQueueItem(
277 UniquePtr<EventQueue> aQueue, ThreadEventTarget* aEventTarget)
278 : mQueue(std::move(aQueue)), mEventTarget(aEventTarget) {}