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 "mozilla/Mutex.h" 11 #include "nsIIdlePeriod.h" 12 #include "nsIThreadInternal.h" 13 #include "nsISupportsPriority.h" 14 #include "nsEventQueue.h" 15 #include "nsThreadUtils.h" 16 #include "nsString.h" 17 #include "nsTObserverArray.h" 18 #include "mozilla/Attributes.h" 19 #include "mozilla/NotNull.h" 20 #include "nsAutoPtr.h" 21 #include "mozilla/AlreadyAddRefed.h" 22 #include "mozilla/UniquePtr.h" 23 24 namespace mozilla { 25 class CycleCollectedJSContext; 26 } 27 28 using mozilla::NotNull; 29 30 // A native thread 31 class nsThread 32 : public nsIThreadInternal 33 , public nsISupportsPriority 34 { 35 public: 36 NS_DECL_THREADSAFE_ISUPPORTS 37 NS_DECL_NSIEVENTTARGET 38 NS_DECL_NSITHREAD 39 NS_DECL_NSITHREADINTERNAL 40 NS_DECL_NSISUPPORTSPRIORITY 41 using nsIEventTarget::Dispatch; 42 43 enum MainThreadFlag 44 { 45 MAIN_THREAD, 46 NOT_MAIN_THREAD 47 }; 48 49 nsThread(MainThreadFlag aMainThread, uint32_t aStackSize); 50 51 // Initialize this as a wrapper for a new PRThread. 52 nsresult Init(); 53 54 // Initialize this as a wrapper for the current PRThread. 55 nsresult InitCurrentThread(); 56 57 // The PRThread corresponding to this thread. GetPRThread()58 PRThread* GetPRThread() 59 { 60 return mThread; 61 } 62 63 // If this flag is true, then the nsThread was created using 64 // nsIThreadManager::NewThread. ShutdownRequired()65 bool ShutdownRequired() 66 { 67 return mShutdownRequired; 68 } 69 70 // Clear the observer list. ClearObservers()71 void ClearObservers() 72 { 73 mEventObservers.Clear(); 74 } 75 76 void 77 SetScriptObserver(mozilla::CycleCollectedJSContext* aScriptObserver); 78 79 uint32_t 80 RecursionDepth() const; 81 82 void ShutdownComplete(NotNull<struct nsThreadShutdownContext*> aContext); 83 84 void WaitForAllAsynchronousShutdowns(); 85 86 #ifdef MOZ_CRASHREPORTER 87 enum class ShouldSaveMemoryReport 88 { 89 kMaybeReport, 90 kForceReport 91 }; 92 93 static bool SaveMemoryReportNearOOM(ShouldSaveMemoryReport aShouldSave); 94 #endif 95 96 private: 97 void DoMainThreadSpecificProcessing(bool aReallyWait); 98 99 void GetIdleEvent(nsIRunnable** aEvent, mozilla::MutexAutoLock& aProofOfLock); 100 void GetEvent(bool aWait, nsIRunnable** aEvent, 101 mozilla::MutexAutoLock& aProofOfLock); 102 103 protected: 104 class nsChainedEventQueue; 105 106 class nsNestedEventTarget; 107 friend class nsNestedEventTarget; 108 109 friend class nsThreadShutdownEvent; 110 111 virtual ~nsThread(); 112 ShuttingDown()113 bool ShuttingDown() 114 { 115 return mShutdownContext != nullptr; 116 } 117 118 static void ThreadFunc(void* aArg); 119 120 // Helper GetObserver()121 already_AddRefed<nsIThreadObserver> GetObserver() 122 { 123 nsIThreadObserver* obs; 124 nsThread::GetObserver(&obs); 125 return already_AddRefed<nsIThreadObserver>(obs); 126 } 127 128 // Wrappers for event queue methods: 129 nsresult PutEvent(nsIRunnable* aEvent, nsNestedEventTarget* aTarget); 130 nsresult PutEvent(already_AddRefed<nsIRunnable> aEvent, 131 nsNestedEventTarget* aTarget); 132 133 nsresult DispatchInternal(already_AddRefed<nsIRunnable> aEvent, 134 uint32_t aFlags, nsNestedEventTarget* aTarget); 135 136 struct nsThreadShutdownContext* ShutdownInternal(bool aSync); 137 138 // Wrapper for nsEventQueue that supports chaining. 139 class nsChainedEventQueue 140 { 141 public: nsChainedEventQueue(mozilla::Mutex & aLock)142 explicit nsChainedEventQueue(mozilla::Mutex& aLock) 143 : mNext(nullptr) 144 , mEventsAvailable(aLock, "[nsChainedEventQueue.mEventsAvailable]") 145 , mProcessSecondaryQueueRunnable(false) 146 { 147 mNormalQueue = 148 mozilla::MakeUnique<nsEventQueue>(mEventsAvailable, 149 nsEventQueue::eSharedCondVarQueue); 150 // Both queues need to use the same CondVar! 151 mSecondaryQueue = 152 mozilla::MakeUnique<nsEventQueue>(mEventsAvailable, 153 nsEventQueue::eSharedCondVarQueue); 154 } 155 156 bool GetEvent(bool aMayWait, nsIRunnable** aEvent, 157 mozilla::MutexAutoLock& aProofOfLock); 158 PutEvent(nsIRunnable * aEvent,mozilla::MutexAutoLock & aProofOfLock)159 void PutEvent(nsIRunnable* aEvent, mozilla::MutexAutoLock& aProofOfLock) 160 { 161 RefPtr<nsIRunnable> event(aEvent); 162 PutEvent(event.forget(), aProofOfLock); 163 } 164 PutEvent(already_AddRefed<nsIRunnable> aEvent,mozilla::MutexAutoLock & aProofOfLock)165 void PutEvent(already_AddRefed<nsIRunnable> aEvent, 166 mozilla::MutexAutoLock& aProofOfLock) 167 { 168 RefPtr<nsIRunnable> event(aEvent); 169 nsCOMPtr<nsIRunnablePriority> runnablePrio = 170 do_QueryInterface(event); 171 uint32_t prio = nsIRunnablePriority::PRIORITY_NORMAL; 172 if (runnablePrio) { 173 runnablePrio->GetPriority(&prio); 174 } 175 MOZ_ASSERT(prio == nsIRunnablePriority::PRIORITY_NORMAL || 176 prio == nsIRunnablePriority::PRIORITY_HIGH); 177 if (prio == nsIRunnablePriority::PRIORITY_NORMAL) { 178 mNormalQueue->PutEvent(event.forget(), aProofOfLock); 179 } else { 180 mSecondaryQueue->PutEvent(event.forget(), aProofOfLock); 181 } 182 } 183 HasPendingEvent(mozilla::MutexAutoLock & aProofOfLock)184 bool HasPendingEvent(mozilla::MutexAutoLock& aProofOfLock) 185 { 186 return mNormalQueue->HasPendingEvent(aProofOfLock) || 187 mSecondaryQueue->HasPendingEvent(aProofOfLock); 188 } 189 190 nsChainedEventQueue* mNext; 191 RefPtr<nsNestedEventTarget> mEventTarget; 192 193 private: 194 mozilla::CondVar mEventsAvailable; 195 mozilla::UniquePtr<nsEventQueue> mNormalQueue; 196 mozilla::UniquePtr<nsEventQueue> mSecondaryQueue; 197 198 // Try to process one high priority runnable after each normal 199 // priority runnable. This gives the processing model HTML spec has for 200 // 'Update the rendering' in the case only vsync messages are in the 201 // secondary queue and prevents starving the normal queue. 202 bool mProcessSecondaryQueueRunnable; 203 }; 204 205 class nsNestedEventTarget final : public nsIEventTarget 206 { 207 public: 208 NS_DECL_THREADSAFE_ISUPPORTS 209 NS_DECL_NSIEVENTTARGET 210 nsNestedEventTarget(NotNull<nsThread * > aThread,NotNull<nsChainedEventQueue * > aQueue)211 nsNestedEventTarget(NotNull<nsThread*> aThread, 212 NotNull<nsChainedEventQueue*> aQueue) 213 : mThread(aThread) 214 , mQueue(aQueue) 215 216 217 218 { 219 } 220 221 NotNull<RefPtr<nsThread>> mThread; 222 223 // This is protected by mThread->mLock. 224 nsChainedEventQueue* mQueue; 225 226 private: ~nsNestedEventTarget()227 ~nsNestedEventTarget() 228 { 229 } 230 }; 231 232 // This lock protects access to mObserver, mEvents, mIdleEvents, 233 // mIdlePeriod and mEventsAreDoomed. All of those fields are only 234 // modified on the thread itself (never from another thread). This 235 // means that we can avoid holding the lock while using mObserver 236 // and mEvents on the thread itself. When calling PutEvent on 237 // mEvents, we have to hold the lock to synchronize with 238 // PopEventQueue. 239 mozilla::Mutex mLock; 240 241 nsCOMPtr<nsIThreadObserver> mObserver; 242 mozilla::CycleCollectedJSContext* mScriptObserver; 243 244 // Only accessed on the target thread. 245 nsAutoTObserverArray<NotNull<nsCOMPtr<nsIThreadObserver>>, 2> mEventObservers; 246 247 NotNull<nsChainedEventQueue*> mEvents; // never null 248 nsChainedEventQueue mEventsRoot; 249 250 // mIdlePeriod keeps track of the current idle period. If at any 251 // time the main event queue is empty, calling 252 // mIdlePeriod->GetIdlePeriodHint() will give an estimate of when 253 // the current idle period will end. 254 nsCOMPtr<nsIIdlePeriod> mIdlePeriod; 255 mozilla::CondVar mIdleEventsAvailable; 256 nsEventQueue mIdleEvents; 257 258 int32_t mPriority; 259 PRThread* mThread; 260 uint32_t mNestedEventLoopDepth; 261 uint32_t mStackSize; 262 263 // The shutdown context for ourselves. 264 struct nsThreadShutdownContext* mShutdownContext; 265 // The shutdown contexts for any other threads we've asked to shut down. 266 nsTArray<nsAutoPtr<struct nsThreadShutdownContext>> mRequestedShutdownContexts; 267 268 bool mShutdownRequired; 269 // Set to true when events posted to this thread will never run. 270 bool mEventsAreDoomed; 271 MainThreadFlag mIsMainThread; 272 273 // Set to true if this thread creates a JSRuntime. 274 bool mCanInvokeJS; 275 }; 276 277 #if defined(XP_UNIX) && !defined(ANDROID) && !defined(DEBUG) && HAVE_UALARM \ 278 && defined(_GNU_SOURCE) 279 # define MOZ_CANARY 280 281 extern int sCanaryOutputFD; 282 #endif 283 284 #endif // nsThread_h__ 285