Line data Source code
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 83 : ~nsTimerImpl()
44 83 : {
45 83 : MOZ_ASSERT(!mHolder);
46 83 : }
47 :
48 : public:
49 : typedef mozilla::TimeStamp TimeStamp;
50 :
51 : explicit nsTimerImpl(nsITimer* aTimer);
52 942 : 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 : bool CancelCheckIfFiring();
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 90 : int32_t GetGeneration()
69 : {
70 90 : return mGeneration;
71 : }
72 :
73 : struct Callback {
74 2222 : Callback() :
75 : mType(Type::Unknown),
76 : mName(Nothing),
77 2222 : mClosure(nullptr)
78 : {
79 2222 : mCallback.c = nullptr;
80 2222 : }
81 :
82 : Callback(const Callback& other) = delete;
83 : Callback& operator=(const Callback& other) = delete;
84 :
85 1953 : ~Callback()
86 1953 : {
87 1953 : if (mType == Type::Interface) {
88 60 : NS_RELEASE(mCallback.i);
89 1893 : } else if (mType == Type::Observer) {
90 6 : NS_RELEASE(mCallback.o);
91 : }
92 1953 : }
93 :
94 1878 : void swap(Callback& other)
95 : {
96 1878 : std::swap(mType, other.mType);
97 1878 : std::swap(mCallback, other.mCallback);
98 1878 : std::swap(mName, other.mName);
99 1878 : std::swap(mClosure, other.mClosure);
100 1878 : }
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 TimeDuration& aDelay, uint32_t aType,
136 : Callback&& newCallback);
137 :
138 85 : Callback& GetCallback()
139 : {
140 85 : mMutex.AssertCurrentThreadOwns();
141 85 : if (mCallback.mType == Callback::Type::Unknown) {
142 5 : return mCallbackDuringFire;
143 : }
144 :
145 80 : return mCallback;
146 : }
147 :
148 37 : bool IsRepeating() const
149 : {
150 : static_assert(nsITimer::TYPE_ONE_SHOT < nsITimer::TYPE_REPEATING_SLACK,
151 : "invalid ordering of timer types!");
152 : static_assert(
153 : nsITimer::TYPE_REPEATING_SLACK < nsITimer::TYPE_REPEATING_PRECISE,
154 : "invalid ordering of timer types!");
155 : static_assert(
156 : nsITimer::TYPE_REPEATING_PRECISE <
157 : nsITimer::TYPE_REPEATING_PRECISE_CAN_SKIP,
158 : "invalid ordering of timer types!");
159 60 : return mType >= nsITimer::TYPE_REPEATING_SLACK &&
160 60 : mType < nsITimer::TYPE_ONE_SHOT_LOW_PRIORITY;
161 : }
162 :
163 6 : bool IsLowPriority() const
164 : {
165 12 : return mType == nsITimer::TYPE_ONE_SHOT_LOW_PRIORITY ||
166 12 : mType == nsITimer::TYPE_REPEATING_SLACK_LOW_PRIORITY;
167 : }
168 :
169 18 : bool IsSlack() const
170 : {
171 36 : return mType == nsITimer::TYPE_REPEATING_SLACK ||
172 36 : mType == nsITimer::TYPE_REPEATING_SLACK_LOW_PRIORITY;
173 : }
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 : TimeDuration mDelay;
204 : // Updated only after this timer has been removed from the timer thread.
205 : 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 : const 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 436 : nsTimer() : mImpl(new nsTimerImpl(this)) {}
225 :
226 : friend class TimerThread;
227 : friend class nsTimerEvent;
228 : friend struct TimerAdditionComparator;
229 :
230 : NS_DECL_THREADSAFE_ISUPPORTS
231 1830 : NS_FORWARD_SAFE_NSITIMER(mImpl);
232 :
233 : virtual size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const override;
234 :
235 : private:
236 : // nsTimerImpl holds a strong ref to us. When our refcount goes to 1, we will
237 : // null this to break the cycle.
238 : RefPtr<nsTimerImpl> mImpl;
239 : };
240 :
241 : // A class that holds on to an nsTimerImpl. This lets the nsTimerImpl object
242 : // directly instruct its holder to forget the timer, avoiding list lookups.
243 : class nsTimerImplHolder
244 : {
245 : public:
246 306 : explicit nsTimerImplHolder(nsTimerImpl* aTimerImpl)
247 306 : : mTimerImpl(aTimerImpl)
248 : {
249 306 : if (mTimerImpl) {
250 306 : mTimerImpl->SetHolder(this);
251 : }
252 306 : }
253 :
254 180 : ~nsTimerImplHolder()
255 180 : {
256 180 : if (mTimerImpl) {
257 0 : mTimerImpl->SetHolder(nullptr);
258 : }
259 180 : }
260 :
261 : void
262 174 : Forget(nsTimerImpl* aTimerImpl)
263 : {
264 174 : if (MOZ_UNLIKELY(!mTimerImpl)) {
265 0 : return;
266 : }
267 174 : MOZ_ASSERT(aTimerImpl == mTimerImpl);
268 174 : mTimerImpl->SetHolder(nullptr);
269 174 : mTimerImpl = nullptr;
270 : }
271 :
272 : protected:
273 : RefPtr<nsTimerImpl> mTimerImpl;
274 : };
275 :
276 :
277 : #endif /* nsTimerImpl_h___ */
|