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