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