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 "Scheduler.h"
8 :
9 : #include "jsfriendapi.h"
10 : #include "LabeledEventQueue.h"
11 : #include "LeakRefPtr.h"
12 : #include "MainThreadQueue.h"
13 : #include "mozilla/CooperativeThreadPool.h"
14 : #include "mozilla/dom/ScriptSettings.h"
15 : #include "mozilla/ipc/BackgroundChild.h"
16 : #include "mozilla/SchedulerGroup.h"
17 : #include "nsCycleCollector.h"
18 : #include "nsIThread.h"
19 : #include "nsPrintfCString.h"
20 : #include "nsThread.h"
21 : #include "nsThreadManager.h"
22 : #include "PrioritizedEventQueue.h"
23 : #include "xpcpublic.h"
24 : #include "xpccomponents.h"
25 :
26 : // Windows silliness. winbase.h defines an empty no-argument Yield macro.
27 : #undef Yield
28 :
29 : using namespace mozilla;
30 :
31 : // Using the anonymous namespace here causes GCC to generate:
32 : // error: 'mozilla::SchedulerImpl' has a field 'mozilla::SchedulerImpl::mQueue' whose type uses the anonymous namespace
33 : namespace mozilla {
34 : namespace detail {
35 :
36 0 : class SchedulerEventQueue final : public SynchronizedEventQueue
37 : {
38 : public:
39 0 : explicit SchedulerEventQueue(UniquePtr<AbstractEventQueue> aQueue)
40 0 : : mLock("Scheduler")
41 : , mNonCooperativeCondVar(mLock, "SchedulerNonCoop")
42 0 : , mQueue(std::move(aQueue))
43 0 : , mScheduler(nullptr)
44 0 : {}
45 :
46 : bool PutEvent(already_AddRefed<nsIRunnable>&& aEvent,
47 : EventPriority aPriority) final;
48 :
49 0 : void Disconnect(const MutexAutoLock& aProofOfLock) final {}
50 :
51 : already_AddRefed<nsIRunnable> GetEvent(bool aMayWait,
52 : EventPriority* aPriority) final;
53 : bool HasPendingEvent() final;
54 : bool HasPendingEvent(const MutexAutoLock& aProofOfLock);
55 :
56 : bool ShutdownIfNoPendingEvents() final;
57 :
58 : already_AddRefed<nsIThreadObserver> GetObserver() final;
59 : already_AddRefed<nsIThreadObserver> GetObserverOnThread() final;
60 : void SetObserver(nsIThreadObserver* aObserver) final;
61 :
62 : void EnableInputEventPrioritization() final;
63 : void FlushInputEventPrioritization() final;
64 : void SuspendInputEventPrioritization() final;
65 : void ResumeInputEventPrioritization() final;
66 :
67 : bool UseCooperativeScheduling() const;
68 : void SetScheduler(SchedulerImpl* aScheduler);
69 :
70 0 : Mutex& MutexRef() { return mLock; }
71 :
72 : private:
73 : Mutex mLock;
74 : CondVar mNonCooperativeCondVar;
75 :
76 : // Using the actual type here would avoid a virtual dispatch. However, that
77 : // would prevent us from switching between EventQueue and LabeledEventQueue at
78 : // runtime.
79 : UniquePtr<AbstractEventQueue> mQueue;
80 :
81 : bool mEventsAreDoomed = false;
82 : SchedulerImpl* mScheduler;
83 : nsCOMPtr<nsIThreadObserver> mObserver;
84 : };
85 :
86 : } // namespace detail
87 : } // namespace mozilla
88 :
89 : using mozilla::detail::SchedulerEventQueue;
90 :
91 0 : class mozilla::SchedulerImpl
92 : {
93 : public:
94 : explicit SchedulerImpl(SchedulerEventQueue* aQueue);
95 :
96 : void Start();
97 : void Stop(already_AddRefed<nsIRunnable> aStoppedCallback);
98 : void Shutdown();
99 :
100 : void Dispatch(already_AddRefed<nsIRunnable> aEvent);
101 :
102 : void Yield();
103 :
104 : static void EnterNestedEventLoop(Scheduler::EventLoopActivation& aOuterActivation);
105 : static void ExitNestedEventLoop(Scheduler::EventLoopActivation& aOuterActivation);
106 :
107 : static void StartEvent(Scheduler::EventLoopActivation& aActivation);
108 : static void FinishEvent(Scheduler::EventLoopActivation& aActivation);
109 :
110 : void SetJSContext(size_t aIndex, JSContext* aCx)
111 : {
112 0 : mContexts[aIndex] = aCx;
113 : }
114 :
115 : static void YieldCallback(JSContext* aCx);
116 : static bool InterruptCallback(JSContext* aCx);
117 :
118 0 : CooperativeThreadPool* GetThreadPool() { return mThreadPool.get(); }
119 :
120 0 : static bool UnlabeledEventRunning() { return sUnlabeledEventRunning; }
121 0 : static bool AnyEventRunning() { return sNumThreadsRunning > 0; }
122 :
123 : void BlockThreadedExecution(nsIBlockThreadedExecutionCallback* aCallback);
124 : void UnblockThreadedExecution();
125 :
126 0 : CooperativeThreadPool::Resource* GetQueueResource() { return &mQueueResource; }
127 : bool UseCooperativeScheduling() const { return mQueue->UseCooperativeScheduling(); }
128 :
129 : // Preferences.
130 : static bool sPrefChaoticScheduling;
131 : static bool sPrefPreemption;
132 : static size_t sPrefThreadCount;
133 : static bool sPrefUseMultipleQueues;
134 :
135 : private:
136 : void Interrupt(JSContext* aCx);
137 : void YieldFromJS(JSContext* aCx);
138 :
139 : static void SwitcherThread(void* aData);
140 : void Switcher();
141 :
142 : size_t mNumThreads;
143 :
144 : // Protects mQueue as well as mThreadPool. The lock comes from the SchedulerEventQueue.
145 : Mutex& mLock;
146 : CondVar mShutdownCondVar;
147 :
148 : bool mShuttingDown;
149 :
150 : // Runnable to call when the scheduler has finished shutting down.
151 : nsTArray<nsCOMPtr<nsIRunnable>> mShutdownCallbacks;
152 :
153 : UniquePtr<CooperativeThreadPool> mThreadPool;
154 :
155 : RefPtr<SchedulerEventQueue> mQueue;
156 :
157 : class QueueResource : public CooperativeThreadPool::Resource
158 : {
159 : public:
160 : explicit QueueResource(SchedulerImpl* aScheduler)
161 0 : : mScheduler(aScheduler)
162 : {}
163 :
164 : bool IsAvailable(const MutexAutoLock& aProofOfLock) override;
165 :
166 : private:
167 : SchedulerImpl* mScheduler;
168 : };
169 : QueueResource mQueueResource;
170 :
171 : class SystemZoneResource : public CooperativeThreadPool::Resource
172 : {
173 : public:
174 : explicit SystemZoneResource(SchedulerImpl* aScheduler)
175 0 : : mScheduler(aScheduler) {}
176 :
177 : bool IsAvailable(const MutexAutoLock& aProofOfLock) override;
178 :
179 : private:
180 : SchedulerImpl* mScheduler;
181 : };
182 : SystemZoneResource mSystemZoneResource;
183 :
184 0 : class ThreadController : public CooperativeThreadPool::Controller
185 : {
186 : public:
187 0 : ThreadController(SchedulerImpl* aScheduler, SchedulerEventQueue* aQueue)
188 0 : : mScheduler(aScheduler)
189 0 : , mMainVirtual(GetCurrentVirtualThread())
190 0 : , mMainLoop(MessageLoop::current())
191 0 : , mMainQueue(aQueue)
192 0 : {}
193 :
194 : void OnStartThread(size_t aIndex, const nsACString& aName, void* aStackTop) override;
195 : void OnStopThread(size_t aIndex) override;
196 :
197 : void OnSuspendThread(size_t aIndex) override;
198 : void OnResumeThread(size_t aIndex) override;
199 :
200 : private:
201 : SchedulerImpl* mScheduler;
202 : PRThread* mMainVirtual;
203 : MessageLoop* mMainLoop;
204 : MessageLoop* mOldMainLoop;
205 : RefPtr<SynchronizedEventQueue> mMainQueue;
206 : };
207 : ThreadController mController;
208 :
209 : static size_t sNumThreadsRunning;
210 : static bool sUnlabeledEventRunning;
211 :
212 : // Number of times that BlockThreadedExecution has been called without
213 : // corresponding calls to UnblockThreadedExecution. If this is non-zero,
214 : // scheduling is disabled.
215 : size_t mNumSchedulerBlocks = 0;
216 :
217 : JSContext* mContexts[CooperativeThreadPool::kMaxThreads];
218 : };
219 :
220 : bool SchedulerImpl::sPrefChaoticScheduling = false;
221 : bool SchedulerImpl::sPrefPreemption = false;
222 : bool SchedulerImpl::sPrefUseMultipleQueues = false;
223 : size_t SchedulerImpl::sPrefThreadCount = 2;
224 :
225 : size_t SchedulerImpl::sNumThreadsRunning;
226 : bool SchedulerImpl::sUnlabeledEventRunning;
227 :
228 : bool
229 0 : SchedulerEventQueue::PutEvent(already_AddRefed<nsIRunnable>&& aEvent,
230 : EventPriority aPriority)
231 : {
232 : // We want to leak the reference when we fail to dispatch it, so that
233 : // we won't release the event in a wrong thread.
234 0 : LeakRefPtr<nsIRunnable> event(std::move(aEvent));
235 0 : nsCOMPtr<nsIThreadObserver> obs;
236 :
237 : {
238 0 : MutexAutoLock lock(mLock);
239 :
240 0 : if (mEventsAreDoomed) {
241 0 : return false;
242 : }
243 :
244 0 : mQueue->PutEvent(event.take(), aPriority, lock);
245 :
246 0 : if (mScheduler) {
247 0 : CooperativeThreadPool* pool = mScheduler->GetThreadPool();
248 0 : MOZ_ASSERT(pool);
249 0 : pool->RecheckBlockers(lock);
250 : } else {
251 0 : mNonCooperativeCondVar.Notify();
252 : }
253 :
254 : // Make sure to grab the observer before dropping the lock, otherwise the
255 : // event that we just placed into the queue could run and eventually delete
256 : // this nsThread before the calling thread is scheduled again. We would then
257 : // crash while trying to access a dead nsThread.
258 0 : obs = mObserver;
259 : }
260 :
261 0 : if (obs) {
262 0 : obs->OnDispatchedEvent();
263 : }
264 :
265 : return true;
266 : }
267 :
268 : already_AddRefed<nsIRunnable>
269 0 : SchedulerEventQueue::GetEvent(bool aMayWait,
270 : EventPriority* aPriority)
271 : {
272 0 : MutexAutoLock lock(mLock);
273 :
274 0 : if (SchedulerImpl::sPrefChaoticScheduling) {
275 0 : CooperativeThreadPool::Yield(nullptr, lock);
276 : }
277 :
278 0 : nsCOMPtr<nsIRunnable> event;
279 : for (;;) {
280 0 : event = mQueue->GetEvent(aPriority, lock);
281 :
282 0 : if (event || !aMayWait) {
283 : break;
284 : }
285 :
286 0 : if (mScheduler) {
287 0 : CooperativeThreadPool::Yield(mScheduler->GetQueueResource(), lock);
288 : } else {
289 0 : AUTO_PROFILER_LABEL("SchedulerEventQueue::GetEvent::Wait", IDLE);
290 0 : mNonCooperativeCondVar.Wait();
291 : }
292 : }
293 :
294 0 : return event.forget();
295 : }
296 :
297 : bool
298 0 : SchedulerEventQueue::HasPendingEvent()
299 : {
300 0 : MutexAutoLock lock(mLock);
301 0 : return HasPendingEvent(lock);
302 : }
303 :
304 : bool
305 0 : SchedulerEventQueue::HasPendingEvent(const MutexAutoLock& aProofOfLock)
306 : {
307 0 : return mQueue->HasReadyEvent(aProofOfLock);
308 : }
309 :
310 : bool
311 0 : SchedulerEventQueue::ShutdownIfNoPendingEvents()
312 : {
313 0 : MutexAutoLock lock(mLock);
314 :
315 0 : MOZ_ASSERT(!mScheduler);
316 :
317 0 : if (mQueue->IsEmpty(lock)) {
318 0 : mEventsAreDoomed = true;
319 0 : return true;
320 : }
321 : return false;
322 : }
323 :
324 : bool
325 0 : SchedulerEventQueue::UseCooperativeScheduling() const
326 : {
327 0 : MOZ_ASSERT(NS_IsMainThread());
328 0 : return !!mScheduler;
329 : }
330 :
331 : void
332 0 : SchedulerEventQueue::SetScheduler(SchedulerImpl* aScheduler)
333 : {
334 0 : MutexAutoLock lock(mLock);
335 0 : mScheduler = aScheduler;
336 0 : }
337 :
338 : already_AddRefed<nsIThreadObserver>
339 0 : SchedulerEventQueue::GetObserver()
340 : {
341 0 : MutexAutoLock lock(mLock);
342 0 : return do_AddRef(mObserver);
343 : }
344 :
345 : already_AddRefed<nsIThreadObserver>
346 0 : SchedulerEventQueue::GetObserverOnThread()
347 : {
348 0 : MOZ_ASSERT(NS_IsMainThread());
349 0 : return do_AddRef(mObserver);
350 : }
351 :
352 : void
353 0 : SchedulerEventQueue::SetObserver(nsIThreadObserver* aObserver)
354 : {
355 0 : MutexAutoLock lock(mLock);
356 0 : mObserver = aObserver;
357 0 : }
358 :
359 : void
360 0 : SchedulerEventQueue::EnableInputEventPrioritization()
361 : {
362 0 : MutexAutoLock lock(mLock);
363 0 : mQueue->EnableInputEventPrioritization(lock);
364 0 : }
365 :
366 : void
367 0 : SchedulerEventQueue::FlushInputEventPrioritization()
368 : {
369 0 : MutexAutoLock lock(mLock);
370 0 : mQueue->FlushInputEventPrioritization(lock);
371 0 : }
372 :
373 : void
374 0 : SchedulerEventQueue::SuspendInputEventPrioritization()
375 : {
376 0 : MutexAutoLock lock(mLock);
377 0 : mQueue->SuspendInputEventPrioritization(lock);
378 0 : }
379 :
380 : void
381 0 : SchedulerEventQueue::ResumeInputEventPrioritization()
382 : {
383 0 : MutexAutoLock lock(mLock);
384 0 : mQueue->ResumeInputEventPrioritization(lock);
385 0 : }
386 :
387 1 : UniquePtr<SchedulerImpl> Scheduler::sScheduler;
388 :
389 0 : SchedulerImpl::SchedulerImpl(SchedulerEventQueue* aQueue)
390 : : mNumThreads(sPrefThreadCount)
391 0 : , mLock(aQueue->MutexRef())
392 : , mShutdownCondVar(aQueue->MutexRef(), "SchedulerImpl")
393 : , mShuttingDown(false)
394 : , mQueue(aQueue)
395 : , mQueueResource(this)
396 : , mSystemZoneResource(this)
397 : , mController(this, aQueue)
398 0 : , mContexts()
399 : {
400 0 : }
401 :
402 : void
403 0 : SchedulerImpl::Interrupt(JSContext* aCx)
404 : {
405 0 : MutexAutoLock lock(mLock);
406 0 : CooperativeThreadPool::Yield(nullptr, lock);
407 0 : }
408 :
409 : /* static */ bool
410 0 : SchedulerImpl::InterruptCallback(JSContext* aCx)
411 : {
412 0 : Scheduler::sScheduler->Interrupt(aCx);
413 0 : return true;
414 : }
415 :
416 : void
417 0 : SchedulerImpl::YieldFromJS(JSContext* aCx)
418 : {
419 0 : MutexAutoLock lock(mLock);
420 0 : CooperativeThreadPool::Yield(&mSystemZoneResource, lock);
421 0 : }
422 :
423 : /* static */ void
424 0 : SchedulerImpl::YieldCallback(JSContext* aCx)
425 : {
426 0 : Scheduler::sScheduler->YieldFromJS(aCx);
427 0 : }
428 :
429 : void
430 0 : SchedulerImpl::Switcher()
431 : {
432 : // This thread switcher is extremely basic and only meant for testing. The
433 : // goal is to switch as much as possible without regard for performance.
434 :
435 0 : MutexAutoLock lock(mLock);
436 0 : while (!mShuttingDown) {
437 0 : CooperativeThreadPool::SelectedThread threadIndex = mThreadPool->CurrentThreadIndex(lock);
438 0 : if (threadIndex.is<size_t>()) {
439 0 : JSContext* cx = mContexts[threadIndex.as<size_t>()];
440 0 : if (cx) {
441 0 : JS_RequestInterruptCallbackCanWait(cx);
442 : }
443 : }
444 :
445 0 : mShutdownCondVar.Wait(TimeDuration::FromMicroseconds(50));
446 : }
447 0 : }
448 :
449 : /* static */ void
450 0 : SchedulerImpl::SwitcherThread(void* aData)
451 : {
452 0 : static_cast<SchedulerImpl*>(aData)->Switcher();
453 0 : }
454 :
455 : void
456 0 : SchedulerImpl::Start()
457 : {
458 0 : MOZ_ASSERT(mNumSchedulerBlocks == 0);
459 :
460 0 : NS_DispatchToMainThread(NS_NewRunnableFunction("Scheduler::Start", [this]() -> void {
461 : // Let's pretend the runnable here isn't actually running.
462 0 : MOZ_ASSERT(sUnlabeledEventRunning);
463 0 : sUnlabeledEventRunning = false;
464 0 : MOZ_ASSERT(sNumThreadsRunning == 1);
465 0 : sNumThreadsRunning = 0;
466 :
467 0 : mQueue->SetScheduler(this);
468 :
469 0 : xpc::YieldCooperativeContext();
470 :
471 0 : mThreadPool = MakeUnique<CooperativeThreadPool>(mNumThreads, mLock,
472 0 : mController);
473 :
474 0 : PRThread* switcher = nullptr;
475 0 : if (sPrefPreemption) {
476 : switcher = PR_CreateThread(PR_USER_THREAD,
477 : SwitcherThread,
478 : this,
479 : PR_PRIORITY_HIGH,
480 : PR_GLOBAL_THREAD,
481 : PR_JOINABLE_THREAD,
482 0 : 0);
483 : }
484 :
485 : {
486 0 : MutexAutoLock mutex(mLock);
487 0 : while (!mShuttingDown) {
488 0 : mShutdownCondVar.Wait();
489 : }
490 : }
491 :
492 0 : if (switcher) {
493 0 : PR_JoinThread(switcher);
494 : }
495 :
496 0 : mThreadPool->Shutdown();
497 0 : mThreadPool = nullptr;
498 :
499 0 : mQueue->SetScheduler(nullptr);
500 :
501 0 : xpc::ResumeCooperativeContext();
502 :
503 : // Put things back to the way they were before we started scheduling.
504 0 : MOZ_ASSERT(!sUnlabeledEventRunning);
505 0 : sUnlabeledEventRunning = true;
506 0 : MOZ_ASSERT(sNumThreadsRunning == 0);
507 0 : sNumThreadsRunning = 1;
508 :
509 0 : mShuttingDown = false;
510 0 : nsTArray<nsCOMPtr<nsIRunnable>> callbacks = std::move(mShutdownCallbacks);
511 0 : for (nsIRunnable* runnable : callbacks) {
512 0 : runnable->Run();
513 : }
514 0 : }));
515 0 : }
516 :
517 : void
518 0 : SchedulerImpl::Stop(already_AddRefed<nsIRunnable> aStoppedCallback)
519 : {
520 0 : MOZ_ASSERT(mNumSchedulerBlocks > 0);
521 :
522 : // Note that this may be called when mShuttingDown is already true. We still
523 : // want to invoke the callback in that case.
524 :
525 0 : MutexAutoLock lock(mLock);
526 0 : mShuttingDown = true;
527 0 : mShutdownCallbacks.AppendElement(aStoppedCallback);
528 0 : mShutdownCondVar.Notify();
529 0 : }
530 :
531 : void
532 0 : SchedulerImpl::Shutdown()
533 : {
534 0 : MOZ_ASSERT(mNumSchedulerBlocks == 0);
535 :
536 0 : MutexAutoLock lock(mLock);
537 0 : mShuttingDown = true;
538 :
539 : // Delete the SchedulerImpl once shutdown is complete.
540 0 : mShutdownCallbacks.AppendElement(NS_NewRunnableFunction("SchedulerImpl::Shutdown",
541 0 : [] { Scheduler::sScheduler = nullptr; }));
542 :
543 0 : mShutdownCondVar.Notify();
544 0 : }
545 :
546 : bool
547 0 : SchedulerImpl::QueueResource::IsAvailable(const MutexAutoLock& aProofOfLock)
548 : {
549 0 : mScheduler->mLock.AssertCurrentThreadOwns();
550 :
551 0 : RefPtr<SchedulerEventQueue> queue = mScheduler->mQueue;
552 0 : return queue->HasPendingEvent(aProofOfLock);
553 : }
554 :
555 : bool
556 0 : SchedulerImpl::SystemZoneResource::IsAvailable(const MutexAutoLock& aProofOfLock)
557 : {
558 0 : mScheduler->mLock.AssertCurrentThreadOwns();
559 :
560 : // It doesn't matter which context we pick; we really just some main-thread
561 : // JSContext.
562 0 : JSContext* cx = mScheduler->mContexts[0];
563 0 : return js::SystemZoneAvailable(cx);
564 : }
565 :
566 : MOZ_THREAD_LOCAL(Scheduler::EventLoopActivation*) Scheduler::EventLoopActivation::sTopActivation;
567 :
568 : /* static */ void
569 1 : Scheduler::EventLoopActivation::Init()
570 : {
571 0 : sTopActivation.infallibleInit();
572 1 : }
573 :
574 0 : Scheduler::EventLoopActivation::EventLoopActivation()
575 1626 : : mPrev(sTopActivation.get())
576 : , mProcessingEvent(false)
577 3252 : , mIsLabeled(false)
578 : {
579 1626 : sTopActivation.set(this);
580 :
581 0 : if (mPrev && mPrev->mProcessingEvent) {
582 51 : SchedulerImpl::EnterNestedEventLoop(*mPrev);
583 : }
584 1626 : }
585 :
586 3250 : Scheduler::EventLoopActivation::~EventLoopActivation()
587 : {
588 0 : if (mProcessingEvent) {
589 1624 : SchedulerImpl::FinishEvent(*this);
590 : }
591 :
592 0 : MOZ_ASSERT(sTopActivation.get() == this);
593 3250 : sTopActivation.set(mPrev);
594 :
595 0 : if (mPrev && mPrev->mProcessingEvent) {
596 51 : SchedulerImpl::ExitNestedEventLoop(*mPrev);
597 : }
598 1625 : }
599 :
600 : /* static */ void
601 1676 : SchedulerImpl::StartEvent(Scheduler::EventLoopActivation& aActivation)
602 : {
603 0 : MOZ_ASSERT(!sUnlabeledEventRunning);
604 0 : if (aActivation.IsLabeled()) {
605 0 : SchedulerGroup::SetValidatingAccess(SchedulerGroup::StartValidation);
606 0 : aActivation.EventGroupsAffected().SetIsRunning(true);
607 : } else {
608 1676 : sUnlabeledEventRunning = true;
609 : }
610 0 : sNumThreadsRunning++;
611 1676 : }
612 :
613 : /* static */ void
614 1675 : SchedulerImpl::FinishEvent(Scheduler::EventLoopActivation& aActivation)
615 : {
616 0 : if (aActivation.IsLabeled()) {
617 0 : aActivation.EventGroupsAffected().SetIsRunning(false);
618 0 : SchedulerGroup::SetValidatingAccess(SchedulerGroup::EndValidation);
619 : } else {
620 0 : MOZ_ASSERT(sUnlabeledEventRunning);
621 1675 : sUnlabeledEventRunning = false;
622 : }
623 :
624 0 : MOZ_ASSERT(sNumThreadsRunning > 0);
625 0 : sNumThreadsRunning--;
626 1675 : }
627 :
628 : // When we enter a nested event loop, we act as if the outer event loop's event
629 : // finished. When we exit the nested event loop, we "resume" the outer event
630 : // loop's event.
631 : /* static */ void
632 0 : SchedulerImpl::EnterNestedEventLoop(Scheduler::EventLoopActivation& aOuterActivation)
633 : {
634 1 : FinishEvent(aOuterActivation);
635 0 : }
636 :
637 : /* static */ void
638 0 : SchedulerImpl::ExitNestedEventLoop(Scheduler::EventLoopActivation& aOuterActivation)
639 : {
640 1 : StartEvent(aOuterActivation);
641 0 : }
642 :
643 : void
644 1626 : Scheduler::EventLoopActivation::SetEvent(nsIRunnable* aEvent,
645 : EventPriority aPriority)
646 : {
647 0 : if (nsCOMPtr<nsILabelableRunnable> labelable = do_QueryInterface(aEvent)) {
648 0 : if (labelable->GetAffectedSchedulerGroups(mEventGroups)) {
649 0 : mIsLabeled = true;
650 : }
651 : }
652 :
653 0 : mPriority = aPriority;
654 1626 : mProcessingEvent = aEvent != nullptr;
655 :
656 0 : if (aEvent) {
657 1625 : SchedulerImpl::StartEvent(*this);
658 : }
659 1626 : }
660 :
661 : void
662 0 : SchedulerImpl::ThreadController::OnStartThread(size_t aIndex, const nsACString& aName, void* aStackTop)
663 : {
664 : using mozilla::ipc::BackgroundChild;
665 :
666 : // Causes GetCurrentVirtualThread() to return mMainVirtual and NS_IsMainThread()
667 : // to return true.
668 0 : NS_SetMainThread(mMainVirtual);
669 :
670 : // This will initialize the thread's mVirtualThread to mMainVirtual since
671 : // GetCurrentVirtualThread() now returns mMainVirtual.
672 0 : nsThreadManager::get().CreateCurrentThread(mMainQueue, nsThread::MAIN_THREAD);
673 :
674 0 : PROFILER_REGISTER_THREAD(aName.BeginReading());
675 :
676 0 : mOldMainLoop = MessageLoop::current();
677 :
678 0 : MessageLoop::set_current(mMainLoop);
679 :
680 0 : xpc::CreateCooperativeContext();
681 :
682 0 : JSContext* cx = dom::danger::GetJSContext();
683 0 : mScheduler->SetJSContext(aIndex, cx);
684 0 : if (sPrefPreemption) {
685 0 : JS_AddInterruptCallback(cx, SchedulerImpl::InterruptCallback);
686 : }
687 0 : Servo_InitializeCooperativeThread();
688 0 : }
689 :
690 : void
691 0 : SchedulerImpl::ThreadController::OnStopThread(size_t aIndex)
692 : {
693 0 : xpc::DestroyCooperativeContext();
694 :
695 0 : NS_UnsetMainThread();
696 0 : MessageLoop::set_current(mOldMainLoop);
697 :
698 0 : RefPtr<nsThread> self = static_cast<nsThread*>(NS_GetCurrentThread());
699 0 : nsThreadManager::get().UnregisterCurrentThread(*self);
700 :
701 0 : PROFILER_UNREGISTER_THREAD();
702 0 : }
703 :
704 : void
705 0 : SchedulerImpl::ThreadController::OnSuspendThread(size_t aIndex)
706 : {
707 0 : xpc::YieldCooperativeContext();
708 0 : }
709 :
710 : void
711 0 : SchedulerImpl::ThreadController::OnResumeThread(size_t aIndex)
712 : {
713 0 : xpc::ResumeCooperativeContext();
714 0 : }
715 :
716 : void
717 0 : SchedulerImpl::Yield()
718 : {
719 0 : MutexAutoLock lock(mLock);
720 0 : CooperativeThreadPool::Yield(nullptr, lock);
721 0 : }
722 :
723 : void
724 0 : SchedulerImpl::BlockThreadedExecution(nsIBlockThreadedExecutionCallback* aCallback)
725 : {
726 0 : if (mNumSchedulerBlocks++ == 0 || mShuttingDown) {
727 0 : Stop(NewRunnableMethod("BlockThreadedExecution", aCallback,
728 0 : &nsIBlockThreadedExecutionCallback::Callback));
729 : } else {
730 : // The scheduler is already blocked.
731 0 : nsCOMPtr<nsIBlockThreadedExecutionCallback> kungFuDeathGrip(aCallback);
732 0 : aCallback->Callback();
733 : }
734 0 : }
735 :
736 : void
737 0 : SchedulerImpl::UnblockThreadedExecution()
738 : {
739 0 : if (--mNumSchedulerBlocks == 0) {
740 0 : Start();
741 : }
742 0 : }
743 :
744 : /* static */ already_AddRefed<nsThread>
745 0 : Scheduler::Init(nsIIdlePeriod* aIdlePeriod)
746 : {
747 0 : MOZ_ASSERT(!sScheduler);
748 :
749 0 : RefPtr<SchedulerEventQueue> queue;
750 0 : RefPtr<nsThread> mainThread;
751 0 : if (Scheduler::UseMultipleQueues()) {
752 0 : mainThread = CreateMainThread<SchedulerEventQueue, LabeledEventQueue>(aIdlePeriod, getter_AddRefs(queue));
753 : } else {
754 0 : mainThread = CreateMainThread<SchedulerEventQueue, EventQueue>(aIdlePeriod, getter_AddRefs(queue));
755 : }
756 :
757 0 : sScheduler = MakeUnique<SchedulerImpl>(queue);
758 0 : return mainThread.forget();
759 : }
760 :
761 : /* static */ void
762 0 : Scheduler::Start()
763 : {
764 0 : sScheduler->Start();
765 0 : }
766 :
767 : /* static */ void
768 0 : Scheduler::Shutdown()
769 : {
770 0 : if (sScheduler) {
771 0 : sScheduler->Shutdown();
772 : }
773 0 : }
774 :
775 : /* static */ nsCString
776 2 : Scheduler::GetPrefs()
777 : {
778 2 : MOZ_ASSERT(XRE_IsParentProcess());
779 : nsPrintfCString result("%d%d%d%d,%d",
780 : false, // XXX The scheduler is always disabled.
781 2 : Preferences::GetBool("dom.ipc.scheduler.chaoticScheduling",
782 : SchedulerImpl::sPrefChaoticScheduling),
783 2 : Preferences::GetBool("dom.ipc.scheduler.preemption",
784 : SchedulerImpl::sPrefPreemption),
785 2 : Preferences::GetBool("dom.ipc.scheduler.useMultipleQueues",
786 : SchedulerImpl::sPrefUseMultipleQueues),
787 : Preferences::GetInt("dom.ipc.scheduler.threadCount",
788 10 : SchedulerImpl::sPrefThreadCount));
789 :
790 4 : return result;
791 : }
792 :
793 : /* static */ void
794 0 : Scheduler::SetPrefs(const char* aPrefs)
795 : {
796 0 : MOZ_ASSERT(XRE_IsContentProcess());
797 :
798 : // If the prefs weren't sent to this process, use the default values.
799 0 : if (!aPrefs) {
800 : return;
801 : }
802 :
803 : // If the pref string appears truncated, use the default values.
804 0 : if (strlen(aPrefs) < 6) {
805 : return;
806 : }
807 :
808 0 : SchedulerImpl::sPrefChaoticScheduling = aPrefs[1] == '1';
809 0 : SchedulerImpl::sPrefPreemption = aPrefs[2] == '1';
810 0 : SchedulerImpl::sPrefUseMultipleQueues = aPrefs[3] == '1';
811 0 : MOZ_ASSERT(aPrefs[4] == ',');
812 0 : SchedulerImpl::sPrefThreadCount = atoi(aPrefs + 5);
813 : }
814 :
815 : /* static */ bool
816 0 : Scheduler::IsSchedulerEnabled()
817 : {
818 : // XXX We never enable the scheduler because it will crash immediately.
819 0 : return false;
820 : }
821 :
822 : /* static */ bool
823 0 : Scheduler::UseMultipleQueues()
824 : {
825 0 : return SchedulerImpl::sPrefUseMultipleQueues;
826 : }
827 :
828 : /* static */ bool
829 0 : Scheduler::IsCooperativeThread()
830 : {
831 0 : return CooperativeThreadPool::IsCooperativeThread();
832 : }
833 :
834 : /* static */ void
835 0 : Scheduler::Yield()
836 : {
837 0 : sScheduler->Yield();
838 0 : }
839 :
840 : /* static */ bool
841 0 : Scheduler::UnlabeledEventRunning()
842 : {
843 0 : return SchedulerImpl::UnlabeledEventRunning();
844 : }
845 :
846 : /* static */ bool
847 0 : Scheduler::AnyEventRunning()
848 : {
849 0 : return SchedulerImpl::AnyEventRunning();
850 : }
851 :
852 : /* static */ void
853 0 : Scheduler::BlockThreadedExecution(nsIBlockThreadedExecutionCallback* aCallback)
854 : {
855 0 : if (!sScheduler) {
856 0 : nsCOMPtr<nsIBlockThreadedExecutionCallback> kungFuDeathGrip(aCallback);
857 0 : aCallback->Callback();
858 : return;
859 : }
860 :
861 0 : sScheduler->BlockThreadedExecution(aCallback);
862 : }
863 :
864 : /* static */ void
865 0 : Scheduler::UnblockThreadedExecution()
866 : {
867 0 : if (!sScheduler) {
868 : return;
869 : }
870 :
871 0 : sScheduler->UnblockThreadedExecution();
872 : }
|