/src/mozilla-central/xpcom/threads/nsTimerImpl.h
Line | Count | Source (jump to first uncovered line) |
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/. */ |
6 | | |
7 | | #ifndef nsTimerImpl_h___ |
8 | | #define nsTimerImpl_h___ |
9 | | |
10 | | #include "nsITimer.h" |
11 | | #include "nsIEventTarget.h" |
12 | | #include "nsIObserver.h" |
13 | | |
14 | | #include "nsCOMPtr.h" |
15 | | |
16 | | #include "mozilla/Attributes.h" |
17 | | #include "mozilla/Logging.h" |
18 | | #include "mozilla/Mutex.h" |
19 | | #include "mozilla/TimeStamp.h" |
20 | | #include "mozilla/Variant.h" |
21 | | |
22 | | #ifdef MOZ_TASK_TRACER |
23 | | #include "TracedTaskCommon.h" |
24 | | #endif |
25 | | |
26 | | extern mozilla::LogModule* GetTimerLog(); |
27 | | |
28 | | #define NS_TIMER_CID \ |
29 | | { /* 5ff24248-1dd2-11b2-8427-fbab44f29bc8 */ \ |
30 | | 0x5ff24248, \ |
31 | | 0x1dd2, \ |
32 | | 0x11b2, \ |
33 | | {0x84, 0x27, 0xfb, 0xab, 0x44, 0xf2, 0x9b, 0xc8} \ |
34 | | } |
35 | | |
36 | | class nsTimerImplHolder; |
37 | | |
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. |
41 | | class nsTimerImpl |
42 | | { |
43 | | ~nsTimerImpl() |
44 | 0 | { |
45 | 0 | MOZ_ASSERT(!mHolder); |
46 | 0 | } |
47 | | |
48 | | public: |
49 | | typedef mozilla::TimeStamp TimeStamp; |
50 | | |
51 | | explicit nsTimerImpl(nsITimer* aTimer); |
52 | | NS_INLINE_DECL_THREADSAFE_REFCOUNTING(nsTimerImpl) |
53 | | NS_DECL_NON_VIRTUAL_NSITIMER |
54 | | |
55 | | static nsresult Startup(); |
56 | | static void Shutdown(); |
57 | | |
58 | | void SetDelayInternal(uint32_t aDelay, TimeStamp aBase = TimeStamp::Now()); |
59 | | void CancelImpl(bool aClearITimer); |
60 | | |
61 | | void Fire(int32_t aGeneration); |
62 | | |
63 | | #ifdef MOZ_TASK_TRACER |
64 | | void GetTLSTraceInfo(); |
65 | | mozilla::tasktracer::TracedTaskCommon GetTracedTask(); |
66 | | #endif |
67 | | |
68 | | int32_t GetGeneration() |
69 | 86 | { |
70 | 86 | return mGeneration; |
71 | 86 | } |
72 | | |
73 | | struct Callback { |
74 | | Callback() : |
75 | | mType(Type::Unknown), |
76 | | mName(Nothing), |
77 | | mClosure(nullptr) |
78 | 464 | { |
79 | 464 | mCallback.c = nullptr; |
80 | 464 | } |
81 | | |
82 | | Callback(const Callback& other) = delete; |
83 | | Callback& operator=(const Callback& other) = delete; |
84 | | |
85 | | ~Callback() |
86 | 258 | { |
87 | 258 | if (mType == Type::Interface) { |
88 | 0 | NS_RELEASE(mCallback.i); |
89 | 258 | } else if (mType == Type::Observer) { |
90 | 0 | NS_RELEASE(mCallback.o); |
91 | 0 | } |
92 | 258 | } |
93 | | |
94 | | void swap(Callback& other) |
95 | 258 | { |
96 | 258 | std::swap(mType, other.mType); |
97 | 258 | std::swap(mCallback, other.mCallback); |
98 | 258 | std::swap(mName, other.mName); |
99 | 258 | std::swap(mClosure, other.mClosure); |
100 | 258 | } |
101 | | |
102 | | enum class Type : uint8_t { |
103 | | Unknown = 0, |
104 | | Interface = 1, |
105 | | Function = 2, |
106 | | Observer = 3, |
107 | | }; |
108 | | Type mType; |
109 | | |
110 | | union CallbackUnion |
111 | | { |
112 | | nsTimerCallbackFunc c; |
113 | | // These refcounted references are managed manually, as they are in a union |
114 | | nsITimerCallback* MOZ_OWNING_REF i; |
115 | | nsIObserver* MOZ_OWNING_REF o; |
116 | | } mCallback; |
117 | | |
118 | | // |Name| is a tagged union type representing one of (a) nothing, (b) a |
119 | | // string, or (c) a function. mozilla::Variant doesn't naturally handle the |
120 | | // "nothing" case, so we define a dummy type and value (which is unused and |
121 | | // so the exact value doesn't matter) for it. |
122 | | typedef const int NameNothing; |
123 | | typedef const char* NameString; |
124 | | typedef nsTimerNameCallbackFunc NameFunc; |
125 | | typedef mozilla::Variant<NameNothing, NameString, NameFunc> Name; |
126 | | static const NameNothing Nothing; |
127 | | Name mName; |
128 | | |
129 | | void* mClosure; |
130 | | }; |
131 | | |
132 | | nsresult InitCommon(uint32_t aDelayMS, uint32_t aType, |
133 | | Callback&& newCallback); |
134 | | |
135 | | nsresult InitCommon(const mozilla::TimeDuration& aDelay, uint32_t aType, |
136 | | Callback&& newCallback); |
137 | | |
138 | | Callback& GetCallback() |
139 | 0 | { |
140 | 0 | mMutex.AssertCurrentThreadOwns(); |
141 | 0 | if (mCallback.mType == Callback::Type::Unknown) { |
142 | 0 | return mCallbackDuringFire; |
143 | 0 | } |
144 | 0 | |
145 | 0 | return mCallback; |
146 | 0 | } |
147 | | |
148 | | bool IsRepeating() const |
149 | 0 | { |
150 | 0 | static_assert(nsITimer::TYPE_ONE_SHOT < nsITimer::TYPE_REPEATING_SLACK, |
151 | 0 | "invalid ordering of timer types!"); |
152 | 0 | static_assert( |
153 | 0 | nsITimer::TYPE_REPEATING_SLACK < nsITimer::TYPE_REPEATING_PRECISE, |
154 | 0 | "invalid ordering of timer types!"); |
155 | 0 | static_assert( |
156 | 0 | nsITimer::TYPE_REPEATING_PRECISE < |
157 | 0 | nsITimer::TYPE_REPEATING_PRECISE_CAN_SKIP, |
158 | 0 | "invalid ordering of timer types!"); |
159 | 0 | return mType >= nsITimer::TYPE_REPEATING_SLACK && |
160 | 0 | mType < nsITimer::TYPE_ONE_SHOT_LOW_PRIORITY; |
161 | 0 | } |
162 | | |
163 | | bool IsLowPriority() const |
164 | 0 | { |
165 | 0 | return mType == nsITimer::TYPE_ONE_SHOT_LOW_PRIORITY || |
166 | 0 | mType == nsITimer::TYPE_REPEATING_SLACK_LOW_PRIORITY; |
167 | 0 | } |
168 | | |
169 | | bool IsSlack() const |
170 | 0 | { |
171 | 0 | return mType == nsITimer::TYPE_REPEATING_SLACK || |
172 | 0 | mType == nsITimer::TYPE_REPEATING_SLACK_LOW_PRIORITY; |
173 | 0 | } |
174 | | |
175 | | void GetName(nsACString& aName); |
176 | | |
177 | | void SetHolder(nsTimerImplHolder* aHolder); |
178 | | |
179 | | nsCOMPtr<nsIEventTarget> mEventTarget; |
180 | | |
181 | | void LogFiring(const Callback& aCallback, uint8_t aType, uint32_t aDelay); |
182 | | |
183 | | nsresult InitWithFuncCallbackCommon(nsTimerCallbackFunc aFunc, |
184 | | void* aClosure, |
185 | | uint32_t aDelay, |
186 | | uint32_t aType, |
187 | | const Callback::Name& aName); |
188 | | |
189 | | // This weak reference must be cleared by the nsTimerImplHolder by calling |
190 | | // SetHolder(nullptr) before the holder is destroyed. |
191 | | nsTimerImplHolder* mHolder; |
192 | | |
193 | | // These members are set by the initiating thread, when the timer's type is |
194 | | // changed and during the period where it fires on that thread. |
195 | | uint8_t mType; |
196 | | |
197 | | // The generation number of this timer, re-generated each time the timer is |
198 | | // initialized so one-shot timers can be canceled and re-initialized by the |
199 | | // arming thread without any bad race conditions. |
200 | | // Updated only after this timer has been removed from the timer thread. |
201 | | int32_t mGeneration; |
202 | | |
203 | | mozilla::TimeDuration mDelay; |
204 | | // Updated only after this timer has been removed from the timer thread. |
205 | | mozilla::TimeStamp mTimeout; |
206 | | |
207 | | #ifdef MOZ_TASK_TRACER |
208 | | mozilla::tasktracer::TracedTaskCommon mTracedTask; |
209 | | #endif |
210 | | |
211 | | static double sDeltaSum; |
212 | | static double sDeltaSumSquared; |
213 | | static double sDeltaNum; |
214 | | RefPtr<nsITimer> mITimer; |
215 | | mozilla::Mutex mMutex; |
216 | | Callback mCallback; |
217 | | Callback mCallbackDuringFire; |
218 | | }; |
219 | | |
220 | | class nsTimer final : public nsITimer |
221 | | { |
222 | | virtual ~nsTimer(); |
223 | | public: |
224 | 103 | nsTimer() : mImpl(new nsTimerImpl(this)) {} |
225 | | |
226 | | friend class TimerThread; |
227 | | friend class nsTimerEvent; |
228 | | |
229 | | NS_DECL_THREADSAFE_ISUPPORTS |
230 | | NS_FORWARD_SAFE_NSITIMER(mImpl); |
231 | | |
232 | | virtual size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const override; |
233 | | |
234 | | private: |
235 | | // nsTimerImpl holds a strong ref to us. When our refcount goes to 1, we will |
236 | | // null this to break the cycle. |
237 | | RefPtr<nsTimerImpl> mImpl; |
238 | | }; |
239 | | |
240 | | // A class that holds on to an nsTimerImpl. This lets the nsTimerImpl object |
241 | | // directly instruct its holder to forget the timer, avoiding list lookups. |
242 | | class nsTimerImplHolder |
243 | | { |
244 | | public: |
245 | | explicit nsTimerImplHolder(nsTimerImpl* aTimerImpl) |
246 | | : mTimerImpl(aTimerImpl) |
247 | 90 | { |
248 | 90 | if (mTimerImpl) { |
249 | 90 | mTimerImpl->SetHolder(this); |
250 | 90 | } |
251 | 90 | } |
252 | | |
253 | | ~nsTimerImplHolder() |
254 | 90 | { |
255 | 90 | if (mTimerImpl) { |
256 | 0 | mTimerImpl->SetHolder(nullptr); |
257 | 0 | } |
258 | 90 | } |
259 | | |
260 | | void |
261 | | Forget(nsTimerImpl* aTimerImpl) |
262 | 4 | { |
263 | 4 | if (MOZ_UNLIKELY(!mTimerImpl)) { |
264 | 0 | return; |
265 | 0 | } |
266 | 4 | MOZ_ASSERT(aTimerImpl == mTimerImpl); |
267 | 4 | mTimerImpl->SetHolder(nullptr); |
268 | 4 | mTimerImpl = nullptr; |
269 | 4 | } |
270 | | |
271 | | protected: |
272 | | RefPtr<nsTimerImpl> mTimerImpl; |
273 | | }; |
274 | | |
275 | | |
276 | | #endif /* nsTimerImpl_h___ */ |