summaryrefslogtreecommitdiffstats
path: root/xpcom/threads/nsTimerImpl.h
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:22:09 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:22:09 +0000
commit43a97878ce14b72f0981164f87f2e35e14151312 (patch)
tree620249daf56c0258faa40cbdcf9cfba06de2a846 /xpcom/threads/nsTimerImpl.h
parentInitial commit. (diff)
downloadfirefox-43a97878ce14b72f0981164f87f2e35e14151312.tar.xz
firefox-43a97878ce14b72f0981164f87f2e35e14151312.zip
Adding upstream version 110.0.1.upstream/110.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--xpcom/threads/nsTimerImpl.h256
1 files changed, 256 insertions, 0 deletions
diff --git a/xpcom/threads/nsTimerImpl.h b/xpcom/threads/nsTimerImpl.h
new file mode 100644
index 0000000000..4065dc0100
--- /dev/null
+++ b/xpcom/threads/nsTimerImpl.h
@@ -0,0 +1,256 @@
+/* -*- 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 nsTimerImpl_h___
+#define nsTimerImpl_h___
+
+#include "nsITimer.h"
+#include "nsIEventTarget.h"
+#include "nsIObserver.h"
+
+#include "nsCOMPtr.h"
+
+#include "mozilla/Attributes.h"
+#include "mozilla/Mutex.h"
+#include "mozilla/StaticMutex.h"
+#include "mozilla/TimeStamp.h"
+#include "mozilla/Variant.h"
+#include "mozilla/Logging.h"
+
+extern mozilla::LogModule* GetTimerLog();
+
+#define NS_TIMER_CID \
+ { /* 5ff24248-1dd2-11b2-8427-fbab44f29bc8 */ \
+ 0x5ff24248, 0x1dd2, 0x11b2, { \
+ 0x84, 0x27, 0xfb, 0xab, 0x44, 0xf2, 0x9b, 0xc8 \
+ } \
+ }
+
+class nsIObserver;
+class nsTimerImplHolder;
+
+namespace mozilla {
+class LogModule;
+}
+
+// TimerThread, nsTimerEvent, and nsTimer have references to these. nsTimer has
+// a separate lifecycle so we can Cancel() the underlying timer when the user of
+// the nsTimer has let go of its last reference.
+class nsTimerImpl {
+ ~nsTimerImpl() {
+ MOZ_ASSERT(!mHolder);
+
+ // The nsITimer interface requires that its users keep a reference to the
+ // timers they use while those timers are initialized but have not yet
+ // fired. If this assert ever fails, it is a bug in the code that created
+ // and used the timer.
+ //
+ // Further, note that this should never fail even with a misbehaving user,
+ // because nsTimer::Release checks for a refcount of 1 with an armed timer
+ // (a timer whose only reference is from the timer thread) and when it hits
+ // this will remove the timer from the timer thread and thus destroy the
+ // last reference, preventing this situation from occurring.
+ MOZ_ASSERT(
+ mCallback.is<UnknownCallback>() || mEventTarget->IsOnCurrentThread(),
+ "Must not release mCallback off-target without canceling");
+ }
+
+ public:
+ typedef mozilla::TimeStamp TimeStamp;
+
+ nsTimerImpl(nsITimer* aTimer, nsIEventTarget* aTarget);
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(nsTimerImpl)
+ NS_DECL_NON_VIRTUAL_NSITIMER
+
+ static nsresult Startup();
+ static void Shutdown();
+
+ void SetDelayInternal(uint32_t aDelay, TimeStamp aBase = TimeStamp::Now());
+ void CancelImpl(bool aClearITimer);
+
+ void Fire(int32_t aGeneration);
+
+ int32_t GetGeneration() { return mGeneration; }
+
+ struct UnknownCallback {};
+
+ using InterfaceCallback = nsCOMPtr<nsITimerCallback>;
+
+ using ObserverCallback = nsCOMPtr<nsIObserver>;
+
+ /// A raw function pointer and its closed-over state, along with its name for
+ /// logging purposes.
+ struct FuncCallback {
+ nsTimerCallbackFunc mFunc;
+ void* mClosure;
+ const char* mName;
+ };
+
+ /// A callback defined by an owned closure and its name for logging purposes.
+ struct ClosureCallback {
+ std::function<void(nsITimer*)> mFunc;
+ const char* mName;
+ };
+
+ using Callback =
+ mozilla::Variant<UnknownCallback, InterfaceCallback, ObserverCallback,
+ FuncCallback, ClosureCallback>;
+
+ nsresult InitCommon(const mozilla::TimeDuration& aDelay, uint32_t aType,
+ Callback&& newCallback,
+ const mozilla::MutexAutoLock& aProofOfLock)
+ MOZ_REQUIRES(mMutex);
+
+ Callback& GetCallback() MOZ_REQUIRES(mMutex) {
+ mMutex.AssertCurrentThreadOwns();
+ return mCallback;
+ }
+
+ bool IsRepeating() const {
+ static_assert(nsITimer::TYPE_ONE_SHOT < nsITimer::TYPE_REPEATING_SLACK,
+ "invalid ordering of timer types!");
+ static_assert(
+ nsITimer::TYPE_REPEATING_SLACK < nsITimer::TYPE_REPEATING_PRECISE,
+ "invalid ordering of timer types!");
+ static_assert(nsITimer::TYPE_REPEATING_PRECISE <
+ nsITimer::TYPE_REPEATING_PRECISE_CAN_SKIP,
+ "invalid ordering of timer types!");
+ return mType >= nsITimer::TYPE_REPEATING_SLACK &&
+ mType < nsITimer::TYPE_ONE_SHOT_LOW_PRIORITY;
+ }
+
+ bool IsLowPriority() const {
+ return mType == nsITimer::TYPE_ONE_SHOT_LOW_PRIORITY ||
+ mType == nsITimer::TYPE_REPEATING_SLACK_LOW_PRIORITY;
+ }
+
+ bool IsSlack() const {
+ return mType == nsITimer::TYPE_REPEATING_SLACK ||
+ mType == nsITimer::TYPE_REPEATING_SLACK_LOW_PRIORITY;
+ }
+
+ void GetName(nsACString& aName, const mozilla::MutexAutoLock& aProofOfLock)
+ MOZ_REQUIRES(mMutex);
+
+ void GetName(nsACString& aName);
+
+ void SetHolder(nsTimerImplHolder* aHolder);
+
+ nsCOMPtr<nsIEventTarget> mEventTarget;
+
+ void LogFiring(const Callback& aCallback, uint8_t aType, uint32_t aDelay);
+
+ nsresult InitWithClosureCallback(std::function<void(nsITimer*)>&& aCallback,
+ const mozilla::TimeDuration& aDelay,
+ uint32_t aType, const char* aNameString);
+
+ // This weak reference must be cleared by the nsTimerImplHolder by
+ // calling SetHolder(nullptr) before the holder is destroyed. Take()
+ // also sets this to null, to indicate it's no longer in the
+ // TimerThread's list. This Take() call is NOT made under the
+ // nsTimerImpl's mutex (all other SetHolder calls are under the mutex,
+ // and all references other than in the constructor or destructor of
+ // nsTimerImpl). However, ALL uses and references to the holder are
+ // under the TimerThread's Monitor lock, so consistency is guaranteed by that.
+ nsTimerImplHolder* mHolder;
+
+ // These members are set by the initiating thread, when the timer's type is
+ // changed and during the period where it fires on that thread.
+ uint8_t mType;
+
+ // The generation number of this timer, re-generated each time the timer is
+ // initialized so one-shot timers can be canceled and re-initialized by the
+ // arming thread without any bad race conditions.
+ // Updated only after this timer has been removed from the timer thread.
+ int32_t mGeneration;
+
+ mozilla::TimeDuration mDelay MOZ_GUARDED_BY(mMutex);
+ // Never updated while in the TimerThread's timer list. Only updated
+ // before adding to that list or during nsTimerImpl::Fire(), when it has
+ // been removed from the TimerThread's list. TimerThread can access
+ // mTimeout of any timer in the list safely
+ mozilla::TimeStamp mTimeout;
+
+ RefPtr<nsITimer> mITimer MOZ_GUARDED_BY(mMutex);
+ mozilla::Mutex mMutex;
+ Callback mCallback MOZ_GUARDED_BY(mMutex);
+ // Counter because in rare cases we can Fire reentrantly
+ unsigned int mFiring MOZ_GUARDED_BY(mMutex);
+
+ static mozilla::StaticMutex sDeltaMutex;
+ static double sDeltaSum MOZ_GUARDED_BY(sDeltaMutex);
+ static double sDeltaSumSquared MOZ_GUARDED_BY(sDeltaMutex);
+ static double sDeltaNum MOZ_GUARDED_BY(sDeltaMutex);
+};
+
+class nsTimer final : public nsITimer {
+ explicit nsTimer(nsIEventTarget* aTarget)
+ : mImpl(new nsTimerImpl(this, aTarget)) {}
+
+ virtual ~nsTimer();
+
+ public:
+ friend class TimerThread;
+ friend class nsTimerEvent;
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_FORWARD_SAFE_NSITIMER(mImpl);
+
+ // NOTE: This constructor is not exposed on `nsITimer` as NS_FORWARD_SAFE_
+ // does not support forwarding rvalue references.
+ nsresult InitWithClosureCallback(std::function<void(nsITimer*)>&& aCallback,
+ const mozilla::TimeDuration& aDelay,
+ uint32_t aType, const char* aNameString) {
+ return mImpl ? mImpl->InitWithClosureCallback(std::move(aCallback), aDelay,
+ aType, aNameString)
+ : NS_ERROR_NULL_POINTER;
+ }
+
+ // Create a timer targeting the given target. nullptr indicates that the
+ // current thread should be used as the timer's target.
+ static RefPtr<nsTimer> WithEventTarget(nsIEventTarget* aTarget);
+
+ static nsresult XPCOMConstructor(REFNSIID aIID, void** aResult);
+
+ private:
+ // nsTimerImpl holds a strong ref to us. When our refcount goes to 1, we will
+ // null this to break the cycle.
+ RefPtr<nsTimerImpl> mImpl;
+};
+
+// A class that holds on to an nsTimerImpl. This lets the nsTimerImpl object
+// directly instruct its holder to forget the timer, avoiding list lookups.
+class nsTimerImplHolder {
+ public:
+ explicit nsTimerImplHolder(nsTimerImpl* aTimerImpl) : mTimerImpl(aTimerImpl) {
+ if (mTimerImpl) {
+ mTimerImpl->mMutex.AssertCurrentThreadOwns();
+ mTimerImpl->SetHolder(this);
+ }
+ }
+
+ ~nsTimerImplHolder() {
+ if (mTimerImpl) {
+ mTimerImpl->mMutex.AssertCurrentThreadOwns();
+ mTimerImpl->SetHolder(nullptr);
+ }
+ }
+
+ void Forget(nsTimerImpl* aTimerImpl) {
+ if (MOZ_UNLIKELY(!mTimerImpl)) {
+ return;
+ }
+ MOZ_ASSERT(aTimerImpl == mTimerImpl);
+ mTimerImpl->mMutex.AssertCurrentThreadOwns();
+ mTimerImpl->SetHolder(nullptr);
+ mTimerImpl = nullptr;
+ }
+
+ protected:
+ RefPtr<nsTimerImpl> mTimerImpl;
+};
+
+#endif /* nsTimerImpl_h___ */