/* -*- 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/. */
#ifdef XP_WIN # include <process.h> # ifndef getpid # define getpid _getpid # endif #else # include <unistd.h> #endif
using mozilla::Atomic; using mozilla::LogLevel; using mozilla::MakeRefPtr; using mozilla::MutexAutoLock; using mozilla::TimeDuration; using mozilla::TimeStamp;
// Holds the timer thread and manages all interactions with it // under a locked mutex. This wrapper is not destroyed until after // nsThreadManager shutdown to ensure we don't UAF during an offthread access to // the timer thread. class TimerThreadWrapper { public:
constexpr TimerThreadWrapper() : mThread(nullptr) {};
~TimerThreadWrapper() = default;
{
mozilla::StaticMutexAutoLock lock(sMutex); if (!mThread) { return;
}
thread = mThread;
} // Shutdown calls |nsTimerImpl::Cancel| which needs to make a call into // |RemoveTimer|. This can't be done under the lock.
thread->Shutdown();
// This module prints info about which timers are firing, which is useful for // wakeups for the purposes of power profiling. Set the following environment // variable before starting the browser. // // MOZ_LOG=TimerFirings:4 // // Then a line will be printed for every timer that fires. // // If you redirect this output to a file called "out", you can then // post-process it with a command something like the following. // // cat out | grep timer | sort | uniq -c | sort -r -n // // This will show how often each unique line appears, with the most common ones // first. // // More detailed docs are here: // https://developer.mozilla.org/en-US/docs/Mozilla/Performance/TimerFirings_logging // static mozilla::LazyLogModule sTimerFiringsLog("TimerFirings");
if (count == 1) { // Last ref, in nsTimerImpl::mITimer. Make sure the cycle is broken.
mImpl->CancelImpl(true);
} elseif (count == 0) { deletethis;
}
return count;
}
nsTimerImpl::nsTimerImpl(nsITimer* aTimer, nsIEventTarget* aTarget)
: mEventTarget(aTarget),
mIsInTimerThread(false),
mType(0),
mGeneration(0),
mITimer(aTimer),
mMutex("nsTimerImpl::mMutex"),
mCallback(UnknownCallback{}),
mFiring(0) { // XXX some code creates timers during xpcom shutdown, when threads are no // longer available, so we cannot turn this on yet. // MOZ_ASSERT(mEventTarget);
}
// If we have an existing callback, using `swap` ensures it's destroyed after // the mutex is unlocked in our caller.
std::swap(mCallback, newCallback);
++mGeneration;
// The swap ensures our callback isn't dropped until after the mutex is // unlocked.
std::swap(cbTrash, mCallback);
++mGeneration;
// Don't clear this if we're firing; once Fire returns, we'll get this call // again. if (aClearITimer && !mFiring) {
MOZ_RELEASE_ASSERT(
mITimer, "mITimer was nulled already! " "This indicates that someone has messed up the refcount on nsTimer!");
timerTrash.swap(mITimer);
}
}
}
nsresult nsTimerImpl::SetDelay(uint32_t aDelay) {
MutexAutoLock lock(mMutex); if (GetCallback().is<UnknownCallback>() && !IsRepeating()) { // This may happen if someone tries to re-use a one-shot timer // by re-setting delay instead of reinitializing the timer.
NS_ERROR( "nsITimer->SetDelay() called when the " "one-shot timer is not set up."); return NS_ERROR_NOT_INITIALIZED;
}
nsresult nsTimerImpl::SetType(uint32_t aType) {
MutexAutoLock lock(mMutex);
mType = (uint8_t)aType; // XXX if this is called, we should change the actual type.. this could effect // repeating timers. we need to ensure in Fire() that if mType has changed // during the callback that we don't end up with the timer in the queue twice. return NS_OK;
}
{ // Don't fire callbacks or fiddle with refcounts when the mutex is locked. // If some other thread Cancels/Inits after this, they're just too late.
MutexAutoLock lock(mMutex); if (aGeneration != mGeneration) { // This timer got rescheduled or cancelled before we fired, so ignore this // firing return;
}
// We modify mTimeout, so we must not be in the current TimerThread's // mTimers list.
MOZ_ASSERT(!mIsInTimerThread);
++mFiring;
callbackDuringFire = mCallback;
oldType = mType;
oldDelay = mDelay.ToMilliseconds();
oldTimeout = mTimeout; // Ensure that the nsITimer does not unhook from the nsTimerImpl during // Fire; this will cause null pointer crashes if the user of the timer drops // its reference, and then uses the nsITimer* passed in the callback.
timer = mITimer;
}
AUTO_PROFILER_LABEL("nsTimerImpl::Fire", OTHER);
TimeStamp fireTime; if (MOZ_LOG_TEST(GetTimerLog(), LogLevel::Debug)) {
fireTime = TimeStamp::Now();
TimeDuration delta = fireTime - oldTimeout;
int32_t d = delta.ToMilliseconds(); // delta in ms
{
mozilla::StaticMutexAutoLock lock(sDeltaMutex);
sDeltaSum += abs(d);
sDeltaSumSquared += double(d) * double(d);
sDeltaNum++;
}
MOZ_LOG(GetTimerLog(), LogLevel::Debug,
("[this=%p] expected delay time %4ums\n", this, oldDelay));
MOZ_LOG(GetTimerLog(), LogLevel::Debug,
("[this=%p] actual delay time %4dms\n", this, oldDelay + d));
MOZ_LOG(GetTimerLog(), LogLevel::Debug,
("[this=%p] (mType is %d) -------\n", this, oldType));
MOZ_LOG(GetTimerLog(), LogLevel::Debug,
("[this=%p] delta %4dms\n", this, d));
}
if (MOZ_LOG_TEST(GetTimerFiringsLog(), LogLevel::Debug)) {
LogFiring(callbackDuringFire, oldType, oldDelay);
}
MutexAutoLock lock(mMutex); if (aGeneration == mGeneration) { if (IsRepeating()) { // Repeating timer has not been re-init or canceled; reschedule if (IsSlack()) {
mTimeout = now + mDelay;
} else { if (mDelay) { // If we are late enough finishing the callback that we have missed // some firings, do not attempt to play catchup, just get back on the // cadence we're supposed to maintain. unsigned missedFirings = static_cast<unsigned>((now - mTimeout) / mDelay);
mTimeout += mDelay * (missedFirings + 1);
} else { // Can we stop allowing repeating timers with delay 0?
mTimeout = now;
}
}
gThreadWrapper.AddTimer(this, lock);
} else { // Non-repeating timer that has not been re-scheduled. Clear. // XXX(nika): Other callsites seem to go to some effort to avoid // destroying mCallback when it's held. Why not this one?
mCallback = mozilla::AsVariant(UnknownCallback{});
}
}
--mFiring;
MOZ_LOG(GetTimerLog(), LogLevel::Debug,
("[this=%p] Took %fms to fire timer callback\n", this,
(now - fireTime).ToMilliseconds()));
}
// See the big comment above GetTimerFiringsLog() to understand this code. void nsTimerImpl::LogFiring(const Callback& aCallback, uint8_t aType,
uint32_t aDelay) { constchar* typeStr; switch (aType) { case nsITimer::TYPE_ONE_SHOT:
typeStr = "ONE_SHOT "; break; case nsITimer::TYPE_ONE_SHOT_LOW_PRIORITY:
typeStr = "ONE_LOW "; break; case nsITimer::TYPE_REPEATING_SLACK:
typeStr = "SLACK "; break; case nsITimer::TYPE_REPEATING_SLACK_LOW_PRIORITY:
typeStr = "SLACK_LOW "; break; case nsITimer::TYPE_REPEATING_PRECISE: /* fall through */ case nsITimer::TYPE_REPEATING_PRECISE_CAN_SKIP:
typeStr = "PRECISE "; break; default:
MOZ_CRASH("bad type");
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.