summaryrefslogtreecommitdiffstats
path: root/xpcom/threads/nsThread.h
blob: 305f092dbaa8ce05bfca4ae2e8a461bad9220c77 (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
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
/* -*- 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/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 SynchronizedEventQueue;
class ThreadEventQueue;
class ThreadEventTarget;

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

using mozilla::NotNull;

class nsIRunnable;
class nsLocalExecutionRecord;
class nsThreadEnumerator;

// 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, uint32_t aStackSize);

 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();

 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<struct 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();

  static uint32_t MaxActiveThreads();

  // When entering local execution mode a new event queue is created and used as
  // an event source. This queue is only accessible through an
  // nsLocalExecutionGuard constructed from the nsLocalExecutionRecord returned
  // by this function, effectively restricting the events that get run while in
  // local execution mode to those dispatched by the owner of the guard object.
  //
  // Local execution is not nestable. When the nsLocalExecutionGuard is
  // destructed, the thread exits the local execution mode.
  //
  // Note that code run in local execution mode is not considered a task in the
  // spec sense. Events from the local queue are considered part of the
  // enclosing task and as such do not trigger profiling hooks, observer
  // notifications, etc.
  nsLocalExecutionRecord EnterLocalExecution();

  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);
  }

  struct nsThreadShutdownContext* ShutdownInternal(bool aSync);

  friend class nsThreadManager;
  friend class nsThreadPool;

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

  // The current number of active threads.
  static uint32_t sActiveThreads;
  // The maximum current number of active threads we've had in this session.
  static uint32_t sMaxActiveThreads;

  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 shutdown contexts for any other threads we've asked to shut down.
  using ShutdownContexts =
      nsTArray<mozilla::UniquePtr<struct nsThreadShutdownContext>>;

  // Helper for finding a ShutdownContext in the contexts array.
  struct ShutdownContextsComp {
    bool Equals(const ShutdownContexts::elem_type& a,
                const ShutdownContexts::elem_type::Pointer b) const;
  };

  ShutdownContexts mRequestedShutdownContexts;
  // The shutdown context for ourselves.
  struct nsThreadShutdownContext* mShutdownContext;

  mozilla::CycleCollectedJSContext* mScriptObserver;

  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;

  bool mHasTLSEntry = false;

  // 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;

  bool mIsInLocalExecutionMode = false;

  mozilla::SimpleTaskQueue mDirectTasks;
};

struct nsThreadShutdownContext {
  nsThreadShutdownContext(NotNull<nsThread*> aTerminatingThread,
                          NotNull<nsThread*> aJoiningThread,
                          bool aAwaitingShutdownAck)
      : mTerminatingThread(aTerminatingThread),
        mTerminatingPRThread(aTerminatingThread->GetPRThread()),
        mJoiningThread(aJoiningThread),
        mAwaitingShutdownAck(aAwaitingShutdownAck),
        mIsMainThreadJoining(NS_IsMainThread()) {
    MOZ_COUNT_CTOR(nsThreadShutdownContext);
  }
  MOZ_COUNTED_DTOR(nsThreadShutdownContext)

  // NB: This will be the last reference.
  NotNull<RefPtr<nsThread>> mTerminatingThread;
  PRThread* const mTerminatingPRThread;
  NotNull<nsThread*> MOZ_UNSAFE_REF(
      "Thread manager is holding reference to joining thread") mJoiningThread;
  bool mAwaitingShutdownAck;
  bool mIsMainThreadJoining;
};

// This RAII class controls the duration of the associated nsThread's local
// execution mode and provides access to the local event target. (See
// nsThread::EnterLocalExecution() for details.) It is constructed from an
// nsLocalExecutionRecord, which can only be constructed by nsThread.
class MOZ_RAII nsLocalExecutionGuard final {
 public:
  MOZ_IMPLICIT nsLocalExecutionGuard(
      nsLocalExecutionRecord&& aLocalExecutionRecord);
  nsLocalExecutionGuard(const nsLocalExecutionGuard&) = delete;
  nsLocalExecutionGuard(nsLocalExecutionGuard&&) = delete;
  ~nsLocalExecutionGuard();

  nsCOMPtr<nsISerialEventTarget> GetEventTarget() const {
    return mLocalEventTarget;
  }

 private:
  mozilla::SynchronizedEventQueue& mEventQueueStack;
  nsCOMPtr<nsISerialEventTarget> mLocalEventTarget;
  bool& mLocalExecutionFlag;
};

class MOZ_TEMPORARY_CLASS nsLocalExecutionRecord final {
 private:
  friend class nsThread;
  friend class nsLocalExecutionGuard;

  nsLocalExecutionRecord(mozilla::SynchronizedEventQueue& aEventQueueStack,
                         bool& aLocalExecutionFlag)
      : mEventQueueStack(aEventQueueStack),
        mLocalExecutionFlag(aLocalExecutionFlag) {}

  nsLocalExecutionRecord(nsLocalExecutionRecord&&) = default;

 public:
  nsLocalExecutionRecord(const nsLocalExecutionRecord&) = delete;

 private:
  mozilla::SynchronizedEventQueue& mEventQueueStack;
  bool& mLocalExecutionFlag;
};

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__