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/TimeStamp.h"
19 #include "mozilla/Variant.h"
20 #include "mozilla/Logging.h"
22 #ifdef MOZ_TASK_TRACER
23 # include "TracedTaskCommon.h"
26 extern mozilla::LogModule
* GetTimerLog();
28 #define NS_TIMER_CID \
29 { /* 5ff24248-1dd2-11b2-8427-fbab44f29bc8 */ \
30 0x5ff24248, 0x1dd2, 0x11b2, { \
31 0x84, 0x27, 0xfb, 0xab, 0x44, 0xf2, 0x9b, 0xc8 \
36 class nsTimerImplHolder
;
42 // TimerThread, nsTimerEvent, and nsTimer have references to these. nsTimer has
43 // a separate lifecycle so we can Cancel() the underlying timer when the user of
44 // the nsTimer has let go of its last reference.
46 ~nsTimerImpl() { MOZ_ASSERT(!mHolder
); }
49 typedef mozilla::TimeStamp TimeStamp
;
51 nsTimerImpl(nsITimer
* aTimer
, nsIEventTarget
* aTarget
);
52 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(nsTimerImpl
)
53 NS_DECL_NON_VIRTUAL_NSITIMER
55 static nsresult
Startup();
56 static void Shutdown();
58 void SetDelayInternal(uint32_t aDelay
, TimeStamp aBase
= TimeStamp::Now());
59 void CancelImpl(bool aClearITimer
);
61 void Fire(int32_t aGeneration
);
63 #ifdef MOZ_TASK_TRACER
64 void GetTLSTraceInfo();
65 mozilla::tasktracer::TracedTaskCommon
GetTracedTask();
68 int32_t GetGeneration() { return mGeneration
; }
71 Callback() : mType(Type::Unknown
), mName(Nothing
), mClosure(nullptr) {
72 mCallback
.c
= nullptr;
75 Callback(const Callback
& other
) : Callback() { *this = other
; }
77 enum class Type
: uint8_t {
84 Callback
& operator=(const Callback
& other
) {
92 mCallback
.i
= other
.mCallback
.i
;
93 NS_ADDREF(mCallback
.i
);
96 mCallback
.c
= other
.mCallback
.c
;
99 mCallback
.o
= other
.mCallback
.o
;
100 NS_ADDREF(mCallback
.o
);
104 mClosure
= other
.mClosure
;
109 ~Callback() { clear(); }
112 if (mType
== Type::Interface
) {
113 NS_RELEASE(mCallback
.i
);
114 } else if (mType
== Type::Observer
) {
115 NS_RELEASE(mCallback
.o
);
117 mType
= Type::Unknown
;
120 void swap(Callback
& other
) {
121 std::swap(mType
, other
.mType
);
122 std::swap(mCallback
, other
.mCallback
);
123 std::swap(mName
, other
.mName
);
124 std::swap(mClosure
, other
.mClosure
);
129 union CallbackUnion
{
130 nsTimerCallbackFunc c
;
131 // These refcounted references are managed manually, as they are in a
133 nsITimerCallback
* MOZ_OWNING_REF i
;
134 nsIObserver
* MOZ_OWNING_REF o
;
137 // |Name| is a tagged union type representing one of (a) nothing, (b) a
138 // string, or (c) a function. mozilla::Variant doesn't naturally handle the
139 // "nothing" case, so we define a dummy type and value (which is unused and
140 // so the exact value doesn't matter) for it.
141 typedef const int NameNothing
;
142 typedef const char* NameString
;
143 typedef nsTimerNameCallbackFunc NameFunc
;
144 typedef mozilla::Variant
<NameNothing
, NameString
, NameFunc
> Name
;
145 static const NameNothing Nothing
;
151 nsresult
InitCommon(uint32_t aDelayMS
, uint32_t aType
,
152 Callback
&& newCallback
);
154 nsresult
InitCommon(const mozilla::TimeDuration
& aDelay
, uint32_t aType
,
155 Callback
&& newCallback
);
157 Callback
& GetCallback() {
158 mMutex
.AssertCurrentThreadOwns();
162 bool IsRepeating() const {
163 static_assert(nsITimer::TYPE_ONE_SHOT
< nsITimer::TYPE_REPEATING_SLACK
,
164 "invalid ordering of timer types!");
166 nsITimer::TYPE_REPEATING_SLACK
< nsITimer::TYPE_REPEATING_PRECISE
,
167 "invalid ordering of timer types!");
168 static_assert(nsITimer::TYPE_REPEATING_PRECISE
<
169 nsITimer::TYPE_REPEATING_PRECISE_CAN_SKIP
,
170 "invalid ordering of timer types!");
171 return mType
>= nsITimer::TYPE_REPEATING_SLACK
&&
172 mType
< nsITimer::TYPE_ONE_SHOT_LOW_PRIORITY
;
175 bool IsLowPriority() const {
176 return mType
== nsITimer::TYPE_ONE_SHOT_LOW_PRIORITY
||
177 mType
== nsITimer::TYPE_REPEATING_SLACK_LOW_PRIORITY
;
180 bool IsSlack() const {
181 return mType
== nsITimer::TYPE_REPEATING_SLACK
||
182 mType
== nsITimer::TYPE_REPEATING_SLACK_LOW_PRIORITY
;
185 void GetName(nsACString
& aName
);
187 void SetHolder(nsTimerImplHolder
* aHolder
);
189 nsCOMPtr
<nsIEventTarget
> mEventTarget
;
191 void LogFiring(const Callback
& aCallback
, uint8_t aType
, uint32_t aDelay
);
193 nsresult
InitWithFuncCallbackCommon(nsTimerCallbackFunc aFunc
, void* aClosure
,
194 uint32_t aDelay
, uint32_t aType
,
195 const Callback::Name
& aName
);
197 // This weak reference must be cleared by the nsTimerImplHolder by calling
198 // SetHolder(nullptr) before the holder is destroyed.
199 nsTimerImplHolder
* mHolder
;
201 // These members are set by the initiating thread, when the timer's type is
202 // changed and during the period where it fires on that thread.
205 // The generation number of this timer, re-generated each time the timer is
206 // initialized so one-shot timers can be canceled and re-initialized by the
207 // arming thread without any bad race conditions.
208 // Updated only after this timer has been removed from the timer thread.
211 mozilla::TimeDuration mDelay
;
212 // Updated only after this timer has been removed from the timer thread.
213 mozilla::TimeStamp mTimeout
;
215 #ifdef MOZ_TASK_TRACER
216 mozilla::tasktracer::TracedTaskCommon mTracedTask
;
219 static double sDeltaSum
;
220 static double sDeltaSumSquared
;
221 static double sDeltaNum
;
222 RefPtr
<nsITimer
> mITimer
;
223 mozilla::Mutex mMutex
;
225 // Counter because in rare cases we can Fire reentrantly
226 unsigned int mFiring
;
229 class nsTimer final
: public nsITimer
{
230 explicit nsTimer(nsIEventTarget
* aTarget
)
231 : mImpl(new nsTimerImpl(this, aTarget
)) {}
236 friend class TimerThread
;
237 friend class nsTimerEvent
;
239 NS_DECL_THREADSAFE_ISUPPORTS
240 NS_FORWARD_SAFE_NSITIMER(mImpl
);
242 virtual size_t SizeOfIncludingThis(
243 mozilla::MallocSizeOf aMallocSizeOf
) const override
;
245 // Create a timer targeting the given target. nullptr indicates that the
246 // current thread should be used as the timer's target.
247 static RefPtr
<nsTimer
> WithEventTarget(nsIEventTarget
* aTarget
);
249 static nsresult
XPCOMConstructor(nsISupports
* aOuter
, REFNSIID aIID
,
253 // nsTimerImpl holds a strong ref to us. When our refcount goes to 1, we will
254 // null this to break the cycle.
255 RefPtr
<nsTimerImpl
> mImpl
;
258 // A class that holds on to an nsTimerImpl. This lets the nsTimerImpl object
259 // directly instruct its holder to forget the timer, avoiding list lookups.
260 class nsTimerImplHolder
{
262 explicit nsTimerImplHolder(nsTimerImpl
* aTimerImpl
) : mTimerImpl(aTimerImpl
) {
264 mTimerImpl
->SetHolder(this);
268 ~nsTimerImplHolder() {
270 mTimerImpl
->SetHolder(nullptr);
274 void Forget(nsTimerImpl
* aTimerImpl
) {
275 if (MOZ_UNLIKELY(!mTimerImpl
)) {
278 MOZ_ASSERT(aTimerImpl
== mTimerImpl
);
279 mTimerImpl
->SetHolder(nullptr);
280 mTimerImpl
= nullptr;
284 RefPtr
<nsTimerImpl
> mTimerImpl
;
287 #endif /* nsTimerImpl_h___ */