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/Mutex.h"
18 #include "mozilla/TimeStamp.h"
19 #include "mozilla/Variant.h"
20 #include "mozilla/Logging.h"
21 
22 extern mozilla::LogModule* GetTimerLog();
23 
24 #define NS_TIMER_CID                                 \
25   { /* 5ff24248-1dd2-11b2-8427-fbab44f29bc8 */       \
26     0x5ff24248, 0x1dd2, 0x11b2, {                    \
27       0x84, 0x27, 0xfb, 0xab, 0x44, 0xf2, 0x9b, 0xc8 \
28     }                                                \
29   }
30 
31 class nsIObserver;
32 class nsTimerImplHolder;
33 
34 namespace mozilla {
35 class LogModule;
36 }
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 {
~nsTimerImpl()42   ~nsTimerImpl() { MOZ_ASSERT(!mHolder); }
43 
44  public:
45   typedef mozilla::TimeStamp TimeStamp;
46 
47   nsTimerImpl(nsITimer* aTimer, nsIEventTarget* aTarget);
48   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(nsTimerImpl)
49   NS_DECL_NON_VIRTUAL_NSITIMER
50 
51   static nsresult Startup();
52   static void Shutdown();
53 
54   void SetDelayInternal(uint32_t aDelay, TimeStamp aBase = TimeStamp::Now());
55   void CancelImpl(bool aClearITimer);
56 
57   void Fire(int32_t aGeneration);
58 
GetGeneration()59   int32_t GetGeneration() { return mGeneration; }
60 
61   struct UnknownCallback {};
62 
63   using InterfaceCallback = nsCOMPtr<nsITimerCallback>;
64 
65   using ObserverCallback = nsCOMPtr<nsIObserver>;
66 
67   /// A raw function pointer and its closed-over state, along with its name for
68   /// logging purposes.
69   struct FuncCallback {
70     nsTimerCallbackFunc mFunc;
71     void* mClosure;
72     const char* mName;
73   };
74 
75   /// A callback defined by an owned closure and its name for logging purposes.
76   struct ClosureCallback {
77     std::function<void(nsITimer*)> mFunc;
78     const char* mName;
79   };
80 
81   using Callback =
82       mozilla::Variant<UnknownCallback, InterfaceCallback, ObserverCallback,
83                        FuncCallback, ClosureCallback>;
84 
85   nsresult InitCommon(uint32_t aDelayMS, uint32_t aType,
86                       Callback&& newCallback);
87 
88   nsresult InitCommon(const mozilla::TimeDuration& aDelay, uint32_t aType,
89                       Callback&& newCallback);
90 
GetCallback()91   Callback& GetCallback() {
92     mMutex.AssertCurrentThreadOwns();
93     return mCallback;
94   }
95 
IsRepeating()96   bool IsRepeating() const {
97     static_assert(nsITimer::TYPE_ONE_SHOT < nsITimer::TYPE_REPEATING_SLACK,
98                   "invalid ordering of timer types!");
99     static_assert(
100         nsITimer::TYPE_REPEATING_SLACK < nsITimer::TYPE_REPEATING_PRECISE,
101         "invalid ordering of timer types!");
102     static_assert(nsITimer::TYPE_REPEATING_PRECISE <
103                       nsITimer::TYPE_REPEATING_PRECISE_CAN_SKIP,
104                   "invalid ordering of timer types!");
105     return mType >= nsITimer::TYPE_REPEATING_SLACK &&
106            mType < nsITimer::TYPE_ONE_SHOT_LOW_PRIORITY;
107   }
108 
IsLowPriority()109   bool IsLowPriority() const {
110     return mType == nsITimer::TYPE_ONE_SHOT_LOW_PRIORITY ||
111            mType == nsITimer::TYPE_REPEATING_SLACK_LOW_PRIORITY;
112   }
113 
IsSlack()114   bool IsSlack() const {
115     return mType == nsITimer::TYPE_REPEATING_SLACK ||
116            mType == nsITimer::TYPE_REPEATING_SLACK_LOW_PRIORITY;
117   }
118 
119   void GetName(nsACString& aName);
120 
121   void SetHolder(nsTimerImplHolder* aHolder);
122 
123   nsCOMPtr<nsIEventTarget> mEventTarget;
124 
125   void LogFiring(const Callback& aCallback, uint8_t aType, uint32_t aDelay);
126 
127   nsresult InitWithClosureCallback(std::function<void(nsITimer*)>&& aCallback,
128                                    const mozilla::TimeDuration& aDelay,
129                                    uint32_t aType, const char* aNameString);
130 
131   // This weak reference must be cleared by the nsTimerImplHolder by calling
132   // SetHolder(nullptr) before the holder is destroyed.
133   nsTimerImplHolder* mHolder;
134 
135   // These members are set by the initiating thread, when the timer's type is
136   // changed and during the period where it fires on that thread.
137   uint8_t mType;
138 
139   // The generation number of this timer, re-generated each time the timer is
140   // initialized so one-shot timers can be canceled and re-initialized by the
141   // arming thread without any bad race conditions.
142   // Updated only after this timer has been removed from the timer thread.
143   int32_t mGeneration;
144 
145   mozilla::TimeDuration mDelay;
146   // Updated only after this timer has been removed from the timer thread.
147   mozilla::TimeStamp mTimeout;
148 
149   static double sDeltaSum;
150   static double sDeltaSumSquared;
151   static double sDeltaNum;
152   RefPtr<nsITimer> mITimer;
153   mozilla::Mutex mMutex;
154   Callback mCallback;
155   // Counter because in rare cases we can Fire reentrantly
156   unsigned int mFiring;
157 };
158 
159 class nsTimer final : public nsITimer {
nsTimer(nsIEventTarget * aTarget)160   explicit nsTimer(nsIEventTarget* aTarget)
161       : mImpl(new nsTimerImpl(this, aTarget)) {}
162 
163   virtual ~nsTimer();
164 
165  public:
166   friend class TimerThread;
167   friend class nsTimerEvent;
168 
169   NS_DECL_THREADSAFE_ISUPPORTS
170   NS_FORWARD_SAFE_NSITIMER(mImpl);
171 
172   // NOTE: This constructor is not exposed on `nsITimer` as NS_FORWARD_SAFE_
173   // does not support forwarding rvalue references.
InitWithClosureCallback(std::function<void (nsITimer *)> && aCallback,const mozilla::TimeDuration & aDelay,uint32_t aType,const char * aNameString)174   nsresult InitWithClosureCallback(std::function<void(nsITimer*)>&& aCallback,
175                                    const mozilla::TimeDuration& aDelay,
176                                    uint32_t aType, const char* aNameString) {
177     return mImpl ? mImpl->InitWithClosureCallback(std::move(aCallback), aDelay,
178                                                   aType, aNameString)
179                  : NS_ERROR_NULL_POINTER;
180   }
181 
182   virtual size_t SizeOfIncludingThis(
183       mozilla::MallocSizeOf aMallocSizeOf) const override;
184 
185   // Create a timer targeting the given target.  nullptr indicates that the
186   // current thread should be used as the timer's target.
187   static RefPtr<nsTimer> WithEventTarget(nsIEventTarget* aTarget);
188 
189   static nsresult XPCOMConstructor(nsISupports* aOuter, REFNSIID aIID,
190                                    void** aResult);
191 
192  private:
193   // nsTimerImpl holds a strong ref to us. When our refcount goes to 1, we will
194   // null this to break the cycle.
195   RefPtr<nsTimerImpl> mImpl;
196 };
197 
198 // A class that holds on to an nsTimerImpl.  This lets the nsTimerImpl object
199 // directly instruct its holder to forget the timer, avoiding list lookups.
200 class nsTimerImplHolder {
201  public:
nsTimerImplHolder(nsTimerImpl * aTimerImpl)202   explicit nsTimerImplHolder(nsTimerImpl* aTimerImpl) : mTimerImpl(aTimerImpl) {
203     if (mTimerImpl) {
204       mTimerImpl->SetHolder(this);
205     }
206   }
207 
~nsTimerImplHolder()208   ~nsTimerImplHolder() {
209     if (mTimerImpl) {
210       mTimerImpl->SetHolder(nullptr);
211     }
212   }
213 
Forget(nsTimerImpl * aTimerImpl)214   void Forget(nsTimerImpl* aTimerImpl) {
215     if (MOZ_UNLIKELY(!mTimerImpl)) {
216       return;
217     }
218     MOZ_ASSERT(aTimerImpl == mTimerImpl);
219     mTimerImpl->SetHolder(nullptr);
220     mTimerImpl = nullptr;
221   }
222 
223  protected:
224   RefPtr<nsTimerImpl> mTimerImpl;
225 };
226 
227 #endif /* nsTimerImpl_h___ */
228