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