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/. */
10 #include "MainThreadUtils.h"
11 #include "mozilla/AlreadyAddRefed.h"
12 #include "mozilla/Atomics.h"
13 #include "mozilla/Attributes.h"
14 #include "mozilla/DataMutex.h"
15 #include "mozilla/EventQueue.h"
16 #include "mozilla/LinkedList.h"
17 #include "mozilla/MemoryReporting.h"
18 #include "mozilla/Mutex.h"
19 #include "mozilla/NotNull.h"
20 #include "mozilla/RefPtr.h"
21 #include "mozilla/TaskDispatcher.h"
22 #include "mozilla/TimeStamp.h"
23 #include "mozilla/UniquePtr.h"
24 #include "nsIDirectTaskDispatcher.h"
25 #include "nsIEventTarget.h"
26 #include "nsISerialEventTarget.h"
27 #include "nsISupportsPriority.h"
28 #include "nsIThread.h"
29 #include "nsIThreadInternal.h"
33 class CycleCollectedJSContext
;
34 class DelayedRunnable
;
35 class SynchronizedEventQueue
;
36 class ThreadEventQueue
;
37 class ThreadEventTarget
;
39 template <typename T
, size_t Length
>
41 } // namespace mozilla
43 using mozilla::NotNull
;
46 class nsThreadShutdownContext
;
48 // See https://www.w3.org/TR/longtasks
49 #define W3_LONGTASK_BUSY_WINDOW_MS 50
51 // Time a Runnable executes before we accumulate telemetry on it
52 #define LONGTASK_TELEMETRY_MS 30
54 // A class for managing performance counter state.
56 class PerformanceCounterState
{
58 explicit PerformanceCounterState(
59 const uint32_t& aNestedEventLoopDepthRef
, bool aIsMainThread
= false,
60 const Maybe
<uint32_t>& aLongTaskLength
= Nothing())
61 : mNestedEventLoopDepth(aNestedEventLoopDepthRef
),
62 mIsMainThread(aIsMainThread
),
63 mLongTaskLength(aLongTaskLength
),
64 // Does it really make sense to initialize these to "now" when we
65 // haven't run any tasks?
66 mLastLongTaskEnd(TimeStamp::Now()),
67 mLastLongNonIdleTaskEnd(mLastLongTaskEnd
) {}
71 Snapshot(uint32_t aOldEventLoopDepth
, bool aOldIsIdleRunnable
)
72 : mOldEventLoopDepth(aOldEventLoopDepth
),
73 mOldIsIdleRunnable(aOldIsIdleRunnable
) {}
75 Snapshot(const Snapshot
&) = default;
76 Snapshot(Snapshot
&&) = default;
79 friend class PerformanceCounterState
;
81 const uint32_t mOldEventLoopDepth
;
82 const bool mOldIsIdleRunnable
;
85 // Notification that a runnable is about to run. This captures a snapshot of
86 // our current state before we reset to prepare for the new runnable. This
87 // muast be called after mNestedEventLoopDepth has been incremented for the
88 // runnable execution. The performance counter passed in should be the one
89 // for the relevant runnable and may be null. aIsIdleRunnable should be true
90 // if and only if the runnable has idle priority.
91 Snapshot
RunnableWillRun(TimeStamp aNow
, bool aIsIdleRunnable
);
93 // Notification that a runnable finished executing. This must be passed the
94 // snapshot that RunnableWillRun returned for the same runnable. This must be
95 // called before mNestedEventLoopDepth is decremented after the runnable's
97 void RunnableDidRun(const nsCString
& aName
, Snapshot
&& aSnapshot
);
99 const TimeStamp
& LastLongTaskEnd() const { return mLastLongTaskEnd
; }
100 const TimeStamp
& LastLongNonIdleTaskEnd() const {
101 return mLastLongNonIdleTaskEnd
;
105 // Called to report accumulated time, as needed, when we're about to run a
106 // runnable or just finished running one.
107 void MaybeReportAccumulatedTime(const nsCString
& aName
, TimeStamp aNow
);
109 // Whether the runnable we are about to run, or just ran, is a nested
110 // runnable, in the sense that there is some other runnable up the stack
111 // spinning the event loop. This must be called before we change our
112 // mCurrentEventLoopDepth (when about to run a new event) or after we restore
113 // it (after we ran one).
114 bool IsNestedRunnable() const {
115 return mNestedEventLoopDepth
> mCurrentEventLoopDepth
;
118 // The event loop depth of the currently running runnable. Set to the max
119 // value of a uint32_t when there is no runnable running, so when starting to
120 // run a toplevel (not nested) runnable IsNestedRunnable() will test false.
121 uint32_t mCurrentEventLoopDepth
= std::numeric_limits
<uint32_t>::max();
123 // A reference to the nsThread's mNestedEventLoopDepth, so we can
124 // see what it is right now.
125 const uint32_t& mNestedEventLoopDepth
;
127 // A boolean that indicates whether the currently running runnable is an idle
128 // runnable. Only has a useful value between RunnableWillRun() being called
129 // and RunnableDidRun() returning.
130 bool mCurrentRunnableIsIdleRunnable
= false;
132 // Whether we're attached to the mainthread nsThread.
133 const bool mIsMainThread
;
135 // what is considered a LongTask (in ms)
136 const Maybe
<uint32_t> mLongTaskLength
;
138 // The timestamp from which time to be accounted for should be measured. This
139 // can be the start of a runnable running or the end of a nested runnable
141 TimeStamp mCurrentTimeSliceStart
;
143 // Information about when long tasks last ended.
144 TimeStamp mLastLongTaskEnd
;
145 TimeStamp mLastLongNonIdleTaskEnd
;
147 } // namespace mozilla
150 class nsThread
: public nsIThreadInternal
,
151 public nsISupportsPriority
,
152 public nsIDirectTaskDispatcher
,
153 private mozilla::LinkedListElement
<nsThread
> {
154 friend mozilla::LinkedList
<nsThread
>;
155 friend mozilla::LinkedListElement
<nsThread
>;
158 NS_DECL_THREADSAFE_ISUPPORTS
159 NS_DECL_NSIEVENTTARGET_FULL
161 NS_DECL_NSITHREADINTERNAL
162 NS_DECL_NSISUPPORTSPRIORITY
163 NS_DECL_NSIDIRECTTASKDISPATCHER
165 enum MainThreadFlag
{ MAIN_THREAD
, NOT_MAIN_THREAD
};
167 nsThread(NotNull
<mozilla::SynchronizedEventQueue
*> aQueue
,
168 MainThreadFlag aMainThread
,
169 nsIThreadManager::ThreadCreationOptions aOptions
);
175 // Initialize this as a named wrapper for a new PRThread.
176 nsresult
Init(const nsACString
& aName
);
178 // Initialize this as a wrapper for the current PRThread.
179 nsresult
InitCurrentThread();
181 // Get this thread's name, thread-safe.
182 void GetThreadName(nsACString
& aNameBuffer
);
184 // Set this thread's name. Consider using
185 // NS_SetCurrentThreadName if you are not sure.
186 void SetThreadNameInternal(const nsACString
& aName
);
189 // Initializes the mThreadId and stack base/size members, and adds the thread
190 // to the ThreadList().
194 // The PRThread corresponding to this thread.
195 PRThread
* GetPRThread() const { return mThread
; }
197 const void* StackBase() const { return mStackBase
; }
198 size_t StackSize() const { return mStackSize
; }
200 uint32_t ThreadId() const { return mThreadId
; }
202 // If this flag is true, then the nsThread was created using
203 // nsIThreadManager::NewThread.
204 bool ShutdownRequired() { return mShutdownRequired
; }
206 // Lets GetRunningEventDelay() determine if the pool this is part
207 // of has an unstarted thread
208 void SetPoolThreadFreePtr(mozilla::Atomic
<bool, mozilla::Relaxed
>* aPtr
) {
209 mIsAPoolThreadFree
= aPtr
;
212 void SetScriptObserver(mozilla::CycleCollectedJSContext
* aScriptObserver
);
214 uint32_t RecursionDepth() const;
216 void ShutdownComplete(NotNull
<nsThreadShutdownContext
*> aContext
);
218 void WaitForAllAsynchronousShutdowns();
220 static const uint32_t kRunnableNameBufSize
= 1000;
221 static mozilla::Array
<char, kRunnableNameBufSize
> sMainThreadRunnableName
;
223 mozilla::SynchronizedEventQueue
* EventQueue() { return mEvents
.get(); }
225 bool ShuttingDown() const { return mShutdownContext
!= nullptr; }
227 size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf
) const;
229 // Returns the size of this object, its PRThread, and its shutdown contexts,
230 // but excluding its event queues.
231 size_t ShallowSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf
) const;
233 size_t SizeOfEventQueues(mozilla::MallocSizeOf aMallocSizeOf
) const;
235 void SetUseHangMonitor(bool aValue
) {
236 MOZ_ASSERT(IsOnCurrentThread());
237 mUseHangMonitor
= aValue
;
241 void DoMainThreadSpecificProcessing() const;
244 friend class nsThreadShutdownEvent
;
248 static void ThreadFunc(void* aArg
);
251 already_AddRefed
<nsIThreadObserver
> GetObserver() {
252 nsIThreadObserver
* obs
;
253 nsThread::GetObserver(&obs
);
254 return already_AddRefed
<nsIThreadObserver
>(obs
);
257 already_AddRefed
<nsThreadShutdownContext
> ShutdownInternal(bool aSync
);
259 friend class nsThreadManager
;
260 friend class nsThreadPool
;
262 void MaybeRemoveFromThreadList();
264 // Whether or not these members have a value determines whether the nsThread
265 // is treated as a full XPCOM thread or as a thin wrapper.
267 // For full nsThreads, they will always contain valid pointers. For thin
268 // wrappers around non-XPCOM threads, they will be null, and event dispatch
269 // methods which rely on them will fail (and assert) if called.
270 RefPtr
<mozilla::SynchronizedEventQueue
> mEvents
;
271 RefPtr
<mozilla::ThreadEventTarget
> mEventTarget
;
273 // The number of outstanding nsThreadShutdownContext started by this thread.
274 // The thread will not be allowed to exit until this number reaches 0.
275 uint32_t mOutstandingShutdownContexts
;
276 // The shutdown context for ourselves.
277 RefPtr
<nsThreadShutdownContext
> mShutdownContext
;
279 mozilla::CycleCollectedJSContext
* mScriptObserver
;
282 mozilla::DataMutex
<nsCString
> mThreadName
;
284 void* mStackBase
= nullptr;
288 uint32_t mNestedEventLoopDepth
;
290 mozilla::Atomic
<bool> mShutdownRequired
;
294 const bool mIsMainThread
;
295 bool mUseHangMonitor
;
296 const bool mIsUiThread
;
297 mozilla::Atomic
<bool, mozilla::Relaxed
>* mIsAPoolThreadFree
;
299 // Set to true if this thread creates a JSRuntime.
302 // The time the currently running event spent in event queues, and
303 // when it started running. If no event is running, they are
304 // TimeDuration() & TimeStamp().
305 mozilla::TimeDuration mLastEventDelay
;
306 mozilla::TimeStamp mLastEventStart
;
308 mozilla::PerformanceCounterState mPerformanceCounterState
;
310 mozilla::SimpleTaskQueue mDirectTasks
;
313 class nsThreadShutdownContext final
: public nsIThreadShutdown
{
315 NS_DECL_THREADSAFE_ISUPPORTS
316 NS_DECL_NSITHREADSHUTDOWN
319 friend class nsThread
;
320 friend class nsThreadShutdownEvent
;
321 friend class nsThreadShutdownAckEvent
;
323 nsThreadShutdownContext(NotNull
<nsThread
*> aTerminatingThread
,
324 nsThread
* aJoiningThread
)
325 : mTerminatingThread(aTerminatingThread
),
326 mTerminatingPRThread(aTerminatingThread
->GetPRThread()),
327 mJoiningThreadMutex("nsThreadShutdownContext::mJoiningThreadMutex"),
328 mJoiningThread(aJoiningThread
) {}
330 ~nsThreadShutdownContext() = default;
332 // Must be called on the joining thread.
333 void MarkCompleted();
335 // NB: This may be the last reference.
336 NotNull
<RefPtr
<nsThread
>> const mTerminatingThread
;
337 PRThread
* const mTerminatingPRThread
;
339 // May only be accessed on the joining thread.
340 bool mCompleted
= false;
341 nsTArray
<nsCOMPtr
<nsIRunnable
>> mCompletionCallbacks
;
343 // The thread waiting for this thread to shut down. Will either be cleared by
344 // the joining thread if `StopWaitingAndLeakThread` is called or by the
345 // terminating thread upon exiting and notifying the joining thread.
346 mozilla::Mutex mJoiningThreadMutex
;
347 RefPtr
<nsThread
> mJoiningThread
MOZ_GUARDED_BY(mJoiningThreadMutex
);
348 bool mThreadLeaked
MOZ_GUARDED_BY(mJoiningThreadMutex
) = false;
351 #if defined(XP_UNIX) && !defined(ANDROID) && !defined(DEBUG) && HAVE_UALARM && \
355 extern int sCanaryOutputFD
;
358 #endif // nsThread_h__