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 #ifndef nsThread_h__
8 #define nsThread_h__
9 
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/PerformanceCounter.h"
21 #include "mozilla/RefPtr.h"
22 #include "mozilla/TaskDispatcher.h"
23 #include "mozilla/TimeStamp.h"
24 #include "mozilla/UniquePtr.h"
25 #include "nsIDelayedRunnableObserver.h"
26 #include "nsIDirectTaskDispatcher.h"
27 #include "nsIEventTarget.h"
28 #include "nsISerialEventTarget.h"
29 #include "nsISupportsPriority.h"
30 #include "nsIThread.h"
31 #include "nsIThreadInternal.h"
32 #include "nsTArray.h"
33 
34 namespace mozilla {
35 class CycleCollectedJSContext;
36 class DelayedRunnable;
37 class SynchronizedEventQueue;
38 class ThreadEventQueue;
39 class ThreadEventTarget;
40 
41 template <typename T, size_t Length>
42 class Array;
43 }  // namespace mozilla
44 
45 using mozilla::NotNull;
46 
47 class nsIRunnable;
48 class nsLocalExecutionRecord;
49 class nsThreadEnumerator;
50 class nsThreadShutdownContext;
51 
52 // See https://www.w3.org/TR/longtasks
53 #define LONGTASK_BUSY_WINDOW_MS 50
54 
55 // A class for managing performance counter state.
56 namespace mozilla {
57 class PerformanceCounterState {
58  public:
PerformanceCounterState(const uint32_t & aNestedEventLoopDepthRef,bool aIsMainThread)59   explicit PerformanceCounterState(const uint32_t& aNestedEventLoopDepthRef,
60                                    bool aIsMainThread)
61       : mNestedEventLoopDepth(aNestedEventLoopDepthRef),
62         mIsMainThread(aIsMainThread),
63         // Does it really make sense to initialize these to "now" when we
64         // haven't run any tasks?
65         mLastLongTaskEnd(TimeStamp::Now()),
66         mLastLongNonIdleTaskEnd(mLastLongTaskEnd) {}
67 
68   class Snapshot {
69    public:
Snapshot(uint32_t aOldEventLoopDepth,PerformanceCounter * aCounter,bool aOldIsIdleRunnable)70     Snapshot(uint32_t aOldEventLoopDepth, PerformanceCounter* aCounter,
71              bool aOldIsIdleRunnable)
72         : mOldEventLoopDepth(aOldEventLoopDepth),
73           mOldPerformanceCounter(aCounter),
74           mOldIsIdleRunnable(aOldIsIdleRunnable) {}
75 
76     Snapshot(const Snapshot&) = default;
77     Snapshot(Snapshot&&) = default;
78 
79    private:
80     friend class PerformanceCounterState;
81 
82     const uint32_t mOldEventLoopDepth;
83     // Non-const so we can move out of it and avoid the extra refcounting.
84     RefPtr<PerformanceCounter> mOldPerformanceCounter;
85     const bool mOldIsIdleRunnable;
86   };
87 
88   // Notification that a runnable is about to run.  This captures a snapshot of
89   // our current state before we reset to prepare for the new runnable.  This
90   // muast be called after mNestedEventLoopDepth has been incremented for the
91   // runnable execution.  The performance counter passed in should be the one
92   // for the relevant runnable and may be null.  aIsIdleRunnable should be true
93   // if and only if the runnable has idle priority.
94   Snapshot RunnableWillRun(PerformanceCounter* Counter, TimeStamp aNow,
95                            bool aIsIdleRunnable);
96 
97   // Notification that a runnable finished executing.  This must be passed the
98   // snapshot that RunnableWillRun returned for the same runnable.  This must be
99   // called before mNestedEventLoopDepth is decremented after the runnable's
100   // execution.
101   void RunnableDidRun(Snapshot&& aSnapshot);
102 
LastLongTaskEnd()103   const TimeStamp& LastLongTaskEnd() const { return mLastLongTaskEnd; }
LastLongNonIdleTaskEnd()104   const TimeStamp& LastLongNonIdleTaskEnd() const {
105     return mLastLongNonIdleTaskEnd;
106   }
107 
108  private:
109   // Called to report accumulated time, as needed, when we're about to run a
110   // runnable or just finished running one.
111   void MaybeReportAccumulatedTime(TimeStamp aNow);
112 
113   // Whether the runnable we are about to run, or just ran, is a nested
114   // runnable, in the sense that there is some other runnable up the stack
115   // spinning the event loop.  This must be called before we change our
116   // mCurrentEventLoopDepth (when about to run a new event) or after we restore
117   // it (after we ran one).
IsNestedRunnable()118   bool IsNestedRunnable() const {
119     return mNestedEventLoopDepth > mCurrentEventLoopDepth;
120   }
121 
122   // The event loop depth of the currently running runnable.  Set to the max
123   // value of a uint32_t when there is no runnable running, so when starting to
124   // run a toplevel (not nested) runnable IsNestedRunnable() will test false.
125   uint32_t mCurrentEventLoopDepth = std::numeric_limits<uint32_t>::max();
126 
127   // A reference to the nsThread's mNestedEventLoopDepth, so we can
128   // see what it is right now.
129   const uint32_t& mNestedEventLoopDepth;
130 
131   // A boolean that indicates whether the currently running runnable is an idle
132   // runnable.  Only has a useful value between RunnableWillRun() being called
133   // and RunnableDidRun() returning.
134   bool mCurrentRunnableIsIdleRunnable = false;
135 
136   // Whether we're attached to the mainthread nsThread.
137   const bool mIsMainThread;
138 
139   // The timestamp from which time to be accounted for should be measured.  This
140   // can be the start of a runnable running or the end of a nested runnable
141   // running.
142   TimeStamp mCurrentTimeSliceStart;
143 
144   // Information about when long tasks last ended.
145   TimeStamp mLastLongTaskEnd;
146   TimeStamp mLastLongNonIdleTaskEnd;
147 
148   // The performance counter to use for accumulating the runtime of
149   // the currently running event.  May be null, in which case the
150   // event's running time should not be accounted to any performance
151   // counters.
152   RefPtr<PerformanceCounter> mCurrentPerformanceCounter;
153 };
154 }  // namespace mozilla
155 
156 // A native thread
157 class nsThread : public nsIThreadInternal,
158                  public nsISupportsPriority,
159                  public nsIDelayedRunnableObserver,
160                  public nsIDirectTaskDispatcher,
161                  private mozilla::LinkedListElement<nsThread> {
162   friend mozilla::LinkedList<nsThread>;
163   friend mozilla::LinkedListElement<nsThread>;
164 
165  public:
166   NS_DECL_THREADSAFE_ISUPPORTS
167   NS_DECL_NSIEVENTTARGET_FULL
168   NS_DECL_NSITHREAD
169   NS_DECL_NSITHREADINTERNAL
170   NS_DECL_NSISUPPORTSPRIORITY
171   NS_DECL_NSIDIRECTTASKDISPATCHER
172 
173   enum MainThreadFlag { MAIN_THREAD, NOT_MAIN_THREAD };
174 
175   nsThread(NotNull<mozilla::SynchronizedEventQueue*> aQueue,
176            MainThreadFlag aMainThread, uint32_t aStackSize);
177 
178  private:
179   nsThread();
180 
181  public:
182   // Initialize this as a named wrapper for a new PRThread.
183   nsresult Init(const nsACString& aName);
184 
185   // Initialize this as a wrapper for the current PRThread.
186   nsresult InitCurrentThread();
187 
188   // Get this thread's name, thread-safe.
189   void GetThreadName(nsACString& aNameBuffer);
190 
191   // Set this thread's name. Consider using
192   // NS_SetCurrentThreadName if you are not sure.
193   void SetThreadNameInternal(const nsACString& aName);
194 
195  private:
196   // Initializes the mThreadId and stack base/size members, and adds the thread
197   // to the ThreadList().
198   void InitCommon();
199 
200  public:
201   // The PRThread corresponding to this thread.
GetPRThread()202   PRThread* GetPRThread() const { return mThread; }
203 
StackBase()204   const void* StackBase() const { return mStackBase; }
StackSize()205   size_t StackSize() const { return mStackSize; }
206 
ThreadId()207   uint32_t ThreadId() const { return mThreadId; }
208 
209   // If this flag is true, then the nsThread was created using
210   // nsIThreadManager::NewThread.
ShutdownRequired()211   bool ShutdownRequired() { return mShutdownRequired; }
212 
213   // Lets GetRunningEventDelay() determine if the pool this is part
214   // of has an unstarted thread
SetPoolThreadFreePtr(mozilla::Atomic<bool,mozilla::Relaxed> * aPtr)215   void SetPoolThreadFreePtr(mozilla::Atomic<bool, mozilla::Relaxed>* aPtr) {
216     mIsAPoolThreadFree = aPtr;
217   }
218 
219   void SetScriptObserver(mozilla::CycleCollectedJSContext* aScriptObserver);
220 
221   uint32_t RecursionDepth() const;
222 
223   void ShutdownComplete(NotNull<nsThreadShutdownContext*> aContext);
224 
225   void WaitForAllAsynchronousShutdowns();
226 
227   static const uint32_t kRunnableNameBufSize = 1000;
228   static mozilla::Array<char, kRunnableNameBufSize> sMainThreadRunnableName;
229 
EventQueue()230   mozilla::SynchronizedEventQueue* EventQueue() { return mEvents.get(); }
231 
ShuttingDown()232   bool ShuttingDown() const { return mShutdownContext != nullptr; }
233 
234   static bool GetLabeledRunnableName(nsIRunnable* aEvent, nsACString& aName,
235                                      mozilla::EventQueuePriority aPriority);
236 
237   virtual mozilla::PerformanceCounter* GetPerformanceCounter(
238       nsIRunnable* aEvent) const;
239 
240   static mozilla::PerformanceCounter* GetPerformanceCounterBase(
241       nsIRunnable* aEvent);
242 
243   size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
244 
245   // Returns the size of this object, its PRThread, and its shutdown contexts,
246   // but excluding its event queues.
247   size_t ShallowSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
248 
249   size_t SizeOfEventQueues(mozilla::MallocSizeOf aMallocSizeOf) const;
250 
251   static nsThreadEnumerator Enumerate();
252 
253   // When entering local execution mode a new event queue is created and used as
254   // an event source. This queue is only accessible through an
255   // nsLocalExecutionGuard constructed from the nsLocalExecutionRecord returned
256   // by this function, effectively restricting the events that get run while in
257   // local execution mode to those dispatched by the owner of the guard object.
258   //
259   // Local execution is not nestable. When the nsLocalExecutionGuard is
260   // destructed, the thread exits the local execution mode.
261   //
262   // Note that code run in local execution mode is not considered a task in the
263   // spec sense. Events from the local queue are considered part of the
264   // enclosing task and as such do not trigger profiling hooks, observer
265   // notifications, etc.
266   nsLocalExecutionRecord EnterLocalExecution();
267 
SetUseHangMonitor(bool aValue)268   void SetUseHangMonitor(bool aValue) {
269     MOZ_ASSERT(IsOnCurrentThread());
270     mUseHangMonitor = aValue;
271   }
272 
273   void OnDelayedRunnableCreated(mozilla::DelayedRunnable* aRunnable) override;
274   void OnDelayedRunnableScheduled(mozilla::DelayedRunnable* aRunnable) override;
275   void OnDelayedRunnableRan(mozilla::DelayedRunnable* aRunnable) override;
276 
277  private:
278   void DoMainThreadSpecificProcessing() const;
279 
280  protected:
281   friend class nsThreadShutdownEvent;
282 
283   friend class nsThreadEnumerator;
284 
285   virtual ~nsThread();
286 
287   static void ThreadFunc(void* aArg);
288 
289   // Helper
GetObserver()290   already_AddRefed<nsIThreadObserver> GetObserver() {
291     nsIThreadObserver* obs;
292     nsThread::GetObserver(&obs);
293     return already_AddRefed<nsIThreadObserver>(obs);
294   }
295 
296   already_AddRefed<nsThreadShutdownContext> ShutdownInternal(bool aSync);
297 
298   friend class nsThreadManager;
299   friend class nsThreadPool;
300 
301   static mozilla::OffTheBooksMutex& ThreadListMutex();
302   static mozilla::LinkedList<nsThread>& ThreadList();
303   static void ClearThreadList();
304 
305   void AddToThreadList();
306   void MaybeRemoveFromThreadList();
307 
308   // Whether or not these members have a value determines whether the nsThread
309   // is treated as a full XPCOM thread or as a thin wrapper.
310   //
311   // For full nsThreads, they will always contain valid pointers. For thin
312   // wrappers around non-XPCOM threads, they will be null, and event dispatch
313   // methods which rely on them will fail (and assert) if called.
314   RefPtr<mozilla::SynchronizedEventQueue> mEvents;
315   RefPtr<mozilla::ThreadEventTarget> mEventTarget;
316 
317   // The number of outstanding nsThreadShutdownContext started by this thread.
318   // The thread will not be allowed to exit until this number reaches 0.
319   uint32_t mOutstandingShutdownContexts;
320   // The shutdown context for ourselves.
321   RefPtr<nsThreadShutdownContext> mShutdownContext;
322 
323   mozilla::CycleCollectedJSContext* mScriptObserver;
324 
325   // Our name.
326   mozilla::DataMutex<nsCString> mThreadName;
327 
328   void* mStackBase = nullptr;
329   uint32_t mStackSize;
330   uint32_t mThreadId;
331 
332   uint32_t mNestedEventLoopDepth;
333 
334   mozilla::Atomic<bool> mShutdownRequired;
335 
336   int8_t mPriority;
337 
338   const bool mIsMainThread;
339   bool mUseHangMonitor;
340   mozilla::Atomic<bool, mozilla::Relaxed>* mIsAPoolThreadFree;
341 
342   // Set to true if this thread creates a JSRuntime.
343   bool mCanInvokeJS;
344 
345   bool mHasTLSEntry = false;
346 
347   // The time the currently running event spent in event queues, and
348   // when it started running.  If no event is running, they are
349   // TimeDuration() & TimeStamp().
350   mozilla::TimeDuration mLastEventDelay;
351   mozilla::TimeStamp mLastEventStart;
352 
353 #ifdef EARLY_BETA_OR_EARLIER
354   nsCString mNameForWakeupTelemetry;
355   mozilla::TimeStamp mLastWakeupCheckTime;
356   uint32_t mWakeupCount = 0;
357 #endif
358 
359   mozilla::PerformanceCounterState mPerformanceCounterState;
360 
361   bool mIsInLocalExecutionMode = false;
362 
363   mozilla::SimpleTaskQueue mDirectTasks;
364 };
365 
366 class nsThreadShutdownContext final : public nsIThreadShutdown {
367  public:
368   NS_DECL_THREADSAFE_ISUPPORTS
369   NS_DECL_NSITHREADSHUTDOWN
370 
371  private:
372   friend class nsThread;
373   friend class nsThreadShutdownEvent;
374   friend class nsThreadShutdownAckEvent;
375 
nsThreadShutdownContext(NotNull<nsThread * > aTerminatingThread,nsThread * aJoiningThread)376   nsThreadShutdownContext(NotNull<nsThread*> aTerminatingThread,
377                           nsThread* aJoiningThread)
378       : mTerminatingThread(aTerminatingThread),
379         mTerminatingPRThread(aTerminatingThread->GetPRThread()),
380         mJoiningThread(aJoiningThread,
381                        "nsThreadShutdownContext::mJoiningThread") {}
382 
383   ~nsThreadShutdownContext() = default;
384 
385   // Must be called on the joining thread.
386   void MarkCompleted();
387 
388   // NB: This may be the last reference.
389   NotNull<RefPtr<nsThread>> const mTerminatingThread;
390   PRThread* const mTerminatingPRThread;
391 
392   // May only be accessed on the joining thread.
393   bool mCompleted = false;
394   nsTArray<nsCOMPtr<nsIRunnable>> mCompletionCallbacks;
395 
396   // The thread waiting for this thread to shut down. Will either be cleared by
397   // the joining thread if `StopWaitingAndLeakThread` is called or by the
398   // terminating thread upon exiting and notifying the joining thread.
399   mozilla::DataMutex<RefPtr<nsThread>> mJoiningThread;
400 };
401 
402 // This RAII class controls the duration of the associated nsThread's local
403 // execution mode and provides access to the local event target. (See
404 // nsThread::EnterLocalExecution() for details.) It is constructed from an
405 // nsLocalExecutionRecord, which can only be constructed by nsThread.
406 class MOZ_RAII nsLocalExecutionGuard final {
407  public:
408   MOZ_IMPLICIT nsLocalExecutionGuard(
409       nsLocalExecutionRecord&& aLocalExecutionRecord);
410   nsLocalExecutionGuard(const nsLocalExecutionGuard&) = delete;
411   nsLocalExecutionGuard(nsLocalExecutionGuard&&) = delete;
412   ~nsLocalExecutionGuard();
413 
GetEventTarget()414   nsCOMPtr<nsISerialEventTarget> GetEventTarget() const {
415     return mLocalEventTarget;
416   }
417 
418  private:
419   mozilla::SynchronizedEventQueue& mEventQueueStack;
420   nsCOMPtr<nsISerialEventTarget> mLocalEventTarget;
421   bool& mLocalExecutionFlag;
422 };
423 
424 class MOZ_TEMPORARY_CLASS nsLocalExecutionRecord final {
425  private:
426   friend class nsThread;
427   friend class nsLocalExecutionGuard;
428 
nsLocalExecutionRecord(mozilla::SynchronizedEventQueue & aEventQueueStack,bool & aLocalExecutionFlag)429   nsLocalExecutionRecord(mozilla::SynchronizedEventQueue& aEventQueueStack,
430                          bool& aLocalExecutionFlag)
431       : mEventQueueStack(aEventQueueStack),
432         mLocalExecutionFlag(aLocalExecutionFlag) {}
433 
434   nsLocalExecutionRecord(nsLocalExecutionRecord&&) = default;
435 
436  public:
437   nsLocalExecutionRecord(const nsLocalExecutionRecord&) = delete;
438 
439  private:
440   mozilla::SynchronizedEventQueue& mEventQueueStack;
441   bool& mLocalExecutionFlag;
442 };
443 
444 class MOZ_STACK_CLASS nsThreadEnumerator final {
445  public:
446   nsThreadEnumerator() = default;
447 
begin()448   auto begin() { return nsThread::ThreadList().begin(); }
end()449   auto end() { return nsThread::ThreadList().end(); }
450 
451  private:
452   mozilla::OffTheBooksMutexAutoLock mMal{nsThread::ThreadListMutex()};
453 };
454 
455 #if defined(XP_UNIX) && !defined(ANDROID) && !defined(DEBUG) && HAVE_UALARM && \
456     defined(_GNU_SOURCE)
457 #  define MOZ_CANARY
458 
459 extern int sCanaryOutputFD;
460 #endif
461 
462 #endif  // nsThread_h__
463