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 "nsThread.h"
8 :
9 : #include "base/message_loop.h"
10 :
11 : // Chromium's logging can sometimes leak through...
12 : #ifdef LOG
13 : #undef LOG
14 : #endif
15 :
16 : #include "mozilla/ReentrantMonitor.h"
17 : #include "nsMemoryPressure.h"
18 : #include "nsThreadManager.h"
19 : #include "nsIClassInfoImpl.h"
20 : #include "nsAutoPtr.h"
21 : #include "nsCOMPtr.h"
22 : #include "nsQueryObject.h"
23 : #include "pratom.h"
24 : #include "mozilla/CycleCollectedJSContext.h"
25 : #include "mozilla/Logging.h"
26 : #include "nsIObserverService.h"
27 : #include "mozilla/HangMonitor.h"
28 : #include "mozilla/IOInterposer.h"
29 : #include "mozilla/ipc/MessageChannel.h"
30 : #include "mozilla/ipc/BackgroundChild.h"
31 : #include "mozilla/SchedulerGroup.h"
32 : #include "mozilla/Services.h"
33 : #include "nsXPCOMPrivate.h"
34 : #include "mozilla/ChaosMode.h"
35 : #include "mozilla/Telemetry.h"
36 : #include "mozilla/TimeStamp.h"
37 : #include "mozilla/Unused.h"
38 : #include "mozilla/dom/ScriptSettings.h"
39 : #include "nsIIdlePeriod.h"
40 : #include "nsIIdleRunnable.h"
41 : #include "nsThreadSyncDispatch.h"
42 : #include "LeakRefPtr.h"
43 : #include "GeckoProfiler.h"
44 :
45 : #ifdef MOZ_CRASHREPORTER
46 : #include "nsServiceManagerUtils.h"
47 : #include "nsICrashReporter.h"
48 : #include "mozilla/dom/ContentChild.h"
49 : #endif
50 :
51 : #ifdef XP_LINUX
52 : #include <sys/time.h>
53 : #include <sys/resource.h>
54 : #include <sched.h>
55 : #endif
56 :
57 : #define HAVE_UALARM _BSD_SOURCE || (_XOPEN_SOURCE >= 500 || \
58 : _XOPEN_SOURCE && _XOPEN_SOURCE_EXTENDED) && \
59 : !(_POSIX_C_SOURCE >= 200809L || _XOPEN_SOURCE >= 700)
60 :
61 : #if defined(XP_LINUX) && !defined(ANDROID) && defined(_GNU_SOURCE)
62 : #define HAVE_SCHED_SETAFFINITY
63 : #endif
64 :
65 : #ifdef XP_MACOSX
66 : #include <mach/mach.h>
67 : #include <mach/thread_policy.h>
68 : #endif
69 :
70 : #ifdef MOZ_CANARY
71 : # include <unistd.h>
72 : # include <execinfo.h>
73 : # include <signal.h>
74 : # include <fcntl.h>
75 : # include "nsXULAppAPI.h"
76 : #endif
77 :
78 : #if defined(NS_FUNCTION_TIMER) && defined(_MSC_VER)
79 : #include "nsTimerImpl.h"
80 : #include "mozilla/StackWalk.h"
81 : #endif
82 : #ifdef NS_FUNCTION_TIMER
83 : #include "nsCRT.h"
84 : #endif
85 :
86 : #ifdef MOZ_TASK_TRACER
87 : #include "GeckoTaskTracer.h"
88 : #include "TracedTaskCommon.h"
89 : using namespace mozilla::tasktracer;
90 : #endif
91 :
92 : using namespace mozilla;
93 :
94 : static LazyLogModule sThreadLog("nsThread");
95 : #ifdef LOG
96 : #undef LOG
97 : #endif
98 : #define LOG(args) MOZ_LOG(sThreadLog, mozilla::LogLevel::Debug, args)
99 :
100 : NS_DECL_CI_INTERFACE_GETTER(nsThread)
101 :
102 : const char* nsThread::sMainThreadRunnableName = nullptr;
103 :
104 : //-----------------------------------------------------------------------------
105 : // Because we do not have our own nsIFactory, we have to implement nsIClassInfo
106 : // somewhat manually.
107 :
108 : class nsThreadClassInfo : public nsIClassInfo
109 : {
110 : public:
111 : NS_DECL_ISUPPORTS_INHERITED // no mRefCnt
112 : NS_DECL_NSICLASSINFO
113 :
114 0 : nsThreadClassInfo()
115 0 : {
116 0 : }
117 : };
118 :
119 : NS_IMETHODIMP_(MozExternalRefCountType)
120 0 : nsThreadClassInfo::AddRef()
121 : {
122 0 : return 2;
123 : }
124 : NS_IMETHODIMP_(MozExternalRefCountType)
125 0 : nsThreadClassInfo::Release()
126 : {
127 0 : return 1;
128 : }
129 0 : NS_IMPL_QUERY_INTERFACE(nsThreadClassInfo, nsIClassInfo)
130 :
131 : NS_IMETHODIMP
132 0 : nsThreadClassInfo::GetInterfaces(uint32_t* aCount, nsIID*** aArray)
133 : {
134 0 : return NS_CI_INTERFACE_GETTER_NAME(nsThread)(aCount, aArray);
135 : }
136 :
137 : NS_IMETHODIMP
138 0 : nsThreadClassInfo::GetScriptableHelper(nsIXPCScriptable** aResult)
139 : {
140 0 : *aResult = nullptr;
141 0 : return NS_OK;
142 : }
143 :
144 : NS_IMETHODIMP
145 0 : nsThreadClassInfo::GetContractID(char** aResult)
146 : {
147 0 : *aResult = nullptr;
148 0 : return NS_OK;
149 : }
150 :
151 : NS_IMETHODIMP
152 0 : nsThreadClassInfo::GetClassDescription(char** aResult)
153 : {
154 0 : *aResult = nullptr;
155 0 : return NS_OK;
156 : }
157 :
158 : NS_IMETHODIMP
159 0 : nsThreadClassInfo::GetClassID(nsCID** aResult)
160 : {
161 0 : *aResult = nullptr;
162 0 : return NS_OK;
163 : }
164 :
165 : NS_IMETHODIMP
166 0 : nsThreadClassInfo::GetFlags(uint32_t* aResult)
167 : {
168 0 : *aResult = THREADSAFE;
169 0 : return NS_OK;
170 : }
171 :
172 : NS_IMETHODIMP
173 0 : nsThreadClassInfo::GetClassIDNoAlloc(nsCID* aResult)
174 : {
175 0 : return NS_ERROR_NOT_AVAILABLE;
176 : }
177 :
178 : //-----------------------------------------------------------------------------
179 :
180 8180 : NS_IMPL_ADDREF(nsThread)
181 7707 : NS_IMPL_RELEASE(nsThread)
182 3796 : NS_INTERFACE_MAP_BEGIN(nsThread)
183 3796 : NS_INTERFACE_MAP_ENTRY(nsIThread)
184 1377 : NS_INTERFACE_MAP_ENTRY(nsIThreadInternal)
185 1341 : NS_INTERFACE_MAP_ENTRY(nsIEventTarget)
186 27 : NS_INTERFACE_MAP_ENTRY(nsISerialEventTarget)
187 2 : NS_INTERFACE_MAP_ENTRY(nsISupportsPriority)
188 2 : NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIThread)
189 0 : if (aIID.Equals(NS_GET_IID(nsIClassInfo))) {
190 0 : static nsThreadClassInfo sThreadClassInfo;
191 0 : foundInterface = static_cast<nsIClassInfo*>(&sThreadClassInfo);
192 : } else
193 0 : NS_INTERFACE_MAP_END
194 0 : NS_IMPL_CI_INTERFACE_GETTER(nsThread, nsIThread, nsIThreadInternal,
195 : nsIEventTarget, nsISupportsPriority)
196 :
197 : //-----------------------------------------------------------------------------
198 :
199 : class nsThreadStartupEvent : public Runnable
200 : {
201 : public:
202 56 : nsThreadStartupEvent()
203 56 : : Runnable("nsThreadStartupEvent")
204 : , mMon("nsThreadStartupEvent.mMon")
205 56 : , mInitialized(false)
206 : {
207 56 : }
208 :
209 : // This method does not return until the thread startup object is in the
210 : // completion state.
211 56 : void Wait()
212 : {
213 112 : ReentrantMonitorAutoEnter mon(mMon);
214 168 : while (!mInitialized) {
215 56 : mon.Wait();
216 : }
217 56 : }
218 :
219 : // This method needs to be public to support older compilers (xlC_r on AIX).
220 : // It should be called directly as this class type is reference counted.
221 168 : virtual ~nsThreadStartupEvent() {}
222 :
223 : private:
224 56 : NS_IMETHOD Run() override
225 : {
226 112 : ReentrantMonitorAutoEnter mon(mMon);
227 56 : mInitialized = true;
228 56 : mon.Notify();
229 112 : return NS_OK;
230 : }
231 :
232 : ReentrantMonitor mMon;
233 : bool mInitialized;
234 : };
235 : //-----------------------------------------------------------------------------
236 :
237 : namespace {
238 : class DelayedRunnable : public Runnable,
239 : public nsITimerCallback
240 : {
241 : public:
242 2 : DelayedRunnable(already_AddRefed<nsIThread> aTargetThread,
243 : already_AddRefed<nsIRunnable> aRunnable,
244 : uint32_t aDelay)
245 2 : : mozilla::Runnable("DelayedRunnable")
246 : , mTargetThread(aTargetThread)
247 : , mWrappedRunnable(aRunnable)
248 : , mDelayedFrom(TimeStamp::NowLoRes())
249 2 : , mDelay(aDelay)
250 2 : { }
251 :
252 : NS_DECL_ISUPPORTS_INHERITED
253 :
254 2 : nsresult Init()
255 : {
256 : nsresult rv;
257 2 : mTimer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv);
258 2 : NS_ENSURE_SUCCESS(rv, rv);
259 :
260 2 : MOZ_ASSERT(mTimer);
261 2 : rv = mTimer->SetTarget(mTargetThread);
262 :
263 2 : NS_ENSURE_SUCCESS(rv, rv);
264 2 : return mTimer->InitWithCallback(this, mDelay, nsITimer::TYPE_ONE_SHOT);
265 : }
266 :
267 2 : nsresult DoRun()
268 : {
269 4 : nsCOMPtr<nsIRunnable> r = mWrappedRunnable.forget();
270 4 : return r->Run();
271 : }
272 :
273 2 : NS_IMETHOD Run() override
274 : {
275 : // Already ran?
276 2 : if (!mWrappedRunnable) {
277 0 : return NS_OK;
278 : }
279 :
280 : // Are we too early?
281 2 : if ((TimeStamp::NowLoRes() - mDelayedFrom).ToMilliseconds() < mDelay) {
282 2 : return NS_OK; // Let the nsITimer run us.
283 : }
284 :
285 0 : mTimer->Cancel();
286 0 : return DoRun();
287 : }
288 :
289 2 : NS_IMETHOD Notify(nsITimer* aTimer) override
290 : {
291 : // If we already ran, the timer should have been canceled.
292 2 : MOZ_ASSERT(mWrappedRunnable);
293 2 : MOZ_ASSERT(aTimer == mTimer);
294 :
295 2 : return DoRun();
296 : }
297 :
298 : private:
299 6 : ~DelayedRunnable() {}
300 :
301 : nsCOMPtr<nsIThread> mTargetThread;
302 : nsCOMPtr<nsIRunnable> mWrappedRunnable;
303 : nsCOMPtr<nsITimer> mTimer;
304 : TimeStamp mDelayedFrom;
305 : uint32_t mDelay;
306 : };
307 :
308 30 : NS_IMPL_ISUPPORTS_INHERITED(DelayedRunnable, Runnable, nsITimerCallback)
309 :
310 : } // anonymous namespace
311 :
312 : //-----------------------------------------------------------------------------
313 :
314 : struct nsThreadShutdownContext
315 : {
316 1 : nsThreadShutdownContext(NotNull<nsThread*> aTerminatingThread,
317 : NotNull<nsThread*> aJoiningThread,
318 : bool aAwaitingShutdownAck)
319 1 : : mTerminatingThread(aTerminatingThread)
320 : , mJoiningThread(aJoiningThread)
321 1 : , mAwaitingShutdownAck(aAwaitingShutdownAck)
322 : {
323 1 : MOZ_COUNT_CTOR(nsThreadShutdownContext);
324 1 : }
325 1 : ~nsThreadShutdownContext()
326 1 : {
327 1 : MOZ_COUNT_DTOR(nsThreadShutdownContext);
328 1 : }
329 :
330 : // NB: This will be the last reference.
331 : NotNull<RefPtr<nsThread>> mTerminatingThread;
332 : NotNull<nsThread*> MOZ_UNSAFE_REF("Thread manager is holding reference to joining thread")
333 : mJoiningThread;
334 : bool mAwaitingShutdownAck;
335 : };
336 :
337 : // This event is responsible for notifying nsThread::Shutdown that it is time
338 : // to call PR_JoinThread. It implements nsICancelableRunnable so that it can
339 : // run on a DOM Worker thread (where all events must implement
340 : // nsICancelableRunnable.)
341 : class nsThreadShutdownAckEvent : public CancelableRunnable
342 : {
343 : public:
344 1 : explicit nsThreadShutdownAckEvent(NotNull<nsThreadShutdownContext*> aCtx)
345 1 : : CancelableRunnable("nsThreadShutdownAckEvent")
346 1 : , mShutdownContext(aCtx)
347 : {
348 1 : }
349 1 : NS_IMETHOD Run() override
350 : {
351 1 : mShutdownContext->mTerminatingThread->ShutdownComplete(mShutdownContext);
352 1 : return NS_OK;
353 : }
354 0 : nsresult Cancel() override
355 : {
356 0 : return Run();
357 : }
358 : private:
359 3 : virtual ~nsThreadShutdownAckEvent() { }
360 :
361 : NotNull<nsThreadShutdownContext*> mShutdownContext;
362 : };
363 :
364 : // This event is responsible for setting mShutdownContext
365 3 : class nsThreadShutdownEvent : public Runnable
366 : {
367 : public:
368 1 : nsThreadShutdownEvent(NotNull<nsThread*> aThr,
369 : NotNull<nsThreadShutdownContext*> aCtx)
370 1 : : Runnable("nsThreadShutdownEvent")
371 : , mThread(aThr)
372 1 : , mShutdownContext(aCtx)
373 : {
374 1 : }
375 1 : NS_IMETHOD Run() override
376 : {
377 1 : mThread->mShutdownContext = mShutdownContext;
378 1 : MessageLoop::current()->Quit();
379 1 : return NS_OK;
380 : }
381 : private:
382 : NotNull<RefPtr<nsThread>> mThread;
383 : NotNull<nsThreadShutdownContext*> mShutdownContext;
384 : };
385 :
386 : //-----------------------------------------------------------------------------
387 :
388 : static void
389 0 : SetThreadAffinity(unsigned int cpu)
390 : {
391 : #ifdef HAVE_SCHED_SETAFFINITY
392 : cpu_set_t cpus;
393 0 : CPU_ZERO(&cpus);
394 0 : CPU_SET(cpu, &cpus);
395 0 : sched_setaffinity(0, sizeof(cpus), &cpus);
396 : // Don't assert sched_setaffinity's return value because it intermittently (?)
397 : // fails with EINVAL on Linux x64 try runs.
398 : #elif defined(XP_MACOSX)
399 : // OS X does not provide APIs to pin threads to specific processors, but you
400 : // can tag threads as belonging to the same "affinity set" and the OS will try
401 : // to run them on the same processor. To run threads on different processors,
402 : // tag them as belonging to different affinity sets. Tag 0, the default, means
403 : // "no affinity" so let's pretend each CPU has its own tag `cpu+1`.
404 : thread_affinity_policy_data_t policy;
405 : policy.affinity_tag = cpu + 1;
406 : MOZ_ALWAYS_TRUE(thread_policy_set(mach_thread_self(), THREAD_AFFINITY_POLICY,
407 : &policy.affinity_tag, 1) == KERN_SUCCESS);
408 : #elif defined(XP_WIN)
409 : MOZ_ALWAYS_TRUE(SetThreadIdealProcessor(GetCurrentThread(), cpu) != -1);
410 : #endif
411 0 : }
412 :
413 : static void
414 66 : SetupCurrentThreadForChaosMode()
415 : {
416 66 : if (!ChaosMode::isActive(ChaosFeature::ThreadScheduling)) {
417 66 : return;
418 : }
419 :
420 : #ifdef XP_LINUX
421 : // PR_SetThreadPriority doesn't really work since priorities >
422 : // PR_PRIORITY_NORMAL can't be set by non-root users. Instead we'll just use
423 : // setpriority(2) to set random 'nice values'. In regular Linux this is only
424 : // a dynamic adjustment so it still doesn't really do what we want, but tools
425 : // like 'rr' can be more aggressive about honoring these values.
426 : // Some of these calls may fail due to trying to lower the priority
427 : // (e.g. something may have already called setpriority() for this thread).
428 : // This makes it hard to have non-main threads with higher priority than the
429 : // main thread, but that's hard to fix. Tools like rr can choose to honor the
430 : // requested values anyway.
431 : // Use just 4 priorities so there's a reasonable chance of any two threads
432 : // having equal priority.
433 0 : setpriority(PRIO_PROCESS, 0, ChaosMode::randomUint32LessThan(4));
434 : #else
435 : // We should set the affinity here but NSPR doesn't provide a way to expose it.
436 : uint32_t priority = ChaosMode::randomUint32LessThan(PR_PRIORITY_LAST + 1);
437 : PR_SetThreadPriority(PR_GetCurrentThread(), PRThreadPriority(priority));
438 : #endif
439 :
440 : // Force half the threads to CPU 0 so they compete for CPU
441 0 : if (ChaosMode::randomUint32LessThan(2)) {
442 0 : SetThreadAffinity(0);
443 : }
444 : }
445 :
446 : namespace {
447 :
448 : struct ThreadInitData {
449 : nsThread* thread;
450 : const nsACString& name;
451 : };
452 :
453 : }
454 :
455 : /*static*/ void
456 56 : nsThread::ThreadFunc(void* aArg)
457 : {
458 : using mozilla::ipc::BackgroundChild;
459 :
460 : char stackTop;
461 :
462 56 : ThreadInitData* initData = static_cast<ThreadInitData*>(aArg);
463 56 : nsThread* self = initData->thread; // strong reference
464 :
465 56 : self->mThread = PR_GetCurrentThread();
466 56 : self->mVirtualThread = GetCurrentVirtualThread();
467 56 : SetupCurrentThreadForChaosMode();
468 :
469 56 : if (!initData->name.IsEmpty()) {
470 55 : NS_SetCurrentThreadName(initData->name.BeginReading());
471 : }
472 :
473 : // Inform the ThreadManager
474 56 : nsThreadManager::get().RegisterCurrentThread(*self);
475 :
476 56 : mozilla::IOInterposer::RegisterCurrentThread();
477 :
478 : // This must come after the call to nsThreadManager::RegisterCurrentThread(),
479 : // because that call is needed to properly set up this thread as an nsThread,
480 : // which profiler_register_thread() requires. See bug 1347007.
481 56 : if (!initData->name.IsEmpty()) {
482 55 : profiler_register_thread(initData->name.BeginReading(), &stackTop);
483 : }
484 :
485 : // Wait for and process startup event
486 57 : nsCOMPtr<nsIRunnable> event;
487 : {
488 112 : MutexAutoLock lock(self->mLock);
489 56 : if (!self->mEvents->GetEvent(true, getter_AddRefs(event), nullptr, lock)) {
490 0 : NS_WARNING("failed waiting for thread startup event");
491 0 : return;
492 : }
493 : }
494 :
495 56 : initData = nullptr; // clear before unblocking nsThread::Init
496 :
497 56 : event->Run(); // unblocks nsThread::Init
498 56 : event = nullptr;
499 :
500 : {
501 : // Scope for MessageLoop.
502 : nsAutoPtr<MessageLoop> loop(
503 57 : new MessageLoop(MessageLoop::TYPE_MOZILLA_NONMAINTHREAD, self));
504 :
505 : // Now, process incoming events...
506 56 : loop->Run();
507 :
508 1 : BackgroundChild::CloseForCurrentThread();
509 :
510 : // NB: The main thread does not shut down here! It shuts down via
511 : // nsThreadManager::Shutdown.
512 :
513 : // Do NS_ProcessPendingEvents but with special handling to set
514 : // mEventsAreDoomed atomically with the removal of the last event. The key
515 : // invariant here is that we will never permit PutEvent to succeed if the
516 : // event would be left in the queue after our final call to
517 : // NS_ProcessPendingEvents. We also have to keep processing events as long
518 : // as we have outstanding mRequestedShutdownContexts.
519 : while (true) {
520 : // Check and see if we're waiting on any threads.
521 1 : self->WaitForAllAsynchronousShutdowns();
522 :
523 : {
524 1 : MutexAutoLock lock(self->mLock);
525 1 : if (!self->mEvents->HasPendingEvent(lock)) {
526 : // No events in the queue, so we will stop now. Don't let any more
527 : // events be added, since they won't be processed. It is critical
528 : // that no PutEvent can occur between testing that the event queue is
529 : // empty and setting mEventsAreDoomed!
530 1 : self->mEventsAreDoomed = true;
531 1 : break;
532 : }
533 : }
534 0 : NS_ProcessPendingEvents(self);
535 0 : }
536 : }
537 :
538 1 : mozilla::IOInterposer::UnregisterCurrentThread();
539 :
540 : // Inform the threadmanager that this thread is going away
541 1 : nsThreadManager::get().UnregisterCurrentThread(*self);
542 :
543 1 : profiler_unregister_thread();
544 :
545 : // Dispatch shutdown ACK
546 : NotNull<nsThreadShutdownContext*> context =
547 1 : WrapNotNull(self->mShutdownContext);
548 1 : MOZ_ASSERT(context->mTerminatingThread == self);
549 2 : event = do_QueryObject(new nsThreadShutdownAckEvent(context));
550 1 : context->mJoiningThread->Dispatch(event, NS_DISPATCH_NORMAL);
551 :
552 : // Release any observer of the thread here.
553 1 : self->SetObserver(nullptr);
554 :
555 : #ifdef MOZ_TASK_TRACER
556 : FreeTraceInfo();
557 : #endif
558 :
559 1 : NS_RELEASE(self);
560 : }
561 :
562 : //-----------------------------------------------------------------------------
563 :
564 : #ifdef MOZ_CRASHREPORTER
565 : // Tell the crash reporter to save a memory report if our heuristics determine
566 : // that an OOM failure is likely to occur soon.
567 : // Memory usage will not be checked more than every 30 seconds or saved more
568 : // than every 3 minutes
569 : // If |aShouldSave == kForceReport|, a report will be saved regardless of
570 : // whether the process is low on memory or not. However, it will still not be
571 : // saved if a report was saved less than 3 minutes ago.
572 : bool
573 1230 : nsThread::SaveMemoryReportNearOOM(ShouldSaveMemoryReport aShouldSave)
574 : {
575 : // Keep an eye on memory usage (cheap, ~7ms) somewhat frequently,
576 : // but save memory reports (expensive, ~75ms) less frequently.
577 1230 : const size_t kLowMemoryCheckSeconds = 30;
578 1230 : const size_t kLowMemorySaveSeconds = 3 * 60;
579 :
580 6 : static TimeStamp nextCheck = TimeStamp::NowLoRes()
581 1236 : + TimeDuration::FromSeconds(kLowMemoryCheckSeconds);
582 : static bool recentlySavedReport = false; // Keeps track of whether a report
583 : // was saved last time we checked
584 :
585 : // Are we checking again too soon?
586 1230 : TimeStamp now = TimeStamp::NowLoRes();
587 1230 : if ((aShouldSave == ShouldSaveMemoryReport::kMaybeReport ||
588 2460 : recentlySavedReport) && now < nextCheck) {
589 1230 : return false;
590 : }
591 :
592 0 : bool needMemoryReport = (aShouldSave == ShouldSaveMemoryReport::kForceReport);
593 : #ifdef XP_WIN // XXX implement on other platforms as needed
594 : // If the report is forced there is no need to check whether it is necessary
595 : if (aShouldSave != ShouldSaveMemoryReport::kForceReport) {
596 : const size_t LOWMEM_THRESHOLD_VIRTUAL = 200 * 1024 * 1024;
597 : MEMORYSTATUSEX statex;
598 : statex.dwLength = sizeof(statex);
599 : if (GlobalMemoryStatusEx(&statex)) {
600 : if (statex.ullAvailVirtual < LOWMEM_THRESHOLD_VIRTUAL) {
601 : needMemoryReport = true;
602 : }
603 : }
604 : }
605 : #endif
606 :
607 0 : if (needMemoryReport) {
608 0 : if (XRE_IsContentProcess()) {
609 0 : dom::ContentChild* cc = dom::ContentChild::GetSingleton();
610 0 : if (cc) {
611 0 : cc->SendNotifyLowMemory();
612 : }
613 : } else {
614 : nsCOMPtr<nsICrashReporter> cr =
615 0 : do_GetService("@mozilla.org/toolkit/crash-reporter;1");
616 0 : if (cr) {
617 0 : cr->SaveMemoryReport();
618 : }
619 : }
620 0 : recentlySavedReport = true;
621 0 : nextCheck = now + TimeDuration::FromSeconds(kLowMemorySaveSeconds);
622 : } else {
623 0 : recentlySavedReport = false;
624 0 : nextCheck = now + TimeDuration::FromSeconds(kLowMemoryCheckSeconds);
625 : }
626 :
627 0 : return recentlySavedReport;
628 : }
629 : #endif
630 :
631 : #ifdef MOZ_CANARY
632 : int sCanaryOutputFD = -1;
633 : #endif
634 :
635 66 : nsThread::nsThread(MainThreadFlag aMainThread, uint32_t aStackSize)
636 : : mLock("nsThread.mLock")
637 : , mScriptObserver(nullptr)
638 66 : , mEvents(WrapNotNull(&mEventsRoot))
639 : , mEventsRoot(mLock)
640 : , mIdleEventsAvailable(mLock, "[nsThread.mEventsAvailable]")
641 : , mIdleEvents(mIdleEventsAvailable, nsEventQueue::eNormalQueue)
642 : , mPriority(PRIORITY_NORMAL)
643 : , mThread(nullptr)
644 : , mNestedEventLoopDepth(0)
645 : , mStackSize(aStackSize)
646 : , mShutdownContext(nullptr)
647 : , mShutdownRequired(false)
648 : , mEventsAreDoomed(false)
649 : , mIsMainThread(aMainThread)
650 : , mLastUnlabeledRunnable(TimeStamp::Now())
651 : , mCanInvokeJS(false)
652 132 : , mHasPendingEventsPromisedIdleEvent(false)
653 : {
654 66 : }
655 :
656 3 : nsThread::~nsThread()
657 : {
658 1 : NS_ASSERTION(mRequestedShutdownContexts.IsEmpty(),
659 : "shouldn't be waiting on other threads to shutdown");
660 : #ifdef DEBUG
661 : // We deliberately leak these so they can be tracked by the leak checker.
662 : // If you're having nsThreadShutdownContext leaks, you can set:
663 : // XPCOM_MEM_LOG_CLASSES=nsThreadShutdownContext
664 : // during a test run and that will at least tell you what thread is
665 : // requesting shutdown on another, which can be helpful for diagnosing
666 : // the leak.
667 1 : for (size_t i = 0; i < mRequestedShutdownContexts.Length(); ++i) {
668 0 : Unused << mRequestedShutdownContexts[i].forget();
669 : }
670 : #endif
671 3 : }
672 :
673 : nsresult
674 56 : nsThread::Init(const nsACString& aName)
675 : {
676 : // spawn thread and wait until it is fully setup
677 112 : RefPtr<nsThreadStartupEvent> startup = new nsThreadStartupEvent();
678 :
679 56 : NS_ADDREF_THIS();
680 :
681 56 : mIdlePeriod = new IdlePeriod();
682 :
683 56 : mShutdownRequired = true;
684 :
685 56 : ThreadInitData initData = { this, aName };
686 :
687 : // ThreadFunc is responsible for setting mThread
688 56 : if (!PR_CreateThread(PR_USER_THREAD, ThreadFunc, &initData,
689 : PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
690 : PR_JOINABLE_THREAD, mStackSize)) {
691 0 : NS_RELEASE_THIS();
692 0 : return NS_ERROR_OUT_OF_MEMORY;
693 : }
694 :
695 : // ThreadFunc will wait for this event to be run before it tries to access
696 : // mThread. By delaying insertion of this event into the queue, we ensure
697 : // that mThread is set properly.
698 : {
699 112 : MutexAutoLock lock(mLock);
700 56 : mEventsRoot.PutEvent(startup, lock); // retain a reference
701 : }
702 :
703 : // Wait for thread to call ThreadManager::SetupCurrentThread, which completes
704 : // initialization of ThreadFunc.
705 56 : startup->Wait();
706 56 : return NS_OK;
707 : }
708 :
709 : nsresult
710 10 : nsThread::InitCurrentThread()
711 : {
712 10 : mThread = PR_GetCurrentThread();
713 10 : mVirtualThread = GetCurrentVirtualThread();
714 10 : SetupCurrentThreadForChaosMode();
715 :
716 10 : mIdlePeriod = new IdlePeriod();
717 :
718 10 : nsThreadManager::get().RegisterCurrentThread(*this);
719 10 : return NS_OK;
720 : }
721 :
722 : nsresult
723 0 : nsThread::PutEvent(nsIRunnable* aEvent, nsNestedEventTarget* aTarget)
724 : {
725 0 : nsCOMPtr<nsIRunnable> event(aEvent);
726 0 : return PutEvent(event.forget(), aTarget);
727 : }
728 :
729 : nsresult
730 1566 : nsThread::PutEvent(already_AddRefed<nsIRunnable> aEvent, nsNestedEventTarget* aTarget)
731 : {
732 : // We want to leak the reference when we fail to dispatch it, so that
733 : // we won't release the event in a wrong thread.
734 1566 : LeakRefPtr<nsIRunnable> event(Move(aEvent));
735 3134 : nsCOMPtr<nsIThreadObserver> obs;
736 :
737 : {
738 3133 : MutexAutoLock lock(mLock);
739 1567 : nsChainedEventQueue* queue = aTarget ? aTarget->mQueue : &mEventsRoot;
740 1567 : if (!queue || (queue == &mEventsRoot && mEventsAreDoomed)) {
741 0 : NS_WARNING("An event was posted to a thread that will never run it (rejected)");
742 0 : return NS_ERROR_UNEXPECTED;
743 : }
744 1567 : queue->PutEvent(event.take(), lock);
745 :
746 : // Make sure to grab the observer before dropping the lock, otherwise the
747 : // event that we just placed into the queue could run and eventually delete
748 : // this nsThread before the calling thread is scheduled again. We would then
749 : // crash while trying to access a dead nsThread.
750 1567 : obs = mObserver;
751 : }
752 :
753 1567 : if (obs) {
754 1249 : obs->OnDispatchedEvent(this);
755 : }
756 :
757 1567 : return NS_OK;
758 : }
759 :
760 : nsresult
761 1566 : nsThread::DispatchInternal(already_AddRefed<nsIRunnable> aEvent, uint32_t aFlags,
762 : nsNestedEventTarget* aTarget)
763 : {
764 : // We want to leak the reference when we fail to dispatch it, so that
765 : // we won't release the event in a wrong thread.
766 1566 : LeakRefPtr<nsIRunnable> event(Move(aEvent));
767 1566 : if (NS_WARN_IF(!event)) {
768 0 : return NS_ERROR_INVALID_ARG;
769 : }
770 :
771 1566 : if (gXPCOMThreadsShutDown && MAIN_THREAD != mIsMainThread && !aTarget) {
772 0 : NS_ASSERTION(false, "Failed Dispatch after xpcom-shutdown-threads");
773 0 : return NS_ERROR_ILLEGAL_DURING_SHUTDOWN;
774 : }
775 :
776 : #ifdef MOZ_TASK_TRACER
777 : nsCOMPtr<nsIRunnable> tracedRunnable = CreateTracedRunnable(event.take());
778 : (static_cast<TracedRunnable*>(tracedRunnable.get()))->DispatchTask();
779 : // XXX tracedRunnable will always leaked when we fail to disptch.
780 : event = tracedRunnable.forget();
781 : #endif
782 :
783 1566 : if (aFlags & DISPATCH_SYNC) {
784 0 : nsThread* thread = nsThreadManager::get().GetCurrentThread();
785 0 : if (NS_WARN_IF(!thread)) {
786 0 : return NS_ERROR_NOT_AVAILABLE;
787 : }
788 :
789 : // XXX we should be able to do something better here... we should
790 : // be able to monitor the slot occupied by this event and use
791 : // that to tell us when the event has been processed.
792 :
793 : RefPtr<nsThreadSyncDispatch> wrapper =
794 0 : new nsThreadSyncDispatch(thread, event.take());
795 0 : nsresult rv = PutEvent(wrapper, aTarget); // hold a ref
796 : // Don't wait for the event to finish if we didn't dispatch it...
797 0 : if (NS_FAILED(rv)) {
798 : // PutEvent leaked the wrapper runnable object on failure, so we
799 : // explicitly release this object once for that. Note that this
800 : // object will be released again soon because it exits the scope.
801 0 : wrapper.get()->Release();
802 0 : return rv;
803 : }
804 :
805 : // Allows waiting; ensure no locks are held that would deadlock us!
806 0 : SpinEventLoopUntil([&, wrapper]() -> bool {
807 0 : return !wrapper->IsPending();
808 0 : }, thread);
809 :
810 0 : return NS_OK;
811 : }
812 :
813 1566 : NS_ASSERTION(aFlags == NS_DISPATCH_NORMAL ||
814 : aFlags == NS_DISPATCH_AT_END, "unexpected dispatch flags");
815 1566 : return PutEvent(event.take(), aTarget);
816 : }
817 :
818 : bool
819 2044 : nsThread::nsChainedEventQueue::GetEvent(bool aMayWait, nsIRunnable** aEvent,
820 : unsigned short* aPriority,
821 : mozilla::MutexAutoLock& aProofOfLock)
822 : {
823 2044 : bool retVal = false;
824 2 : do {
825 2046 : if (mProcessSecondaryQueueRunnable) {
826 73 : MOZ_ASSERT(mSecondaryQueue->HasPendingEvent(aProofOfLock));
827 73 : retVal = mSecondaryQueue->GetEvent(aMayWait, aEvent, aProofOfLock);
828 73 : MOZ_ASSERT(*aEvent);
829 73 : if (aPriority) {
830 73 : *aPriority = nsIRunnablePriority::PRIORITY_HIGH;
831 : }
832 73 : mProcessSecondaryQueueRunnable = false;
833 73 : return retVal;
834 : }
835 :
836 : // We don't want to wait if mSecondaryQueue has some events.
837 : bool reallyMayWait =
838 1973 : aMayWait && !mSecondaryQueue->HasPendingEvent(aProofOfLock);
839 : retVal =
840 1973 : mNormalQueue->GetEvent(reallyMayWait, aEvent, aProofOfLock);
841 1947 : if (aPriority) {
842 1878 : *aPriority = nsIRunnablePriority::PRIORITY_NORMAL;
843 : }
844 :
845 : // Let's see if we should next time process an event from the secondary
846 : // queue.
847 1947 : mProcessSecondaryQueueRunnable =
848 1947 : mSecondaryQueue->HasPendingEvent(aProofOfLock);
849 :
850 1947 : if (*aEvent) {
851 : // We got an event, return early.
852 1508 : return retVal;
853 : }
854 439 : } while(aMayWait || mProcessSecondaryQueueRunnable);
855 :
856 437 : return retVal;
857 : }
858 :
859 : //-----------------------------------------------------------------------------
860 : // nsIEventTarget
861 :
862 : NS_IMETHODIMP
863 6 : nsThread::DispatchFromScript(nsIRunnable* aEvent, uint32_t aFlags)
864 : {
865 12 : nsCOMPtr<nsIRunnable> event(aEvent);
866 12 : return Dispatch(event.forget(), aFlags);
867 : }
868 :
869 : NS_IMETHODIMP
870 1528 : nsThread::Dispatch(already_AddRefed<nsIRunnable> aEvent, uint32_t aFlags)
871 : {
872 1528 : LOG(("THRD(%p) Dispatch [%p %x]\n", this, /* XXX aEvent */nullptr, aFlags));
873 :
874 1528 : return DispatchInternal(Move(aEvent), aFlags, nullptr);
875 : }
876 :
877 : NS_IMETHODIMP
878 2 : nsThread::DelayedDispatch(already_AddRefed<nsIRunnable> aEvent, uint32_t aDelayMs)
879 : {
880 2 : NS_ENSURE_TRUE(!!aDelayMs, NS_ERROR_UNEXPECTED);
881 :
882 4 : RefPtr<DelayedRunnable> r = new DelayedRunnable(Move(do_AddRef(this)),
883 2 : Move(aEvent),
884 8 : aDelayMs);
885 2 : nsresult rv = r->Init();
886 2 : NS_ENSURE_SUCCESS(rv, rv);
887 :
888 2 : return DispatchInternal(r.forget(), 0, nullptr);
889 : }
890 :
891 : NS_IMETHODIMP
892 956 : nsThread::IsOnCurrentThread(bool* aResult)
893 : {
894 956 : *aResult = (PR_GetCurrentThread() == mThread);
895 956 : return NS_OK;
896 : }
897 :
898 : NS_IMETHODIMP_(bool)
899 0 : nsThread::IsOnCurrentThreadInfallible()
900 : {
901 : // Rely on mVirtualThread being correct.
902 0 : MOZ_CRASH("IsOnCurrentThreadInfallible should never be called on nsIThread");
903 : }
904 :
905 : //-----------------------------------------------------------------------------
906 : // nsIThread
907 :
908 : NS_IMETHODIMP
909 19 : nsThread::GetPRThread(PRThread** aResult)
910 : {
911 19 : *aResult = mThread;
912 19 : return NS_OK;
913 : }
914 :
915 : NS_IMETHODIMP
916 0 : nsThread::GetCanInvokeJS(bool* aResult)
917 : {
918 0 : *aResult = mCanInvokeJS;
919 0 : return NS_OK;
920 : }
921 :
922 : NS_IMETHODIMP
923 4 : nsThread::SetCanInvokeJS(bool aCanInvokeJS)
924 : {
925 4 : mCanInvokeJS = aCanInvokeJS;
926 4 : return NS_OK;
927 : }
928 :
929 : NS_IMETHODIMP
930 0 : nsThread::AsyncShutdown()
931 : {
932 0 : LOG(("THRD(%p) async shutdown\n", this));
933 :
934 : // XXX If we make this warn, then we hit that warning at xpcom shutdown while
935 : // shutting down a thread in a thread pool. That happens b/c the thread
936 : // in the thread pool is already shutdown by the thread manager.
937 0 : if (!mThread) {
938 0 : return NS_OK;
939 : }
940 :
941 0 : return !!ShutdownInternal(/* aSync = */ false) ? NS_OK : NS_ERROR_UNEXPECTED;
942 : }
943 :
944 : nsThreadShutdownContext*
945 1 : nsThread::ShutdownInternal(bool aSync)
946 : {
947 1 : MOZ_ASSERT(mThread);
948 1 : MOZ_ASSERT(mThread != PR_GetCurrentThread());
949 1 : if (NS_WARN_IF(mThread == PR_GetCurrentThread())) {
950 0 : return nullptr;
951 : }
952 :
953 : // Prevent multiple calls to this method
954 : {
955 2 : MutexAutoLock lock(mLock);
956 1 : if (!mShutdownRequired) {
957 0 : return nullptr;
958 : }
959 1 : mShutdownRequired = false;
960 : }
961 :
962 : NotNull<nsThread*> currentThread =
963 1 : WrapNotNull(nsThreadManager::get().GetCurrentThread());
964 :
965 : nsAutoPtr<nsThreadShutdownContext>& context =
966 1 : *currentThread->mRequestedShutdownContexts.AppendElement();
967 2 : context = new nsThreadShutdownContext(WrapNotNull(this), currentThread, aSync);
968 :
969 : // Set mShutdownContext and wake up the thread in case it is waiting for
970 : // events to process.
971 : nsCOMPtr<nsIRunnable> event =
972 3 : new nsThreadShutdownEvent(WrapNotNull(this), WrapNotNull(context.get()));
973 : // XXXroc What if posting the event fails due to OOM?
974 1 : PutEvent(event.forget(), nullptr);
975 :
976 : // We could still end up with other events being added after the shutdown
977 : // task, but that's okay because we process pending events in ThreadFunc
978 : // after setting mShutdownContext just before exiting.
979 1 : return context;
980 : }
981 :
982 : void
983 2 : nsThread::ShutdownComplete(NotNull<nsThreadShutdownContext*> aContext)
984 : {
985 2 : MOZ_ASSERT(mThread);
986 2 : MOZ_ASSERT(aContext->mTerminatingThread == this);
987 :
988 2 : if (aContext->mAwaitingShutdownAck) {
989 : // We're in a synchronous shutdown, so tell whatever is up the stack that
990 : // we're done and unwind the stack so it can call us again.
991 1 : aContext->mAwaitingShutdownAck = false;
992 1 : return;
993 : }
994 :
995 : // Now, it should be safe to join without fear of dead-locking.
996 :
997 1 : PR_JoinThread(mThread);
998 1 : mThread = nullptr;
999 :
1000 : // We hold strong references to our event observers, and once the thread is
1001 : // shut down the observers can't easily unregister themselves. Do it here
1002 : // to avoid leaking.
1003 1 : ClearObservers();
1004 :
1005 : #ifdef DEBUG
1006 : {
1007 2 : MutexAutoLock lock(mLock);
1008 1 : MOZ_ASSERT(!mObserver, "Should have been cleared at shutdown!");
1009 : }
1010 : #endif
1011 :
1012 : // Delete aContext.
1013 1 : MOZ_ALWAYS_TRUE(
1014 : aContext->mJoiningThread->mRequestedShutdownContexts.RemoveElement(aContext));
1015 : }
1016 :
1017 : void
1018 1 : nsThread::WaitForAllAsynchronousShutdowns()
1019 : {
1020 : // This is the motivating example for why SpinEventLoop has the template
1021 : // parameter we are providing here.
1022 3 : SpinEventLoopUntil<ProcessFailureBehavior::IgnoreAndContinue>([&]() {
1023 1 : return mRequestedShutdownContexts.IsEmpty();
1024 2 : }, this);
1025 1 : }
1026 :
1027 : NS_IMETHODIMP
1028 1 : nsThread::Shutdown()
1029 : {
1030 1 : LOG(("THRD(%p) sync shutdown\n", this));
1031 :
1032 : // XXX If we make this warn, then we hit that warning at xpcom shutdown while
1033 : // shutting down a thread in a thread pool. That happens b/c the thread
1034 : // in the thread pool is already shutdown by the thread manager.
1035 1 : if (!mThread) {
1036 0 : return NS_OK;
1037 : }
1038 :
1039 1 : nsThreadShutdownContext* maybeContext = ShutdownInternal(/* aSync = */ true);
1040 1 : NS_ENSURE_TRUE(maybeContext, NS_ERROR_UNEXPECTED);
1041 1 : NotNull<nsThreadShutdownContext*> context = WrapNotNull(maybeContext);
1042 :
1043 : // Process events on the current thread until we receive a shutdown ACK.
1044 : // Allows waiting; ensure no locks are held that would deadlock us!
1045 19 : SpinEventLoopUntil([&, context]() {
1046 17 : return !context->mAwaitingShutdownAck;
1047 19 : }, context->mJoiningThread);
1048 :
1049 1 : ShutdownComplete(context);
1050 :
1051 1 : return NS_OK;
1052 : }
1053 :
1054 : TimeStamp
1055 303 : nsThread::GetIdleDeadline()
1056 : {
1057 : // If we are shutting down, we won't honor the idle period, and we will
1058 : // always process idle runnables. This will ensure that the idle queue
1059 : // gets exhausted at shutdown time to prevent intermittently leaking
1060 : // some runnables inside that queue and even worse potentially leaving
1061 : // some important cleanup work unfinished.
1062 : // Note that we need to check both of these conditions since ShuttingDown()
1063 : // will never return true on the main thread, where gXPCOMThreadsShutDown
1064 : // performs a similar function.
1065 303 : if (gXPCOMThreadsShutDown || ShuttingDown()) {
1066 0 : return TimeStamp::Now();
1067 : }
1068 :
1069 303 : TimeStamp idleDeadline;
1070 : {
1071 : // Releasing the lock temporarily since getting the idle period
1072 : // might need to lock the timer thread. Unlocking here might make
1073 : // us receive an event on the main queue, but we've committed to
1074 : // run an idle event anyhow.
1075 606 : MutexAutoUnlock unlock(mLock);
1076 303 : mIdlePeriod->GetIdlePeriodHint(&idleDeadline);
1077 : }
1078 :
1079 : // If HasPendingEvents() has been called and it has returned true because of
1080 : // pending idle events, there is a risk that we may decide here that we aren't
1081 : // idle and return null, in which case HasPendingEvents() has effectively
1082 : // lied. Since we can't go back and fix the past, we have to adjust what we
1083 : // do here and forcefully pick the idle queue task here. Note that this means
1084 : // that we are choosing to run a task from the idle queue when we would
1085 : // normally decide that we aren't in an idle period, but this can only happen
1086 : // if we fall out of the idle period in between the call to HasPendingEvents()
1087 : // and here, which should hopefully be quite rare. We are effectively
1088 : // choosing to prioritize the sanity of our API semantics over the optimal
1089 : // scheduling.
1090 1207 : if (!mHasPendingEventsPromisedIdleEvent &&
1091 914 : (!idleDeadline || idleDeadline < TimeStamp::Now())) {
1092 149 : return TimeStamp();
1093 : }
1094 154 : if (mHasPendingEventsPromisedIdleEvent && !idleDeadline) {
1095 : // If HasPendingEvents() has been called and it has returned true, but we're no
1096 : // longer in the idle period, we must return a valid timestamp to pretend that
1097 : // we are still in the idle period.
1098 0 : return TimeStamp::Now();
1099 : }
1100 154 : return idleDeadline;
1101 : }
1102 :
1103 : NS_IMETHODIMP
1104 1709 : nsThread::HasPendingEvents(bool* aResult)
1105 : {
1106 1709 : if (NS_WARN_IF(PR_GetCurrentThread() != mThread)) {
1107 0 : return NS_ERROR_NOT_SAME_THREAD;
1108 : }
1109 :
1110 : {
1111 3418 : MutexAutoLock lock(mLock);
1112 1709 : mHasPendingEventsPromisedIdleEvent = false;
1113 1709 : bool hasPendingEvent = mEvents->HasPendingEvent(lock);
1114 1709 : bool hasPendingIdleEvent = false;
1115 1709 : if (!hasPendingEvent) {
1116 : // Note that GetIdleDeadline() checks mHasPendingEventsPromisedIdleEvent,
1117 : // but that's OK since we set it to false in the beginning of this method!
1118 297 : TimeStamp idleDeadline = GetIdleDeadline();
1119 :
1120 : // Only examine the idle queue if we are in an idle period.
1121 297 : if (idleDeadline) {
1122 148 : hasPendingIdleEvent = mIdleEvents.HasPendingEvent(lock);
1123 148 : mHasPendingEventsPromisedIdleEvent = hasPendingIdleEvent;
1124 : }
1125 : }
1126 1709 : *aResult = hasPendingEvent || hasPendingIdleEvent;
1127 : }
1128 1709 : return NS_OK;
1129 : }
1130 :
1131 : NS_IMETHODIMP
1132 3 : nsThread::RegisterIdlePeriod(already_AddRefed<nsIIdlePeriod> aIdlePeriod)
1133 : {
1134 3 : if (NS_WARN_IF(PR_GetCurrentThread() != mThread)) {
1135 0 : return NS_ERROR_NOT_SAME_THREAD;
1136 : }
1137 :
1138 6 : MutexAutoLock lock(mLock);
1139 3 : mIdlePeriod = aIdlePeriod;
1140 3 : return NS_OK;
1141 : }
1142 :
1143 : NS_IMETHODIMP
1144 14 : nsThread::IdleDispatch(already_AddRefed<nsIRunnable> aEvent)
1145 : {
1146 : // Currently the only supported idle dispatch is from the same
1147 : // thread. To support idle dispatch from another thread we need to
1148 : // support waking threads that are waiting for an event queue that
1149 : // isn't mIdleEvents.
1150 14 : MOZ_ASSERT(PR_GetCurrentThread() == mThread);
1151 :
1152 28 : MutexAutoLock lock(mLock);
1153 14 : LeakRefPtr<nsIRunnable> event(Move(aEvent));
1154 :
1155 14 : if (NS_WARN_IF(!event)) {
1156 0 : return NS_ERROR_INVALID_ARG;
1157 : }
1158 :
1159 14 : if (mEventsAreDoomed) {
1160 0 : NS_WARNING("An idle event was posted to a thread that will never run it (rejected)");
1161 0 : return NS_ERROR_UNEXPECTED;
1162 : }
1163 :
1164 14 : mIdleEvents.PutEvent(event.take(), lock);
1165 14 : return NS_OK;
1166 : }
1167 :
1168 : #ifdef MOZ_CANARY
1169 : void canary_alarm_handler(int signum);
1170 :
1171 : class Canary
1172 : {
1173 : //XXX ToDo: support nested loops
1174 : public:
1175 : Canary()
1176 : {
1177 : if (sCanaryOutputFD > 0 && EventLatencyIsImportant()) {
1178 : signal(SIGALRM, canary_alarm_handler);
1179 : ualarm(15000, 0);
1180 : }
1181 : }
1182 :
1183 : ~Canary()
1184 : {
1185 : if (sCanaryOutputFD != 0 && EventLatencyIsImportant()) {
1186 : ualarm(0, 0);
1187 : }
1188 : }
1189 :
1190 : static bool EventLatencyIsImportant()
1191 : {
1192 : return NS_IsMainThread() && XRE_IsParentProcess();
1193 : }
1194 : };
1195 :
1196 : void canary_alarm_handler(int signum)
1197 : {
1198 : void* array[30];
1199 : const char msg[29] = "event took too long to run:\n";
1200 : // use write to be safe in the signal handler
1201 : write(sCanaryOutputFD, msg, sizeof(msg));
1202 : backtrace_symbols_fd(array, backtrace(array, 30), sCanaryOutputFD);
1203 : }
1204 :
1205 : #endif
1206 :
1207 : #define NOTIFY_EVENT_OBSERVERS(func_, params_) \
1208 : do { \
1209 : if (!mEventObservers.IsEmpty()) { \
1210 : nsAutoTObserverArray<NotNull<nsCOMPtr<nsIThreadObserver>>, 2>::ForwardIterator \
1211 : iter_(mEventObservers); \
1212 : nsCOMPtr<nsIThreadObserver> obs_; \
1213 : while (iter_.HasMore()) { \
1214 : obs_ = iter_.GetNext(); \
1215 : obs_ -> func_ params_ ; \
1216 : } \
1217 : } \
1218 : } while(0)
1219 :
1220 : void
1221 424 : nsThread::GetIdleEvent(nsIRunnable** aEvent, MutexAutoLock& aProofOfLock)
1222 : {
1223 424 : MOZ_ASSERT(PR_GetCurrentThread() == mThread);
1224 424 : MOZ_ASSERT(aEvent);
1225 :
1226 424 : if (!mIdleEvents.HasPendingEvent(aProofOfLock)) {
1227 418 : MOZ_ASSERT(!mHasPendingEventsPromisedIdleEvent);
1228 418 : aEvent = nullptr;
1229 836 : return;
1230 : }
1231 :
1232 6 : TimeStamp idleDeadline = GetIdleDeadline();
1233 6 : if (!idleDeadline) {
1234 0 : aEvent = nullptr;
1235 0 : return;
1236 : }
1237 :
1238 6 : mIdleEvents.GetEvent(false, aEvent, aProofOfLock);
1239 :
1240 6 : if (*aEvent) {
1241 12 : nsCOMPtr<nsIIdleRunnable> idleEvent(do_QueryInterface(*aEvent));
1242 6 : if (idleEvent) {
1243 4 : idleEvent->SetDeadline(idleDeadline);
1244 : }
1245 :
1246 : #ifndef RELEASE_OR_BETA
1247 : // Store the next idle deadline to be able to determine budget use
1248 : // in ProcessNextEvent.
1249 6 : mNextIdleDeadline = idleDeadline;
1250 : #endif
1251 : }
1252 : }
1253 :
1254 : void
1255 1823 : nsThread::GetEvent(bool aWait, nsIRunnable** aEvent,
1256 : unsigned short* aPriority,
1257 : MutexAutoLock& aProofOfLock)
1258 : {
1259 1823 : MOZ_ASSERT(PR_GetCurrentThread() == mThread);
1260 1823 : MOZ_ASSERT(aEvent);
1261 :
1262 1823 : MakeScopeExit([&] {
1263 1823 : mHasPendingEventsPromisedIdleEvent = false;
1264 3646 : });
1265 :
1266 : #ifndef RELEASE_OR_BETA
1267 : // Clear mNextIdleDeadline so that it is possible to determine that
1268 : // we're running an idle runnable in ProcessNextEvent.
1269 1823 : mNextIdleDeadline = TimeStamp();
1270 : #endif
1271 :
1272 : // We'll try to get an event to execute in three stages.
1273 : // [1] First we just try to get it from the regular queue without waiting.
1274 1823 : mEvents->GetEvent(false, aEvent, aPriority, aProofOfLock);
1275 :
1276 : // [2] If we didn't get an event from the regular queue, try to
1277 : // get one from the idle queue
1278 1823 : if (!*aEvent) {
1279 : // Since events in mEvents have higher priority than idle
1280 : // events, we will only consider idle events when there are no
1281 : // pending events in mEvents. We will for the same reason never
1282 : // wait for an idle event, since a higher priority event might
1283 : // appear at any time.
1284 424 : GetIdleEvent(aEvent, aProofOfLock);
1285 :
1286 424 : if (*aEvent && aPriority) {
1287 : // Idle events count as normal priority.
1288 6 : *aPriority = nsIRunnablePriority::PRIORITY_NORMAL;
1289 : }
1290 : }
1291 :
1292 : // [3] If we neither got an event from the regular queue nor the
1293 : // idle queue, then if we should wait for events we block on the
1294 : // main queue until an event is available.
1295 : // If we are shutting down, then do not wait for new events.
1296 1823 : if (!*aEvent && aWait) {
1297 152 : mEvents->GetEvent(aWait, aEvent, aPriority, aProofOfLock);
1298 : }
1299 1797 : }
1300 :
1301 : #ifndef RELEASE_OR_BETA
1302 : static bool
1303 1157 : GetLabeledRunnableName(nsIRunnable* aEvent, nsACString& aName)
1304 : {
1305 1157 : bool labeled = false;
1306 2314 : if (RefPtr<SchedulerGroup::Runnable> groupRunnable = do_QueryObject(aEvent)) {
1307 129 : labeled = true;
1308 129 : MOZ_ALWAYS_TRUE(NS_SUCCEEDED(groupRunnable->GetName(aName)));
1309 2056 : } else if (nsCOMPtr<nsINamed> named = do_QueryInterface(aEvent)) {
1310 1021 : MOZ_ALWAYS_TRUE(NS_SUCCEEDED(named->GetName(aName)));
1311 : } else {
1312 7 : aName.AssignLiteral("non-nsINamed runnable");
1313 : }
1314 1157 : if (aName.IsEmpty()) {
1315 0 : aName.AssignLiteral("anonymous runnable");
1316 : }
1317 :
1318 1157 : return labeled;
1319 : }
1320 : #endif
1321 :
1322 : NS_IMETHODIMP
1323 1822 : nsThread::ProcessNextEvent(bool aMayWait, bool* aResult)
1324 : {
1325 1822 : LOG(("THRD(%p) ProcessNextEvent [%u %u]\n", this, aMayWait,
1326 : mNestedEventLoopDepth));
1327 :
1328 1822 : if (NS_WARN_IF(PR_GetCurrentThread() != mThread)) {
1329 0 : return NS_ERROR_NOT_SAME_THREAD;
1330 : }
1331 :
1332 : // The toplevel event loop normally blocks waiting for the next event, but
1333 : // if we're trying to shut this thread down, we must exit the event loop when
1334 : // the event queue is empty.
1335 : // This only applys to the toplevel event loop! Nested event loops (e.g.
1336 : // during sync dispatch) are waiting for some state change and must be able
1337 : // to block even if something has requested shutdown of the thread. Otherwise
1338 : // we'll just busywait as we endlessly look for an event, fail to find one,
1339 : // and repeat the nested event loop since its state change hasn't happened yet.
1340 1822 : bool reallyWait = aMayWait && (mNestedEventLoopDepth > 0 || !ShuttingDown());
1341 :
1342 3583 : Maybe<SchedulerGroup::AutoProcessEvent> ape;
1343 1822 : if (mIsMainThread == MAIN_THREAD) {
1344 1230 : DoMainThreadSpecificProcessing(reallyWait);
1345 1230 : ape.emplace();
1346 : }
1347 :
1348 1822 : ++mNestedEventLoopDepth;
1349 :
1350 : // We only want to create an AutoNoJSAPI on threads that actually do DOM stuff
1351 : // (including workers). Those are exactly the threads that have an
1352 : // mScriptObserver.
1353 3583 : Maybe<dom::AutoNoJSAPI> noJSAPI;
1354 1823 : bool callScriptObserver = !!mScriptObserver;
1355 1823 : if (callScriptObserver) {
1356 1265 : noJSAPI.emplace();
1357 1265 : mScriptObserver->BeforeProcessTask(reallyWait);
1358 : }
1359 :
1360 3584 : nsCOMPtr<nsIThreadObserver> obs = mObserver;
1361 1823 : if (obs) {
1362 1419 : obs->OnProcessNextEvent(this, reallyWait);
1363 : }
1364 :
1365 1823 : NOTIFY_EVENT_OBSERVERS(OnProcessNextEvent, (this, reallyWait));
1366 :
1367 : #ifdef MOZ_CANARY
1368 : Canary canary;
1369 : #endif
1370 1823 : nsresult rv = NS_OK;
1371 :
1372 : {
1373 : // Scope for |event| to make sure that its destructor fires while
1374 : // mNestedEventLoopDepth has been incremented, since that destructor can
1375 : // also do work.
1376 3583 : nsCOMPtr<nsIRunnable> event;
1377 : unsigned short priority;
1378 : {
1379 3620 : MutexAutoLock lock(mLock);
1380 1823 : GetEvent(reallyWait, getter_AddRefs(event), &priority, lock);
1381 : }
1382 :
1383 1797 : *aResult = (event.get() != nullptr);
1384 :
1385 1797 : if (event) {
1386 1531 : LOG(("THRD(%p) running [%p]\n", this, event.get()));
1387 :
1388 1531 : if (MAIN_THREAD == mIsMainThread) {
1389 1157 : HangMonitor::NotifyActivity();
1390 : }
1391 :
1392 : #ifndef RELEASE_OR_BETA
1393 3026 : Maybe<Telemetry::AutoTimer<Telemetry::MAIN_THREAD_RUNNABLE_MS>> timer;
1394 3026 : Maybe<Telemetry::AutoTimer<Telemetry::IDLE_RUNNABLE_BUDGET_OVERUSE_MS>> idleTimer;
1395 :
1396 3026 : nsAutoCString name;
1397 1531 : if ((MAIN_THREAD == mIsMainThread) || mNextIdleDeadline) {
1398 1157 : bool labeled = GetLabeledRunnableName(event, name);
1399 :
1400 1157 : if (MAIN_THREAD == mIsMainThread) {
1401 1157 : timer.emplace(name);
1402 :
1403 : // High-priority runnables are ignored here since they'll run right away
1404 : // even with the cooperative scheduler.
1405 1157 : if (!labeled && priority == nsIRunnablePriority::PRIORITY_NORMAL) {
1406 955 : TimeStamp now = TimeStamp::Now();
1407 955 : double diff = (now - mLastUnlabeledRunnable).ToMilliseconds();
1408 955 : Telemetry::Accumulate(Telemetry::TIME_BETWEEN_UNLABELED_RUNNABLES_MS, diff);
1409 955 : mLastUnlabeledRunnable = now;
1410 : }
1411 : }
1412 :
1413 1157 : if (mNextIdleDeadline) {
1414 : // If we construct the AutoTimer with the deadline, then we'll
1415 : // compute TimeStamp::Now() - mNextIdleDeadline when
1416 : // accumulating telemetry. If that is positive we've
1417 : // overdrawn our idle budget, if it's negative it will go in
1418 : // the 0 bucket of the histogram.
1419 6 : idleTimer.emplace(name, mNextIdleDeadline);
1420 : }
1421 : }
1422 :
1423 : // If we're on the main thread, we want to record our current runnable's
1424 : // name in a static so that BHR can record it.
1425 1531 : const char* restoreRunnableName = nullptr;
1426 1495 : auto clear = MakeScopeExit([&] {
1427 1495 : if (MAIN_THREAD == mIsMainThread) {
1428 1154 : sMainThreadRunnableName = restoreRunnableName;
1429 : }
1430 4521 : });
1431 1531 : if (MAIN_THREAD == mIsMainThread) {
1432 1157 : restoreRunnableName = sMainThreadRunnableName;
1433 1157 : sMainThreadRunnableName = name.get();
1434 : }
1435 : #endif
1436 :
1437 1531 : event->Run();
1438 266 : } else if (aMayWait) {
1439 0 : MOZ_ASSERT(ShuttingDown(),
1440 : "This should only happen when shutting down");
1441 0 : rv = NS_ERROR_UNEXPECTED;
1442 : }
1443 : }
1444 :
1445 1761 : NOTIFY_EVENT_OBSERVERS(AfterProcessNextEvent, (this, *aResult));
1446 :
1447 1761 : if (obs) {
1448 1416 : obs->AfterProcessNextEvent(this, *aResult);
1449 : }
1450 :
1451 1761 : if (callScriptObserver) {
1452 1258 : if (mScriptObserver) {
1453 1258 : mScriptObserver->AfterProcessTask(mNestedEventLoopDepth);
1454 : }
1455 1258 : noJSAPI.reset();
1456 : }
1457 :
1458 1761 : --mNestedEventLoopDepth;
1459 :
1460 1761 : return rv;
1461 : }
1462 :
1463 : //-----------------------------------------------------------------------------
1464 : // nsISupportsPriority
1465 :
1466 : NS_IMETHODIMP
1467 0 : nsThread::GetPriority(int32_t* aPriority)
1468 : {
1469 0 : *aPriority = mPriority;
1470 0 : return NS_OK;
1471 : }
1472 :
1473 : NS_IMETHODIMP
1474 1 : nsThread::SetPriority(int32_t aPriority)
1475 : {
1476 1 : if (NS_WARN_IF(!mThread)) {
1477 0 : return NS_ERROR_NOT_INITIALIZED;
1478 : }
1479 :
1480 : // NSPR defines the following four thread priorities:
1481 : // PR_PRIORITY_LOW
1482 : // PR_PRIORITY_NORMAL
1483 : // PR_PRIORITY_HIGH
1484 : // PR_PRIORITY_URGENT
1485 : // We map the priority values defined on nsISupportsPriority to these values.
1486 :
1487 1 : mPriority = aPriority;
1488 :
1489 : PRThreadPriority pri;
1490 1 : if (mPriority <= PRIORITY_HIGHEST) {
1491 0 : pri = PR_PRIORITY_URGENT;
1492 1 : } else if (mPriority < PRIORITY_NORMAL) {
1493 0 : pri = PR_PRIORITY_HIGH;
1494 1 : } else if (mPriority > PRIORITY_NORMAL) {
1495 0 : pri = PR_PRIORITY_LOW;
1496 : } else {
1497 1 : pri = PR_PRIORITY_NORMAL;
1498 : }
1499 : // If chaos mode is active, retain the randomly chosen priority
1500 1 : if (!ChaosMode::isActive(ChaosFeature::ThreadScheduling)) {
1501 1 : PR_SetThreadPriority(mThread, pri);
1502 : }
1503 :
1504 1 : return NS_OK;
1505 : }
1506 :
1507 : NS_IMETHODIMP
1508 0 : nsThread::AdjustPriority(int32_t aDelta)
1509 : {
1510 0 : return SetPriority(mPriority + aDelta);
1511 : }
1512 :
1513 : //-----------------------------------------------------------------------------
1514 : // nsIThreadInternal
1515 :
1516 : NS_IMETHODIMP
1517 0 : nsThread::GetObserver(nsIThreadObserver** aObs)
1518 : {
1519 0 : MutexAutoLock lock(mLock);
1520 0 : NS_IF_ADDREF(*aObs = mObserver);
1521 0 : return NS_OK;
1522 : }
1523 :
1524 : NS_IMETHODIMP
1525 9 : nsThread::SetObserver(nsIThreadObserver* aObs)
1526 : {
1527 9 : if (NS_WARN_IF(PR_GetCurrentThread() != mThread)) {
1528 0 : return NS_ERROR_NOT_SAME_THREAD;
1529 : }
1530 :
1531 18 : MutexAutoLock lock(mLock);
1532 9 : mObserver = aObs;
1533 9 : return NS_OK;
1534 : }
1535 :
1536 : uint32_t
1537 307 : nsThread::RecursionDepth() const
1538 : {
1539 307 : MOZ_ASSERT(PR_GetCurrentThread() == mThread);
1540 307 : return mNestedEventLoopDepth;
1541 : }
1542 :
1543 : NS_IMETHODIMP
1544 12 : nsThread::AddObserver(nsIThreadObserver* aObserver)
1545 : {
1546 12 : if (NS_WARN_IF(!aObserver)) {
1547 0 : return NS_ERROR_INVALID_ARG;
1548 : }
1549 12 : if (NS_WARN_IF(PR_GetCurrentThread() != mThread)) {
1550 0 : return NS_ERROR_NOT_SAME_THREAD;
1551 : }
1552 :
1553 12 : NS_WARNING_ASSERTION(!mEventObservers.Contains(aObserver),
1554 : "Adding an observer twice!");
1555 :
1556 12 : if (!mEventObservers.AppendElement(WrapNotNull(aObserver))) {
1557 0 : NS_WARNING("Out of memory!");
1558 0 : return NS_ERROR_OUT_OF_MEMORY;
1559 : }
1560 :
1561 12 : return NS_OK;
1562 : }
1563 :
1564 : NS_IMETHODIMP
1565 11 : nsThread::RemoveObserver(nsIThreadObserver* aObserver)
1566 : {
1567 11 : if (NS_WARN_IF(PR_GetCurrentThread() != mThread)) {
1568 0 : return NS_ERROR_NOT_SAME_THREAD;
1569 : }
1570 :
1571 11 : if (aObserver && !mEventObservers.RemoveElement(aObserver)) {
1572 0 : NS_WARNING("Removing an observer that was never added!");
1573 : }
1574 :
1575 11 : return NS_OK;
1576 : }
1577 :
1578 : NS_IMETHODIMP
1579 17 : nsThread::PushEventQueue(nsIEventTarget** aResult)
1580 : {
1581 17 : if (NS_WARN_IF(PR_GetCurrentThread() != mThread)) {
1582 0 : return NS_ERROR_NOT_SAME_THREAD;
1583 : }
1584 :
1585 : NotNull<nsChainedEventQueue*> queue =
1586 17 : WrapNotNull(new nsChainedEventQueue(mLock));
1587 34 : queue->mEventTarget = new nsNestedEventTarget(WrapNotNull(this), queue);
1588 :
1589 : {
1590 34 : MutexAutoLock lock(mLock);
1591 17 : queue->mNext = mEvents;
1592 17 : mEvents = queue;
1593 : }
1594 :
1595 17 : NS_ADDREF(*aResult = queue->mEventTarget);
1596 17 : return NS_OK;
1597 : }
1598 :
1599 : NS_IMETHODIMP
1600 13 : nsThread::PopEventQueue(nsIEventTarget* aInnermostTarget)
1601 : {
1602 13 : if (NS_WARN_IF(PR_GetCurrentThread() != mThread)) {
1603 0 : return NS_ERROR_NOT_SAME_THREAD;
1604 : }
1605 :
1606 13 : if (NS_WARN_IF(!aInnermostTarget)) {
1607 0 : return NS_ERROR_NULL_POINTER;
1608 : }
1609 :
1610 : // Don't delete or release anything while holding the lock.
1611 26 : nsAutoPtr<nsChainedEventQueue> queue;
1612 26 : RefPtr<nsNestedEventTarget> target;
1613 :
1614 : {
1615 26 : MutexAutoLock lock(mLock);
1616 :
1617 : // Make sure we're popping the innermost event target.
1618 13 : if (NS_WARN_IF(mEvents->mEventTarget != aInnermostTarget)) {
1619 0 : return NS_ERROR_UNEXPECTED;
1620 : }
1621 :
1622 13 : MOZ_ASSERT(mEvents != &mEventsRoot);
1623 :
1624 13 : queue = mEvents;
1625 13 : mEvents = WrapNotNull(mEvents->mNext);
1626 :
1627 26 : nsCOMPtr<nsIRunnable> event;
1628 13 : while (queue->GetEvent(false, getter_AddRefs(event), nullptr, lock)) {
1629 0 : mEvents->PutEvent(event.forget(), lock);
1630 : }
1631 :
1632 : // Don't let the event target post any more events.
1633 13 : queue->mEventTarget.swap(target);
1634 13 : target->mQueue = nullptr;
1635 : }
1636 :
1637 13 : return NS_OK;
1638 : }
1639 :
1640 : void
1641 4 : nsThread::SetScriptObserver(mozilla::CycleCollectedJSContext* aScriptObserver)
1642 : {
1643 4 : if (!aScriptObserver) {
1644 0 : mScriptObserver = nullptr;
1645 0 : return;
1646 : }
1647 :
1648 4 : MOZ_ASSERT(!mScriptObserver);
1649 4 : mScriptObserver = aScriptObserver;
1650 : }
1651 :
1652 : void
1653 1230 : nsThread::DoMainThreadSpecificProcessing(bool aReallyWait)
1654 : {
1655 1230 : MOZ_ASSERT(mIsMainThread == MAIN_THREAD);
1656 :
1657 1230 : ipc::CancelCPOWs();
1658 :
1659 1230 : if (aReallyWait) {
1660 89 : HangMonitor::Suspend();
1661 : }
1662 :
1663 : // Fire a memory pressure notification, if one is pending.
1664 1230 : if (!ShuttingDown()) {
1665 1230 : MemoryPressureState mpPending = NS_GetPendingMemoryPressure();
1666 1230 : if (mpPending != MemPressure_None) {
1667 0 : nsCOMPtr<nsIObserverService> os = services::GetObserverService();
1668 :
1669 0 : if (os) {
1670 : // Use no-forward to prevent the notifications from being transferred to
1671 : // the children of this process.
1672 0 : os->NotifyObservers(nullptr, "memory-pressure",
1673 : mpPending == MemPressure_New ? u"low-memory-no-forward" :
1674 0 : u"low-memory-ongoing-no-forward");
1675 : } else {
1676 0 : NS_WARNING("Can't get observer service!");
1677 : }
1678 : }
1679 : }
1680 :
1681 : #ifdef MOZ_CRASHREPORTER
1682 1230 : if (!ShuttingDown()) {
1683 1230 : SaveMemoryReportNearOOM(ShouldSaveMemoryReport::kMaybeReport);
1684 : }
1685 : #endif
1686 1230 : }
1687 :
1688 : NS_IMETHODIMP
1689 0 : nsThread::GetEventTarget(nsIEventTarget** aEventTarget)
1690 : {
1691 0 : nsCOMPtr<nsIEventTarget> target = this;
1692 0 : target.forget(aEventTarget);
1693 0 : return NS_OK;
1694 : }
1695 :
1696 : nsIEventTarget*
1697 704 : nsThread::EventTarget()
1698 : {
1699 704 : return this;
1700 : }
1701 :
1702 : nsISerialEventTarget*
1703 52 : nsThread::SerialEventTarget()
1704 : {
1705 52 : return this;
1706 : }
1707 :
1708 : //-----------------------------------------------------------------------------
1709 :
1710 209 : NS_IMPL_ISUPPORTS(nsThread::nsNestedEventTarget, nsIEventTarget)
1711 :
1712 : NS_IMETHODIMP
1713 0 : nsThread::nsNestedEventTarget::DispatchFromScript(nsIRunnable* aEvent, uint32_t aFlags)
1714 : {
1715 0 : nsCOMPtr<nsIRunnable> event(aEvent);
1716 0 : return Dispatch(event.forget(), aFlags);
1717 : }
1718 :
1719 : NS_IMETHODIMP
1720 36 : nsThread::nsNestedEventTarget::Dispatch(already_AddRefed<nsIRunnable> aEvent, uint32_t aFlags)
1721 : {
1722 36 : LOG(("THRD(%p) Dispatch [%p %x] to nested loop %p\n", mThread.get().get(),
1723 : /*XXX aEvent*/ nullptr, aFlags, this));
1724 :
1725 36 : return mThread->DispatchInternal(Move(aEvent), aFlags, this);
1726 : }
1727 :
1728 : NS_IMETHODIMP
1729 0 : nsThread::nsNestedEventTarget::DelayedDispatch(already_AddRefed<nsIRunnable>, uint32_t)
1730 : {
1731 0 : return NS_ERROR_NOT_IMPLEMENTED;
1732 : }
1733 :
1734 : NS_IMETHODIMP
1735 0 : nsThread::nsNestedEventTarget::IsOnCurrentThread(bool* aResult)
1736 : {
1737 0 : return mThread->IsOnCurrentThread(aResult);
1738 : }
1739 :
1740 : NS_IMETHODIMP_(bool)
1741 0 : nsThread::nsNestedEventTarget::IsOnCurrentThreadInfallible()
1742 : {
1743 0 : return mThread->IsOnCurrentThread();
1744 : }
|