summaryrefslogtreecommitdiffstats
path: root/xpcom/threads/nsThread.h
blob: 9c7aac4911b50ec97454d62709f3672bc2117126 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef nsThread_h__
#define nsThread_h__

#include "MainThreadUtils.h"
#include "mozilla/AlreadyAddRefed.h"
#include "mozilla/Atomics.h"
#include "mozilla/Attributes.h"
#include "mozilla/DataMutex.h"
#include "mozilla/EventQueue.h"
#include "mozilla/LinkedList.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/Mutex.h"
#include "mozilla/NotNull.h"
#include "mozilla/PerformanceCounter.h"
#include "mozilla/RefPtr.h"
#include "mozilla/TaskDispatcher.h"
#include "mozilla/TimeStamp.h"
#include "mozilla/UniquePtr.h"
#include "nsIDirectTaskDispatcher.h"
#include "nsIEventTarget.h"
#include "nsISerialEventTarget.h"
#include "nsISupportsPriority.h"
#include "nsIThread.h"
#include "nsIThreadInternal.h"
#include "nsTArray.h"

namespace mozilla {
class CycleCollectedJSContext;
class DelayedRunnable;
class SynchronizedEventQueue;
class ThreadEventQueue;
class ThreadEventTarget;

template <typename T, size_t Length>
class Array;
}  // namespace mozilla

using mozilla::NotNull;

class nsIRunnable;
class nsThreadEnumerator;
class nsThreadShutdownContext;

// See https://www.w3.org/TR/longtasks
#define LONGTASK_BUSY_WINDOW_MS 50

// A class for managing performance counter state.
namespace mozilla {
class PerformanceCounterState {
 public:
  explicit PerformanceCounterState(const uint32_t& aNestedEventLoopDepthRef,
                                   bool aIsMainThread)
      : mNestedEventLoopDepth(aNestedEventLoopDepthRef),
        mIsMainThread(aIsMainThread),
        // Does it really make sense to initialize these to "now" when we
        // haven't run any tasks?
        mLastLongTaskEnd(TimeStamp::Now()),
        mLastLongNonIdleTaskEnd(mLastLongTaskEnd) {}

  class Snapshot {
   public:
    Snapshot(uint32_t aOldEventLoopDepth, PerformanceCounter* aCounter,
             bool aOldIsIdleRunnable)
        : mOldEventLoopDepth(aOldEventLoopDepth),
          mOldPerformanceCounter(aCounter),
          mOldIsIdleRunnable(aOldIsIdleRunnable) {}

    Snapshot(const Snapshot&) = default;
    Snapshot(Snapshot&&) = default;

   private:
    friend class PerformanceCounterState;

    const uint32_t mOldEventLoopDepth;
    // Non-const so we can move out of it and avoid the extra refcounting.
    RefPtr<PerformanceCounter> mOldPerformanceCounter;
    const bool mOldIsIdleRunnable;
  };

  // Notification that a runnable is about to run.  This captures a snapshot of
  // our current state before we reset to prepare for the new runnable.  This
  // muast be called after mNestedEventLoopDepth has been incremented for the
  // runnable execution.  The performance counter passed in should be the one
  // for the relevant runnable and may be null.  aIsIdleRunnable should be true
  // if and only if the runnable has idle priority.
  Snapshot RunnableWillRun(PerformanceCounter* Counter, TimeStamp aNow,
                           bool aIsIdleRunnable);

  // Notification that a runnable finished executing.  This must be passed the
  // snapshot that RunnableWillRun returned for the same runnable.  This must be
  // called before mNestedEventLoopDepth is decremented after the runnable's
  // execution.
  void RunnableDidRun(Snapshot&& aSnapshot);

  const TimeStamp& LastLongTaskEnd() const { return mLastLongTaskEnd; }
  const TimeStamp& LastLongNonIdleTaskEnd() const {
    return mLastLongNonIdleTaskEnd;
  }

 private:
  // Called to report accumulated time, as needed, when we're about to run a
  // runnable or just finished running one.
  void MaybeReportAccumulatedTime(TimeStamp aNow);

  // Whether the runnable we are about to run, or just ran, is a nested
  // runnable, in the sense that there is some other runnable up the stack
  // spinning the event loop.  This must be called before we change our
  // mCurrentEventLoopDepth (when about to run a new event) or after we restore
  // it (after we ran one).
  bool IsNestedRunnable() const {
    return mNestedEventLoopDepth > mCurrentEventLoopDepth;
  }

  // The event loop depth of the currently running runnable.  Set to the max
  // value of a uint32_t when there is no runnable running, so when starting to
  // run a toplevel (not nested) runnable IsNestedRunnable() will test false.
  uint32_t mCurrentEventLoopDepth = std::numeric_limits<uint32_t>::max();

  // A reference to the nsThread's mNestedEventLoopDepth, so we can
  // see what it is right now.
  const uint32_t& mNestedEventLoopDepth;

  // A boolean that indicates whether the currently running runnable is an idle
  // runnable.  Only has a useful value between RunnableWillRun() being called
  // and RunnableDidRun() returning.
  bool mCurrentRunnableIsIdleRunnable = false;

  // Whether we're attached to the mainthread nsThread.
  const bool mIsMainThread;

  // The timestamp from which time to be accounted for should be measured.  This
  // can be the start of a runnable running or the end of a nested runnable
  // running.
  TimeStamp mCurrentTimeSliceStart;

  // Information about when long tasks last ended.
  TimeStamp mLastLongTaskEnd;
  TimeStamp mLastLongNonIdleTaskEnd;

  // The performance counter to use for accumulating the runtime of
  // the currently running event.  May be null, in which case the
  // event's running time should not be accounted to any performance
  // counters.
  RefPtr<PerformanceCounter> mCurrentPerformanceCounter;
};
}  // namespace mozilla

// A native thread
class nsThread : public nsIThreadInternal,
                 public nsISupportsPriority,
                 public nsIDirectTaskDispatcher,
                 private mozilla::LinkedListElement<nsThread> {
  friend mozilla::LinkedList<nsThread>;
  friend mozilla::LinkedListElement<nsThread>;

 public:
  NS_DECL_THREADSAFE_ISUPPORTS
  NS_DECL_NSIEVENTTARGET_FULL
  NS_DECL_NSITHREAD
  NS_DECL_NSITHREADINTERNAL
  NS_DECL_NSISUPPORTSPRIORITY
  NS_DECL_NSIDIRECTTASKDISPATCHER

  enum MainThreadFlag { MAIN_THREAD, NOT_MAIN_THREAD };

  nsThread(NotNull<mozilla::SynchronizedEventQueue*> aQueue,
           MainThreadFlag aMainThread,
           nsIThreadManager::ThreadCreationOptions aOptions);

 private:
  nsThread();

 public:
  // Initialize this as a named wrapper for a new PRThread.
  nsresult Init(const nsACString& aName);

  // Initialize this as a wrapper for the current PRThread.
  nsresult InitCurrentThread();

  // Get this thread's name, thread-safe.
  void GetThreadName(nsACString& aNameBuffer);

  // Set this thread's name. Consider using
  // NS_SetCurrentThreadName if you are not sure.
  void SetThreadNameInternal(const nsACString& aName);

 private:
  // Initializes the mThreadId and stack base/size members, and adds the thread
  // to the ThreadList().
  void InitCommon();

 public:
  // The PRThread corresponding to this thread.
  PRThread* GetPRThread() const { return mThread; }

  const void* StackBase() const { return mStackBase; }
  size_t StackSize() const { return mStackSize; }

  uint32_t ThreadId() const { return mThreadId; }

  // If this flag is true, then the nsThread was created using
  // nsIThreadManager::NewThread.
  bool ShutdownRequired() { return mShutdownRequired; }

  // Lets GetRunningEventDelay() determine if the pool this is part
  // of has an unstarted thread
  void SetPoolThreadFreePtr(mozilla::Atomic<bool, mozilla::Relaxed>* aPtr) {
    mIsAPoolThreadFree = aPtr;
  }

  void SetScriptObserver(mozilla::CycleCollectedJSContext* aScriptObserver);

  uint32_t RecursionDepth() const;

  void ShutdownComplete(NotNull<nsThreadShutdownContext*> aContext);

  void WaitForAllAsynchronousShutdowns();

  static const uint32_t kRunnableNameBufSize = 1000;
  static mozilla::Array<char, kRunnableNameBufSize> sMainThreadRunnableName;

  mozilla::SynchronizedEventQueue* EventQueue() { return mEvents.get(); }

  bool ShuttingDown() const { return mShutdownContext != nullptr; }

  static bool GetLabeledRunnableName(nsIRunnable* aEvent, nsACString& aName,
                                     mozilla::EventQueuePriority aPriority);

  virtual mozilla::PerformanceCounter* GetPerformanceCounter(
      nsIRunnable* aEvent) const;

  static mozilla::PerformanceCounter* GetPerformanceCounterBase(
      nsIRunnable* aEvent);

  size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;

  // Returns the size of this object, its PRThread, and its shutdown contexts,
  // but excluding its event queues.
  size_t ShallowSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;

  size_t SizeOfEventQueues(mozilla::MallocSizeOf aMallocSizeOf) const;

  static nsThreadEnumerator Enumerate();

  void SetUseHangMonitor(bool aValue) {
    MOZ_ASSERT(IsOnCurrentThread());
    mUseHangMonitor = aValue;
  }

 private:
  void DoMainThreadSpecificProcessing() const;

 protected:
  friend class nsThreadShutdownEvent;

  friend class nsThreadEnumerator;

  virtual ~nsThread();

  static void ThreadFunc(void* aArg);

  // Helper
  already_AddRefed<nsIThreadObserver> GetObserver() {
    nsIThreadObserver* obs;
    nsThread::GetObserver(&obs);
    return already_AddRefed<nsIThreadObserver>(obs);
  }

  already_AddRefed<nsThreadShutdownContext> ShutdownInternal(bool aSync);

  friend class nsThreadManager;
  friend class nsThreadPool;

  static mozilla::OffTheBooksMutex& ThreadListMutex();
  static mozilla::LinkedList<nsThread>& ThreadList();

  void AddToThreadList();
  void MaybeRemoveFromThreadList();

  // Whether or not these members have a value determines whether the nsThread
  // is treated as a full XPCOM thread or as a thin wrapper.
  //
  // For full nsThreads, they will always contain valid pointers. For thin
  // wrappers around non-XPCOM threads, they will be null, and event dispatch
  // methods which rely on them will fail (and assert) if called.
  RefPtr<mozilla::SynchronizedEventQueue> mEvents;
  RefPtr<mozilla::ThreadEventTarget> mEventTarget;

  // The number of outstanding nsThreadShutdownContext started by this thread.
  // The thread will not be allowed to exit until this number reaches 0.
  uint32_t mOutstandingShutdownContexts;
  // The shutdown context for ourselves.
  RefPtr<nsThreadShutdownContext> mShutdownContext;

  mozilla::CycleCollectedJSContext* mScriptObserver;

  // Our name.
  mozilla::DataMutex<nsCString> mThreadName;

  void* mStackBase = nullptr;
  uint32_t mStackSize;
  uint32_t mThreadId;

  uint32_t mNestedEventLoopDepth;

  mozilla::Atomic<bool> mShutdownRequired;

  int8_t mPriority;

  const bool mIsMainThread;
  bool mUseHangMonitor;
  mozilla::Atomic<bool, mozilla::Relaxed>* mIsAPoolThreadFree;

  // Set to true if this thread creates a JSRuntime.
  bool mCanInvokeJS;

  // The time the currently running event spent in event queues, and
  // when it started running.  If no event is running, they are
  // TimeDuration() & TimeStamp().
  mozilla::TimeDuration mLastEventDelay;
  mozilla::TimeStamp mLastEventStart;

#ifdef EARLY_BETA_OR_EARLIER
  nsCString mNameForWakeupTelemetry;
  mozilla::TimeStamp mLastWakeupCheckTime;
  uint32_t mWakeupCount = 0;
#endif

  mozilla::PerformanceCounterState mPerformanceCounterState;

  mozilla::SimpleTaskQueue mDirectTasks;
};

class nsThreadShutdownContext final : public nsIThreadShutdown {
 public:
  NS_DECL_THREADSAFE_ISUPPORTS
  NS_DECL_NSITHREADSHUTDOWN

 private:
  friend class nsThread;
  friend class nsThreadShutdownEvent;
  friend class nsThreadShutdownAckEvent;

  nsThreadShutdownContext(NotNull<nsThread*> aTerminatingThread,
                          nsThread* aJoiningThread)
      : mTerminatingThread(aTerminatingThread),
        mTerminatingPRThread(aTerminatingThread->GetPRThread()),
        mJoiningThreadMutex("nsThreadShutdownContext::mJoiningThreadMutex"),
        mJoiningThread(aJoiningThread) {}

  ~nsThreadShutdownContext() = default;

  // Must be called on the joining thread.
  void MarkCompleted();

  // NB: This may be the last reference.
  NotNull<RefPtr<nsThread>> const mTerminatingThread;
  PRThread* const mTerminatingPRThread;

  // May only be accessed on the joining thread.
  bool mCompleted = false;
  nsTArray<nsCOMPtr<nsIRunnable>> mCompletionCallbacks;

  // The thread waiting for this thread to shut down. Will either be cleared by
  // the joining thread if `StopWaitingAndLeakThread` is called or by the
  // terminating thread upon exiting and notifying the joining thread.
  mozilla::Mutex mJoiningThreadMutex;
  RefPtr<nsThread> mJoiningThread MOZ_GUARDED_BY(mJoiningThreadMutex);
  bool mThreadLeaked MOZ_GUARDED_BY(mJoiningThreadMutex) = false;
};

class MOZ_STACK_CLASS nsThreadEnumerator final {
 public:
  nsThreadEnumerator() = default;

  auto begin() { return nsThread::ThreadList().begin(); }
  auto end() { return nsThread::ThreadList().end(); }

 private:
  mozilla::OffTheBooksMutexAutoLock mMal{nsThread::ThreadListMutex()};
};

#if defined(XP_UNIX) && !defined(ANDROID) && !defined(DEBUG) && HAVE_UALARM && \
    defined(_GNU_SOURCE)
#  define MOZ_CANARY

extern int sCanaryOutputFD;
#endif

#endif  // nsThread_h__