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/. */
7 #ifndef nsTimerImpl_h___
8 #define nsTimerImpl_h___
11 #include "nsIEventTarget.h"
12 #include "nsIObserver.h"
16 #include "mozilla/Attributes.h"
17 #include "mozilla/Mutex.h"
18 #include "mozilla/StaticMutex.h"
19 #include "mozilla/TimeStamp.h"
20 #include "mozilla/Variant.h"
21 #include "mozilla/Logging.h"
23 extern mozilla::LogModule
* GetTimerLog();
25 #define NS_TIMER_CID \
26 { /* 5ff24248-1dd2-11b2-8427-fbab44f29bc8 */ \
27 0x5ff24248, 0x1dd2, 0x11b2, { \
28 0x84, 0x27, 0xfb, 0xab, 0x44, 0xf2, 0x9b, 0xc8 \
38 // TimerThread, nsTimerEvent, and nsTimer have references to these. nsTimer has
39 // a separate lifecycle so we can Cancel() the underlying timer when the user of
40 // the nsTimer has let go of its last reference.
43 MOZ_ASSERT(!mIsInTimerThread
);
45 // The nsITimer interface requires that its users keep a reference to the
46 // timers they use while those timers are initialized but have not yet
47 // fired. If this assert ever fails, it is a bug in the code that created
48 // and used the timer.
50 // Further, note that this should never fail even with a misbehaving user,
51 // because nsTimer::Release checks for a refcount of 1 with an armed timer
52 // (a timer whose only reference is from the timer thread) and when it hits
53 // this will remove the timer from the timer thread and thus destroy the
54 // last reference, preventing this situation from occurring.
56 mCallback
.is
<UnknownCallback
>() || mEventTarget
->IsOnCurrentThread(),
57 "Must not release mCallback off-target without canceling");
61 typedef mozilla::TimeStamp TimeStamp
;
63 nsTimerImpl(nsITimer
* aTimer
, nsIEventTarget
* aTarget
);
64 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(nsTimerImpl
)
65 NS_DECL_NON_VIRTUAL_NSITIMER
67 static nsresult
Startup();
68 static void Shutdown();
70 void SetDelayInternal(uint32_t aDelay
, TimeStamp aBase
= TimeStamp::Now());
71 void CancelImpl(bool aClearITimer
);
73 void Fire(int32_t aGeneration
);
75 int32_t GetGeneration() { return mGeneration
; }
77 struct UnknownCallback
{};
79 using InterfaceCallback
= nsCOMPtr
<nsITimerCallback
>;
81 using ObserverCallback
= nsCOMPtr
<nsIObserver
>;
83 /// A raw function pointer and its closed-over state, along with its name for
86 nsTimerCallbackFunc mFunc
;
91 /// A callback defined by an owned closure and its name for logging purposes.
92 struct ClosureCallback
{
93 std::function
<void(nsITimer
*)> mFunc
;
98 mozilla::Variant
<UnknownCallback
, InterfaceCallback
, ObserverCallback
,
99 FuncCallback
, ClosureCallback
>;
101 nsresult
InitCommon(const mozilla::TimeDuration
& aDelay
, uint32_t aType
,
102 Callback
&& newCallback
,
103 const mozilla::MutexAutoLock
& aProofOfLock
)
104 MOZ_REQUIRES(mMutex
);
106 Callback
& GetCallback() MOZ_REQUIRES(mMutex
) {
107 mMutex
.AssertCurrentThreadOwns();
111 bool IsRepeating() const {
112 static_assert(nsITimer::TYPE_ONE_SHOT
< nsITimer::TYPE_REPEATING_SLACK
,
113 "invalid ordering of timer types!");
115 nsITimer::TYPE_REPEATING_SLACK
< nsITimer::TYPE_REPEATING_PRECISE
,
116 "invalid ordering of timer types!");
117 static_assert(nsITimer::TYPE_REPEATING_PRECISE
<
118 nsITimer::TYPE_REPEATING_PRECISE_CAN_SKIP
,
119 "invalid ordering of timer types!");
120 return mType
>= nsITimer::TYPE_REPEATING_SLACK
&&
121 mType
< nsITimer::TYPE_ONE_SHOT_LOW_PRIORITY
;
124 bool IsLowPriority() const {
125 return mType
== nsITimer::TYPE_ONE_SHOT_LOW_PRIORITY
||
126 mType
== nsITimer::TYPE_REPEATING_SLACK_LOW_PRIORITY
;
129 bool IsSlack() const {
130 return mType
== nsITimer::TYPE_REPEATING_SLACK
||
131 mType
== nsITimer::TYPE_REPEATING_SLACK_LOW_PRIORITY
;
134 void GetName(nsACString
& aName
, const mozilla::MutexAutoLock
& aProofOfLock
)
135 MOZ_REQUIRES(mMutex
);
137 bool IsInTimerThread() const { return mIsInTimerThread
; }
138 void SetIsInTimerThread(bool aIsInTimerThread
) {
139 mIsInTimerThread
= aIsInTimerThread
;
142 nsCOMPtr
<nsIEventTarget
> mEventTarget
;
144 void LogFiring(const Callback
& aCallback
, uint8_t aType
, uint32_t aDelay
);
146 nsresult
InitWithClosureCallback(std::function
<void(nsITimer
*)>&& aCallback
,
147 const mozilla::TimeDuration
& aDelay
,
148 uint32_t aType
, const char* aNameString
);
150 // Is this timer currently referenced from a TimerThread::Entry?
151 // Note: It is cleared before the Entry is destroyed. Take() also sets it to
152 // false, to indicate it's no longer in the TimerThread's list. This Take()
153 // call is NOT made under the nsTimerImpl's mutex (all other
154 // SetIsInTimerThread calls are under the mutex). However, ALL accesses to
155 // mIsInTimerThread are under the TimerThread's Monitor lock, so consistency
156 // is guaranteed by that.
157 bool mIsInTimerThread
;
159 // These members are set by the initiating thread, when the timer's type is
160 // changed and during the period where it fires on that thread.
163 // The generation number of this timer, re-generated each time the timer is
164 // initialized so one-shot timers can be canceled and re-initialized by the
165 // arming thread without any bad race conditions.
166 // Updated only after this timer has been removed from the timer thread.
169 mozilla::TimeDuration mDelay
MOZ_GUARDED_BY(mMutex
);
170 // Never updated while in the TimerThread's timer list. Only updated
171 // before adding to that list or during nsTimerImpl::Fire(), when it has
172 // been removed from the TimerThread's list. TimerThread can access
173 // mTimeout of any timer in the list safely
174 mozilla::TimeStamp mTimeout
;
176 RefPtr
<nsITimer
> mITimer
MOZ_GUARDED_BY(mMutex
);
177 mozilla::Mutex mMutex
;
178 Callback mCallback
MOZ_GUARDED_BY(mMutex
);
179 // Counter because in rare cases we can Fire reentrantly
180 unsigned int mFiring
MOZ_GUARDED_BY(mMutex
);
182 static mozilla::StaticMutex sDeltaMutex
;
183 static double sDeltaSum
MOZ_GUARDED_BY(sDeltaMutex
);
184 static double sDeltaSumSquared
MOZ_GUARDED_BY(sDeltaMutex
);
185 static double sDeltaNum
MOZ_GUARDED_BY(sDeltaMutex
);
188 class nsTimer final
: public nsITimer
{
189 explicit nsTimer(nsIEventTarget
* aTarget
)
190 : mImpl(new nsTimerImpl(this, aTarget
)) {}
195 friend class TimerThread
;
196 friend class nsTimerEvent
;
198 NS_DECL_THREADSAFE_ISUPPORTS
199 NS_FORWARD_SAFE_NSITIMER(mImpl
);
201 // NOTE: This constructor is not exposed on `nsITimer` as NS_FORWARD_SAFE_
202 // does not support forwarding rvalue references.
203 nsresult
InitWithClosureCallback(std::function
<void(nsITimer
*)>&& aCallback
,
204 const mozilla::TimeDuration
& aDelay
,
205 uint32_t aType
, const char* aNameString
) {
206 return mImpl
? mImpl
->InitWithClosureCallback(std::move(aCallback
), aDelay
,
208 : NS_ERROR_NULL_POINTER
;
211 // Create a timer targeting the given target. nullptr indicates that the
212 // current thread should be used as the timer's target.
213 static RefPtr
<nsTimer
> WithEventTarget(nsIEventTarget
* aTarget
);
215 static nsresult
XPCOMConstructor(REFNSIID aIID
, void** aResult
);
218 // nsTimerImpl holds a strong ref to us. When our refcount goes to 1, we will
219 // null this to break the cycle.
220 RefPtr
<nsTimerImpl
> mImpl
;
223 class nsTimerManager final
: public nsITimerManager
{
226 NS_DECL_NSITIMERMANAGER
228 ~nsTimerManager() = default;
231 #endif /* nsTimerImpl_h___ */