1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef nsTimerImpl_h___
#define nsTimerImpl_h___
#include "nsITimer.h"
#include "nsIEventTarget.h"
#include "nsIObserver.h"
#include "nsCOMPtr.h"
#include "nsString.h"
#include "mozilla/Mutex.h"
#include "mozilla/StaticMutex.h"
#include "mozilla/TimeStamp.h"
#include "mozilla/Variant.h"
#include "mozilla/Logging.h"
extern mozilla::LogModule* GetTimerLog();
#define NS_TIMER_CID \
{/* 5ff24248-1dd2-11b2-8427-fbab44f29bc8 */ \
0x5ff24248, \
0x1dd2, \
0x11b2, \
{0x84, 0x27, 0xfb, 0xab, 0x44, 0xf2, 0x9b, 0xc8}}
class nsIObserver;
namespace mozilla {
class LogModule;
}
// TimerThread, nsTimerEvent, and nsTimer have references to these. nsTimer has
// a separate lifecycle so we can Cancel() the underlying timer when the user of
// the nsTimer has let go of its last reference.
class nsTimerImpl {
~nsTimerImpl() {
MOZ_ASSERT(!mIsInTimerThread);
// The nsITimer interface requires that its users keep a reference to the
// timers they use while those timers are initialized but have not yet
// fired. If this assert ever fails, it is a bug in the code that created
// and used the timer.
//
// Further, note that this should never fail even with a misbehaving user,
// because nsTimer::Release checks for a refcount of 1 with an armed timer
// (a timer whose only reference is from the timer thread) and when it hits
// this will remove the timer from the timer thread and thus destroy the
// last reference, preventing this situation from occurring.
MOZ_ASSERT(
mCallback.is<UnknownCallback>() || mEventTarget->IsOnCurrentThread(),
"Must not release mCallback off-target without canceling");
}
public:
typedef mozilla::TimeStamp TimeStamp;
nsTimerImpl(nsITimer* aTimer, nsIEventTarget* aTarget);
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(nsTimerImpl)
NS_DECL_NON_VIRTUAL_NSITIMER
static nsresult Startup();
static void Shutdown();
void SetDelayInternal(uint32_t aDelay, TimeStamp aBase = TimeStamp::Now());
void CancelImpl(bool aClearITimer);
void Fire(uint64_t aTimerSeq);
struct UnknownCallback {};
using InterfaceCallback = nsCOMPtr<nsITimerCallback>;
using ObserverCallback = nsCOMPtr<nsIObserver>;
/// A raw function pointer and its closed-over state.
struct FuncCallback {
nsTimerCallbackFunc mFunc;
void* mClosure;
};
using ClosureCallback = std::function<void(nsITimer*)>;
using Callback =
mozilla::Variant<UnknownCallback, InterfaceCallback, ObserverCallback,
FuncCallback, ClosureCallback>;
nsresult InitCommon(const mozilla::TimeDuration& aDelay, uint32_t aType,
const nsACString& aName, Callback&& newCallback,
const mozilla::MutexAutoLock& aProofOfLock)
MOZ_REQUIRES(mMutex);
Callback& GetCallback() MOZ_REQUIRES(mMutex) {
mMutex.AssertCurrentThreadOwns();
return mCallback;
}
bool IsRepeating() const {
static_assert(nsITimer::TYPE_ONE_SHOT < nsITimer::TYPE_REPEATING_SLACK,
"invalid ordering of timer types!");
static_assert(
nsITimer::TYPE_REPEATING_SLACK < nsITimer::TYPE_REPEATING_PRECISE,
"invalid ordering of timer types!");
static_assert(nsITimer::TYPE_REPEATING_PRECISE <
nsITimer::TYPE_REPEATING_PRECISE_CAN_SKIP,
"invalid ordering of timer types!");
return mType >= nsITimer::TYPE_REPEATING_SLACK &&
mType < nsITimer::TYPE_ONE_SHOT_LOW_PRIORITY;
}
bool IsLowPriority() const {
return mType == nsITimer::TYPE_ONE_SHOT_LOW_PRIORITY ||
mType == nsITimer::TYPE_REPEATING_SLACK_LOW_PRIORITY;
}
bool IsSlack() const {
return mType == nsITimer::TYPE_REPEATING_SLACK ||
mType == nsITimer::TYPE_REPEATING_SLACK_LOW_PRIORITY;
}
// Caution: Only call this when you hold TimerThread's monitor!
bool IsInTimerThread() const { return mIsInTimerThread; }
// Caution: Only call this when you hold TimerThread's monitor!
void SetIsInTimerThread(bool aIsInTimerThread) {
mIsInTimerThread = aIsInTimerThread;
}
nsCOMPtr<nsIEventTarget> mEventTarget;
void LogFiring(const Callback& aCallback, uint8_t aType, uint32_t aDelay);
nsresult InitWithClosureCallback(std::function<void(nsITimer*)>&& aCallback,
const mozilla::TimeDuration& aDelay,
uint32_t aType,
const nsACString& aNameString);
// Is this timer currently referenced from a TimerThread::Entry in the list?
// ALL accesses to mIsInTimerThread are under the TimerThread's Monitor lock,
// so consistency is guaranteed by that.
bool mIsInTimerThread;
// These members are set by the initiating thread, when the timer's type is
// changed and during the period where it fires on that thread.
uint8_t mType;
// The global sequence number of this timer, updated each time the timer is
// initialized so one-shot timers can be canceled and re-initialized by the
// arming thread without any bad race conditions.
// Updated only after this timer has been removed from the timer thread.
uint64_t mTimerSeq MOZ_GUARDED_BY(mMutex);
mozilla::TimeDuration mDelay MOZ_GUARDED_BY(mMutex);
// Never updated while in the TimerThread's timer list. Only updated
// before adding to that list or during nsTimerImpl::Fire(), when it has
// been removed from the TimerThread's list.
mozilla::TimeStamp mTimeout MOZ_GUARDED_BY(mMutex);
RefPtr<nsITimer> mITimer MOZ_GUARDED_BY(mMutex);
mozilla::Mutex mMutex;
nsCString mName MOZ_GUARDED_BY(mMutex);
Callback mCallback MOZ_GUARDED_BY(mMutex);
// Counter because in rare cases we can Fire reentrantly
unsigned int mFiring MOZ_GUARDED_BY(mMutex);
static mozilla::StaticMutex sDeltaMutex;
static double sDeltaSum MOZ_GUARDED_BY(sDeltaMutex);
static double sDeltaSumSquared MOZ_GUARDED_BY(sDeltaMutex);
static double sDeltaNum MOZ_GUARDED_BY(sDeltaMutex);
};
class nsTimer final : public nsITimer {
explicit nsTimer(nsIEventTarget* aTarget)
: mImpl(new nsTimerImpl(this, aTarget)) {}
virtual ~nsTimer();
public:
friend class TimerThread;
friend class nsTimerEvent;
NS_DECL_THREADSAFE_ISUPPORTS
NS_FORWARD_SAFE_NSITIMER(mImpl);
// NOTE: This constructor is not exposed on `nsITimer` as NS_FORWARD_SAFE_
// does not support forwarding rvalue references.
nsresult InitWithClosureCallback(std::function<void(nsITimer*)>&& aCallback,
const mozilla::TimeDuration& aDelay,
uint32_t aType,
const nsACString& aNameString) {
return mImpl ? mImpl->InitWithClosureCallback(std::move(aCallback), aDelay,
aType, aNameString)
: NS_ERROR_NULL_POINTER;
}
// Create a timer targeting the given target. nullptr indicates that the
// current thread should be used as the timer's target.
static RefPtr<nsTimer> WithEventTarget(nsIEventTarget* aTarget);
static nsresult XPCOMConstructor(REFNSIID aIID, void** aResult);
private:
// nsTimerImpl holds a strong ref to us. When our refcount goes to 1, we will
// null this to break the cycle.
RefPtr<nsTimerImpl> mImpl;
};
class nsTimerManager final : public nsITimerManager {
public:
NS_DECL_ISUPPORTS
NS_DECL_NSITIMERMANAGER
private:
~nsTimerManager() = default;
};
#endif /* nsTimerImpl_h___ */
|