summaryrefslogtreecommitdiffstats
path: root/xpcom/threads/nsThread.h
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--xpcom/threads/nsThread.h400
1 files changed, 400 insertions, 0 deletions
diff --git a/xpcom/threads/nsThread.h b/xpcom/threads/nsThread.h
new file mode 100644
index 0000000000..e4b0eece51
--- /dev/null
+++ b/xpcom/threads/nsThread.h
@@ -0,0 +1,400 @@
+/* -*- 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
+
+// Time a Runnable executes before we accumulate telemetry on it
+#define LONGTASK_TELEMETRY_MS 30
+
+// 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(const nsCString& aName, 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(const nsCString& aName, 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;
+ const bool mIsUiThread;
+ 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__