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