Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/xpcom/threads/nsThreadManager.cpp
Line
Count
Source (jump to first uncovered line)
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 "nsThreadManager.h"
8
#include "nsThread.h"
9
#include "nsThreadUtils.h"
10
#include "nsIClassInfoImpl.h"
11
#include "nsTArray.h"
12
#include "nsAutoPtr.h"
13
#include "nsXULAppAPI.h"
14
#include "LabeledEventQueue.h"
15
#include "MainThreadQueue.h"
16
#include "mozilla/AbstractThread.h"
17
#include "mozilla/ClearOnShutdown.h"
18
#include "mozilla/EventQueue.h"
19
#include "mozilla/Preferences.h"
20
#include "mozilla/Scheduler.h"
21
#include "mozilla/SystemGroup.h"
22
#include "mozilla/StaticPtr.h"
23
#include "mozilla/ThreadEventQueue.h"
24
#include "mozilla/ThreadLocal.h"
25
#include "PrioritizedEventQueue.h"
26
#ifdef MOZ_CANARY
27
#include <fcntl.h>
28
#include <unistd.h>
29
#endif
30
31
#include "MainThreadIdlePeriod.h"
32
#include "InputEventStatistics.h"
33
34
using namespace mozilla;
35
36
static MOZ_THREAD_LOCAL(bool) sTLSIsMainThread;
37
static MOZ_THREAD_LOCAL(PRThread*) gTlsCurrentVirtualThread;
38
39
bool
40
NS_IsMainThreadTLSInitialized()
41
0
{
42
0
  return sTLSIsMainThread.initialized();
43
0
}
44
45
bool
46
NS_IsMainThread()
47
50.8M
{
48
50.8M
  return sTLSIsMainThread.get();
49
50.8M
}
50
51
void
52
NS_SetMainThread()
53
9
{
54
9
  if (!sTLSIsMainThread.init()) {
55
0
    MOZ_CRASH();
56
0
  }
57
9
  sTLSIsMainThread.set(true);
58
9
  MOZ_ASSERT(NS_IsMainThread());
59
9
}
60
61
void
62
NS_SetMainThread(PRThread* aVirtualThread)
63
0
{
64
0
  MOZ_ASSERT(Scheduler::IsCooperativeThread());
65
0
66
0
  MOZ_ASSERT(!gTlsCurrentVirtualThread.get());
67
0
  gTlsCurrentVirtualThread.set(aVirtualThread);
68
0
  NS_SetMainThread();
69
0
}
70
71
void
72
NS_UnsetMainThread()
73
0
{
74
0
  MOZ_ASSERT(Scheduler::IsCooperativeThread());
75
0
76
0
  sTLSIsMainThread.set(false);
77
0
  MOZ_ASSERT(!NS_IsMainThread());
78
0
  gTlsCurrentVirtualThread.set(nullptr);
79
0
}
80
81
#ifdef DEBUG
82
83
namespace mozilla {
84
85
void
86
AssertIsOnMainThread()
87
{
88
  MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
89
}
90
91
} // mozilla namespace
92
93
#endif
94
95
typedef nsTArray<NotNull<RefPtr<nsThread>>> nsThreadArray;
96
97
static bool sShutdownComplete;
98
99
//-----------------------------------------------------------------------------
100
101
/* static */ void
102
nsThreadManager::ReleaseThread(void* aData)
103
0
{
104
0
  if (sShutdownComplete) {
105
0
    // We've already completed shutdown and released the references to all or
106
0
    // our TLS wrappers. Don't try to release them again.
107
0
    return;
108
0
  }
109
0
110
0
  auto* thread = static_cast<nsThread*>(aData);
111
0
112
0
  get().UnregisterCurrentThread(*thread, true);
113
0
114
0
  if (thread->mHasTLSEntry) {
115
0
    thread->mHasTLSEntry = false;
116
0
    thread->Release();
117
0
  }
118
0
}
119
120
// statically allocated instance
121
NS_IMETHODIMP_(MozExternalRefCountType)
122
nsThreadManager::AddRef()
123
0
{
124
0
  return 2;
125
0
}
126
NS_IMETHODIMP_(MozExternalRefCountType)
127
nsThreadManager::Release()
128
0
{
129
0
  return 1;
130
0
}
131
NS_IMPL_CLASSINFO(nsThreadManager, nullptr,
132
                  nsIClassInfo::THREADSAFE | nsIClassInfo::SINGLETON,
133
                  NS_THREADMANAGER_CID)
134
NS_IMPL_QUERY_INTERFACE_CI(nsThreadManager, nsIThreadManager)
135
NS_IMPL_CI_INTERFACE_GETTER(nsThreadManager, nsIThreadManager)
136
137
namespace {
138
139
// Simple observer to monitor the beginning of the shutdown.
140
class ShutdownObserveHelper final : public nsIObserver
141
                                  , public nsSupportsWeakReference
142
{
143
public:
144
  NS_DECL_ISUPPORTS
145
146
  static nsresult
147
  Create(ShutdownObserveHelper** aObserver)
148
3
  {
149
3
    MOZ_ASSERT(aObserver);
150
3
151
3
    RefPtr<ShutdownObserveHelper> observer = new ShutdownObserveHelper();
152
3
153
3
    nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
154
3
    if (NS_WARN_IF(!obs)) {
155
0
      return NS_ERROR_FAILURE;
156
0
    }
157
3
158
3
    nsresult rv = obs->AddObserver(observer, NS_XPCOM_SHUTDOWN_OBSERVER_ID, true);
159
3
    if (NS_WARN_IF(NS_FAILED(rv))) {
160
0
      return rv;
161
0
    }
162
3
163
3
    rv = obs->AddObserver(observer, "content-child-will-shutdown", true);
164
3
    if (NS_WARN_IF(NS_FAILED(rv))) {
165
0
      return rv;
166
0
    }
167
3
168
3
    observer.forget(aObserver);
169
3
    return NS_OK;
170
3
  }
171
172
  NS_IMETHOD
173
  Observe(nsISupports* aSubject, const char* aTopic,
174
          const char16_t* aData) override
175
0
  {
176
0
    if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) ||
177
0
        !strcmp(aTopic, "content-child-will-shutdown")) {
178
0
      mShuttingDown = true;
179
0
      return NS_OK;
180
0
    }
181
0
182
0
    return NS_OK;
183
0
  }
184
185
  bool
186
  ShuttingDown() const
187
0
  {
188
0
    return mShuttingDown;
189
0
  }
190
191
private:
192
  explicit ShutdownObserveHelper()
193
    : mShuttingDown(false)
194
3
  {}
195
196
0
  ~ShutdownObserveHelper() = default;
197
198
  bool mShuttingDown;
199
};
200
201
6
NS_INTERFACE_MAP_BEGIN(ShutdownObserveHelper)
202
6
  NS_INTERFACE_MAP_ENTRY(nsIObserver)
203
6
  NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
204
6
  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIObserver)
205
0
NS_INTERFACE_MAP_END
206
207
NS_IMPL_ADDREF(ShutdownObserveHelper)
208
NS_IMPL_RELEASE(ShutdownObserveHelper)
209
210
StaticRefPtr<ShutdownObserveHelper> gShutdownObserveHelper;
211
212
} // anonymous
213
214
//-----------------------------------------------------------------------------
215
216
/*static*/ nsThreadManager&
217
nsThreadManager::get()
218
466
{
219
466
  static nsThreadManager sInstance;
220
466
  return sInstance;
221
466
}
222
223
/* static */ void
224
nsThreadManager::InitializeShutdownObserver()
225
3
{
226
3
  MOZ_ASSERT(!gShutdownObserveHelper);
227
3
228
3
  RefPtr<ShutdownObserveHelper> observer;
229
3
  nsresult rv = ShutdownObserveHelper::Create(getter_AddRefs(observer));
230
3
  if (NS_WARN_IF(NS_FAILED(rv))) {
231
0
    return;
232
0
  }
233
3
234
3
  gShutdownObserveHelper = observer;
235
3
  ClearOnShutdown(&gShutdownObserveHelper);
236
3
}
237
238
nsresult
239
nsThreadManager::Init()
240
3
{
241
3
  // Child processes need to initialize the thread manager before they
242
3
  // initialize XPCOM in order to set up the crash reporter. This leads to
243
3
  // situations where we get initialized twice.
244
3
  if (mInitialized) {
245
0
    return NS_OK;
246
0
  }
247
3
248
3
  if (!gTlsCurrentVirtualThread.init()) {
249
0
    return NS_ERROR_UNEXPECTED;
250
0
  }
251
3
252
3
  Scheduler::EventLoopActivation::Init();
253
3
254
3
  if (PR_NewThreadPrivateIndex(&mCurThreadIndex, ReleaseThread) == PR_FAILURE) {
255
0
    return NS_ERROR_FAILURE;
256
0
  }
257
3
258
3
259
#ifdef MOZ_CANARY
260
  const int flags = O_WRONLY | O_APPEND | O_CREAT | O_NONBLOCK;
261
  const mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
262
  char* env_var_flag = getenv("MOZ_KILL_CANARIES");
263
  sCanaryOutputFD =
264
    env_var_flag ? (env_var_flag[0] ? open(env_var_flag, flags, mode) :
265
                                      STDERR_FILENO) :
266
                   0;
267
#endif
268
269
3
  nsCOMPtr<nsIIdlePeriod> idlePeriod = new MainThreadIdlePeriod();
270
3
271
3
  bool startScheduler = false;
272
3
  if (XRE_IsContentProcess() && Scheduler::IsSchedulerEnabled()) {
273
0
    mMainThread = Scheduler::Init(idlePeriod);
274
0
    startScheduler = true;
275
3
  } else {
276
3
    if (XRE_IsContentProcess() && Scheduler::UseMultipleQueues()) {
277
0
      mMainThread = CreateMainThread<ThreadEventQueue<PrioritizedEventQueue<LabeledEventQueue>>, LabeledEventQueue>(idlePeriod);
278
3
    } else {
279
3
      mMainThread = CreateMainThread<ThreadEventQueue<PrioritizedEventQueue<EventQueue>>, EventQueue>(idlePeriod);
280
3
    }
281
3
  }
282
3
283
3
  nsresult rv = mMainThread->InitCurrentThread();
284
3
  if (NS_FAILED(rv)) {
285
0
    mMainThread = nullptr;
286
0
    return rv;
287
0
  }
288
3
289
3
  // We need to keep a pointer to the current thread, so we can satisfy
290
3
  // GetIsMainThread calls that occur post-Shutdown.
291
3
  mMainThread->GetPRThread(&mMainPRThread);
292
3
293
3
  // Init AbstractThread.
294
3
  AbstractThread::InitTLS();
295
3
  AbstractThread::InitMainThread();
296
3
297
3
  mInitialized = true;
298
3
299
3
  if (startScheduler) {
300
0
    Scheduler::Start();
301
0
  }
302
3
  return NS_OK;
303
3
}
304
305
void
306
nsThreadManager::Shutdown()
307
0
{
308
0
  MOZ_ASSERT(NS_IsMainThread(), "shutdown not called from main thread");
309
0
310
0
  // Prevent further access to the thread manager (no more new threads!)
311
0
  //
312
0
  // What happens if shutdown happens before NewThread completes?
313
0
  // We Shutdown() the new thread, and return error if we've started Shutdown
314
0
  // between when NewThread started, and when the thread finished initializing
315
0
  // and registering with ThreadManager.
316
0
  //
317
0
  mInitialized = false;
318
0
319
0
  // Empty the main thread event queue before we begin shutting down threads.
320
0
  NS_ProcessPendingEvents(mMainThread);
321
0
322
0
  {
323
0
    // We gather the threads from the hashtable into a list, so that we avoid
324
0
    // holding the hashtable lock while calling nsIThread::Shutdown.
325
0
    nsThreadArray threads;
326
0
    {
327
0
      OffTheBooksMutexAutoLock lock(mLock);
328
0
      for (auto iter = mThreadsByPRThread.Iter(); !iter.Done(); iter.Next()) {
329
0
        RefPtr<nsThread>& thread = iter.Data();
330
0
        threads.AppendElement(WrapNotNull(thread));
331
0
        iter.Remove();
332
0
      }
333
0
    }
334
0
335
0
    // It's tempting to walk the list of threads here and tell them each to stop
336
0
    // accepting new events, but that could lead to badness if one of those
337
0
    // threads is stuck waiting for a response from another thread.  To do it
338
0
    // right, we'd need some way to interrupt the threads.
339
0
    //
340
0
    // Instead, we process events on the current thread while waiting for threads
341
0
    // to shutdown.  This means that we have to preserve a mostly functioning
342
0
    // world until such time as the threads exit.
343
0
344
0
    // Shutdown all threads that require it (join with threads that we created).
345
0
    for (uint32_t i = 0; i < threads.Length(); ++i) {
346
0
      NotNull<nsThread*> thread = threads[i];
347
0
      if (thread->ShutdownRequired()) {
348
0
        thread->Shutdown();
349
0
      }
350
0
    }
351
0
  }
352
0
353
0
  // NB: It's possible that there are events in the queue that want to *start*
354
0
  // an asynchronous shutdown. But we have already shutdown the threads above,
355
0
  // so there's no need to worry about them. We only have to wait for all
356
0
  // in-flight asynchronous thread shutdowns to complete.
357
0
  mMainThread->WaitForAllAsynchronousShutdowns();
358
0
359
0
  // In case there are any more events somehow...
360
0
  NS_ProcessPendingEvents(mMainThread);
361
0
362
0
  // There are no more background threads at this point.
363
0
364
0
  // Clear the table of threads.
365
0
  {
366
0
    OffTheBooksMutexAutoLock lock(mLock);
367
0
    mThreadsByPRThread.Clear();
368
0
  }
369
0
370
0
  // Normally thread shutdown clears the observer for the thread, but since the
371
0
  // main thread is special we do it manually here after we're sure all events
372
0
  // have been processed.
373
0
  mMainThread->SetObserver(nullptr);
374
0
375
0
  // Release main thread object.
376
0
  mMainThread = nullptr;
377
0
378
0
  // Remove the TLS entry for the main thread.
379
0
  PR_SetThreadPrivate(mCurThreadIndex, nullptr);
380
0
381
0
  {
382
0
    // Cleanup the last references to any threads which haven't shut down yet.
383
0
    nsTArray<RefPtr<nsThread>> threads;
384
0
    for (auto* thread : nsThread::Enumerate()) {
385
0
      if (thread->mHasTLSEntry) {
386
0
        threads.AppendElement(dont_AddRef(thread));
387
0
        thread->mHasTLSEntry = false;
388
0
      }
389
0
    }
390
0
  }
391
0
392
0
  // xpcshell tests sometimes leak the main thread. They don't enable leak
393
0
  // checking, so that doesn't cause the test to fail, but leaving the entry in
394
0
  // the thread list triggers an assertion, which does.
395
0
  nsThread::ClearThreadList();
396
0
397
0
  sShutdownComplete = true;
398
0
}
399
400
void
401
nsThreadManager::RegisterCurrentThread(nsThread& aThread)
402
46
{
403
46
  MOZ_ASSERT(aThread.GetPRThread() == PR_GetCurrentThread(), "bad aThread");
404
46
405
46
  OffTheBooksMutexAutoLock lock(mLock);
406
46
407
46
  ++mCurrentNumberOfThreads;
408
46
  if (mCurrentNumberOfThreads > mHighestNumberOfThreads) {
409
46
    mHighestNumberOfThreads = mCurrentNumberOfThreads;
410
46
  }
411
46
412
46
  mThreadsByPRThread.Put(aThread.GetPRThread(), &aThread);  // XXX check OOM?
413
46
414
46
  aThread.AddRef();  // for TLS entry
415
46
  aThread.mHasTLSEntry = true;
416
46
  PR_SetThreadPrivate(mCurThreadIndex, &aThread);
417
46
}
418
419
void
420
nsThreadManager::UnregisterCurrentThread(nsThread& aThread, bool aIfExists)
421
0
{
422
0
  {
423
0
    OffTheBooksMutexAutoLock lock(mLock);
424
0
425
0
    if (aIfExists && !mThreadsByPRThread.GetWeak(aThread.GetPRThread())) {
426
0
      return;
427
0
    }
428
0
429
0
    MOZ_ASSERT(aThread.GetPRThread() == PR_GetCurrentThread(), "bad aThread");
430
0
431
0
    --mCurrentNumberOfThreads;
432
0
    mThreadsByPRThread.Remove(aThread.GetPRThread());
433
0
  }
434
0
435
0
  PR_SetThreadPrivate(mCurThreadIndex, nullptr);
436
0
  // Ref-count balanced via ReleaseThread
437
0
}
438
439
nsThread*
440
nsThreadManager::CreateCurrentThread(SynchronizedEventQueue* aQueue,
441
                                     nsThread::MainThreadFlag aMainThread)
442
0
{
443
0
  // Make sure we don't have an nsThread yet.
444
0
  MOZ_ASSERT(!PR_GetThreadPrivate(mCurThreadIndex));
445
0
446
0
  if (!mInitialized) {
447
0
    return nullptr;
448
0
  }
449
0
450
0
  // OK, that's fine.  We'll dynamically create one :-)
451
0
  RefPtr<nsThread> thread = new nsThread(WrapNotNull(aQueue), aMainThread, 0);
452
0
  if (!thread || NS_FAILED(thread->InitCurrentThread())) {
453
0
    return nullptr;
454
0
  }
455
0
456
0
  return thread.get();  // reference held in TLS
457
0
}
458
459
nsThread*
460
nsThreadManager::GetCurrentThread()
461
258
{
462
258
  // read thread local storage
463
258
  void* data = PR_GetThreadPrivate(mCurThreadIndex);
464
258
  if (data) {
465
219
    return static_cast<nsThread*>(data);
466
219
  }
467
39
468
39
  if (!mInitialized) {
469
9
    return nullptr;
470
9
  }
471
30
472
30
  // OK, that's fine.  We'll dynamically create one :-)
473
30
  RefPtr<ThreadEventQueue<EventQueue>> queue =
474
30
    new ThreadEventQueue<EventQueue>(MakeUnique<EventQueue>());
475
30
  RefPtr<nsThread> thread = new nsThread(WrapNotNull(queue), nsThread::NOT_MAIN_THREAD, 0);
476
30
  if (!thread || NS_FAILED(thread->InitCurrentThread())) {
477
0
    return nullptr;
478
0
  }
479
30
480
30
  return thread.get();  // reference held in TLS
481
30
}
482
483
bool
484
nsThreadManager::IsNSThread() const
485
49
{
486
49
  if (!mInitialized) {
487
6
    return false;
488
6
  }
489
43
  if (auto* thread = (nsThread*)PR_GetThreadPrivate(mCurThreadIndex)) {
490
43
    return thread->mShutdownRequired;
491
43
  }
492
0
  return false;
493
0
}
494
495
NS_IMETHODIMP
496
nsThreadManager::NewThread(uint32_t aCreationFlags,
497
                           uint32_t aStackSize,
498
                           nsIThread** aResult)
499
0
{
500
0
  return NewNamedThread(NS_LITERAL_CSTRING(""), aStackSize, aResult);
501
0
}
502
503
NS_IMETHODIMP
504
nsThreadManager::NewNamedThread(const nsACString& aName,
505
                                uint32_t aStackSize,
506
                                nsIThread** aResult)
507
13
{
508
13
  // Note: can be called from arbitrary threads
509
13
510
13
  // No new threads during Shutdown
511
13
  if (NS_WARN_IF(!mInitialized)) {
512
0
    return NS_ERROR_NOT_INITIALIZED;
513
0
  }
514
13
515
13
  RefPtr<ThreadEventQueue<EventQueue>> queue =
516
13
    new ThreadEventQueue<EventQueue>(MakeUnique<EventQueue>());
517
13
  RefPtr<nsThread> thr = new nsThread(WrapNotNull(queue), nsThread::NOT_MAIN_THREAD, aStackSize);
518
13
  nsresult rv = thr->Init(aName);  // Note: blocks until the new thread has been set up
519
13
  if (NS_FAILED(rv)) {
520
0
    return rv;
521
0
  }
522
13
523
13
  // At this point, we expect that the thread has been registered in mThreadByPRThread;
524
13
  // however, it is possible that it could have also been replaced by now, so
525
13
  // we cannot really assert that it was added.  Instead, kill it if we entered
526
13
  // Shutdown() during/before Init()
527
13
528
13
  if (NS_WARN_IF(!mInitialized)) {
529
0
    if (thr->ShutdownRequired()) {
530
0
      thr->Shutdown(); // ok if it happens multiple times
531
0
    }
532
0
    return NS_ERROR_NOT_INITIALIZED;
533
0
  }
534
13
535
13
  thr.forget(aResult);
536
13
  return NS_OK;
537
13
}
538
539
NS_IMETHODIMP
540
nsThreadManager::GetThreadFromPRThread(PRThread* aThread, nsIThread** aResult)
541
0
{
542
0
  // Keep this functioning during Shutdown
543
0
  if (NS_WARN_IF(!mMainThread)) {
544
0
    return NS_ERROR_NOT_INITIALIZED;
545
0
  }
546
0
  if (NS_WARN_IF(!aThread)) {
547
0
    return NS_ERROR_INVALID_ARG;
548
0
  }
549
0
550
0
  RefPtr<nsThread> temp;
551
0
  {
552
0
    OffTheBooksMutexAutoLock lock(mLock);
553
0
    mThreadsByPRThread.Get(aThread, getter_AddRefs(temp));
554
0
  }
555
0
556
0
  NS_IF_ADDREF(*aResult = temp);
557
0
  return NS_OK;
558
0
}
559
560
NS_IMETHODIMP
561
nsThreadManager::GetMainThread(nsIThread** aResult)
562
97
{
563
97
  // Keep this functioning during Shutdown
564
97
  if (NS_WARN_IF(!mMainThread)) {
565
0
    return NS_ERROR_NOT_INITIALIZED;
566
0
  }
567
97
  NS_ADDREF(*aResult = mMainThread);
568
97
  return NS_OK;
569
97
}
570
571
NS_IMETHODIMP
572
nsThreadManager::GetCurrentThread(nsIThread** aResult)
573
128
{
574
128
  // Keep this functioning during Shutdown
575
128
  if (!mMainThread) {
576
0
    return NS_ERROR_NOT_INITIALIZED;
577
0
  }
578
128
  *aResult = GetCurrentThread();
579
128
  if (!*aResult) {
580
0
    return NS_ERROR_OUT_OF_MEMORY;
581
0
  }
582
128
  NS_ADDREF(*aResult);
583
128
  return NS_OK;
584
128
}
585
586
NS_IMETHODIMP
587
nsThreadManager::SpinEventLoopUntil(nsINestedEventLoopCondition* aCondition)
588
0
{
589
0
  return SpinEventLoopUntilInternal(aCondition, false);
590
0
}
591
592
NS_IMETHODIMP
593
nsThreadManager::SpinEventLoopUntilOrShutdown(nsINestedEventLoopCondition* aCondition)
594
0
{
595
0
  return SpinEventLoopUntilInternal(aCondition, true);
596
0
}
597
598
nsresult
599
nsThreadManager::SpinEventLoopUntilInternal(nsINestedEventLoopCondition* aCondition,
600
                                            bool aCheckingShutdown)
601
0
{
602
0
  nsCOMPtr<nsINestedEventLoopCondition> condition(aCondition);
603
0
  nsresult rv = NS_OK;
604
0
605
0
  // Nothing to do if already shutting down. Note that gShutdownObserveHelper is
606
0
  // nullified on shutdown.
607
0
  if (aCheckingShutdown &&
608
0
      (!gShutdownObserveHelper || gShutdownObserveHelper->ShuttingDown())) {
609
0
    return NS_OK;
610
0
  }
611
0
612
0
  if (!mozilla::SpinEventLoopUntil([&]() -> bool {
613
0
        // Shutting down is started.
614
0
        if (aCheckingShutdown &&
615
0
            (!gShutdownObserveHelper ||
616
0
             gShutdownObserveHelper->ShuttingDown())) {
617
0
          return true;
618
0
        }
619
0
620
0
        bool isDone = false;
621
0
        rv = condition->IsDone(&isDone);
622
0
        // JS failure should be unusual, but we need to stop and propagate
623
0
        // the error back to the caller.
624
0
        if (NS_FAILED(rv)) {
625
0
          return true;
626
0
        }
627
0
628
0
        return isDone;
629
0
      })) {
630
0
    // We stopped early for some reason, which is unexpected.
631
0
    return NS_ERROR_UNEXPECTED;
632
0
  }
633
0
634
0
  // If we exited when the condition told us to, we need to return whether
635
0
  // the condition encountered failure when executing.
636
0
  return rv;
637
0
}
638
639
NS_IMETHODIMP
640
nsThreadManager::SpinEventLoopUntilEmpty()
641
0
{
642
0
  nsIThread* thread = NS_GetCurrentThread();
643
0
644
0
  while (NS_HasPendingEvents(thread)) {
645
0
    (void)NS_ProcessNextEvent(thread, false);
646
0
  }
647
0
648
0
  return NS_OK;
649
0
}
650
651
NS_IMETHODIMP
652
nsThreadManager::GetSystemGroupEventTarget(nsIEventTarget** aTarget)
653
0
{
654
0
  nsCOMPtr<nsIEventTarget> target = SystemGroup::EventTargetFor(TaskCategory::Other);
655
0
  target.forget(aTarget);
656
0
  return NS_OK;
657
0
}
658
659
uint32_t
660
nsThreadManager::GetHighestNumberOfThreads()
661
0
{
662
0
  OffTheBooksMutexAutoLock lock(mLock);
663
0
  return mHighestNumberOfThreads;
664
0
}
665
666
NS_IMETHODIMP
667
nsThreadManager::DispatchToMainThread(nsIRunnable *aEvent, uint32_t aPriority)
668
0
{
669
0
  // Note: C++ callers should instead use NS_DispatchToMainThread.
670
0
  MOZ_ASSERT(NS_IsMainThread());
671
0
672
0
  // Keep this functioning during Shutdown
673
0
  if (NS_WARN_IF(!mMainThread)) {
674
0
    return NS_ERROR_NOT_INITIALIZED;
675
0
  }
676
0
  if (aPriority != nsIRunnablePriority::PRIORITY_NORMAL) {
677
0
    nsCOMPtr<nsIRunnable> event(aEvent);
678
0
    return mMainThread->DispatchFromScript(
679
0
             new PrioritizableRunnable(event.forget(), aPriority), 0);
680
0
  }
681
0
  return mMainThread->DispatchFromScript(aEvent, 0);
682
0
}
683
684
void
685
nsThreadManager::EnableMainThreadEventPrioritization()
686
0
{
687
0
  MOZ_ASSERT(NS_IsMainThread());
688
0
  InputEventStatistics::Get().SetEnable(true);
689
0
  mMainThread->EnableInputEventPrioritization();
690
0
}
691
692
void
693
nsThreadManager::FlushInputEventPrioritization()
694
0
{
695
0
  MOZ_ASSERT(NS_IsMainThread());
696
0
  mMainThread->FlushInputEventPrioritization();
697
0
}
698
699
void
700
nsThreadManager::SuspendInputEventPrioritization()
701
0
{
702
0
  MOZ_ASSERT(NS_IsMainThread());
703
0
  mMainThread->SuspendInputEventPrioritization();
704
0
}
705
706
void
707
nsThreadManager::ResumeInputEventPrioritization()
708
0
{
709
0
  MOZ_ASSERT(NS_IsMainThread());
710
0
  mMainThread->ResumeInputEventPrioritization();
711
0
}
712
713
NS_IMETHODIMP
714
nsThreadManager::IdleDispatchToMainThread(nsIRunnable *aEvent, uint32_t aTimeout)
715
0
{
716
0
  // Note: C++ callers should instead use NS_IdleDispatchToThread or
717
0
  // NS_IdleDispatchToCurrentThread.
718
0
  MOZ_ASSERT(NS_IsMainThread());
719
0
720
0
  nsCOMPtr<nsIRunnable> event(aEvent);
721
0
  if (aTimeout) {
722
0
    return NS_IdleDispatchToThread(event.forget(), aTimeout, mMainThread);
723
0
  }
724
0
725
0
  return NS_IdleDispatchToThread(event.forget(), mMainThread);
726
0
}
727
728
namespace mozilla {
729
730
PRThread*
731
GetCurrentVirtualThread()
732
118
{
733
118
  // We call GetCurrentVirtualThread very early in startup, before the TLS is
734
118
  // initialized. Make sure we don't assert in that case.
735
118
  if (gTlsCurrentVirtualThread.initialized()) {
736
118
    if (gTlsCurrentVirtualThread.get()) {
737
0
      return gTlsCurrentVirtualThread.get();
738
0
    }
739
118
  }
740
118
  return PR_GetCurrentThread();
741
118
}
742
743
PRThread*
744
GetCurrentPhysicalThread()
745
0
{
746
0
  return PR_GetCurrentThread();
747
0
}
748
749
} // namespace mozilla