gecko-dev/xpcom/threads/nsTimerImpl.cpp
Jean-Yves Avenard 498938c5ca Bug 1651249 - Dispatch to the current SerialEventTarget by default. r=froydnj
Dispatching to the underlying thread of what could be a thread-pool can lead to data-race as the code won't be run where you expect it to.

Differential Revision: https://phabricator.services.mozilla.com/D82632
2020-07-14 21:17:31 +00:00

805 lines
25 KiB
C++

/* -*- 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/. */
#include "nsTimerImpl.h"
#include <utility>
#include "GeckoProfiler.h"
#include "TimerThread.h"
#include "mozilla/Atomics.h"
#include "mozilla/IntegerPrintfMacros.h"
#include "mozilla/Logging.h"
#include "mozilla/Mutex.h"
#include "mozilla/ResultExtensions.h"
#include "mozilla/Sprintf.h"
#include "nsThreadManager.h"
#include "nsThreadUtils.h"
#include "pratom.h"
#ifdef MOZ_TASK_TRACER
# include "GeckoTaskTracerImpl.h"
using namespace mozilla::tasktracer;
#endif
#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;
static TimerThread* gThread = nullptr;
// This module prints info about the precision of timers.
static mozilla::LazyLogModule sTimerLog("nsTimerImpl");
mozilla::LogModule* GetTimerLog() { return sTimerLog; }
TimeStamp NS_GetTimerDeadlineHintOnCurrentThread(TimeStamp aDefault,
uint32_t aSearchBound) {
return gThread
? gThread->FindNextFireTimeForCurrentThread(aDefault, aSearchBound)
: TimeStamp();
}
already_AddRefed<nsITimer> NS_NewTimer() { return NS_NewTimer(nullptr); }
already_AddRefed<nsITimer> NS_NewTimer(nsIEventTarget* aTarget) {
return nsTimer::WithEventTarget(aTarget).forget();
}
mozilla::Result<nsCOMPtr<nsITimer>, nsresult> NS_NewTimerWithObserver(
nsIObserver* aObserver, uint32_t aDelay, uint32_t aType,
nsIEventTarget* aTarget) {
nsCOMPtr<nsITimer> timer;
MOZ_TRY(NS_NewTimerWithObserver(getter_AddRefs(timer), aObserver, aDelay,
aType, aTarget));
return std::move(timer);
}
nsresult NS_NewTimerWithObserver(nsITimer** aTimer, nsIObserver* aObserver,
uint32_t aDelay, uint32_t aType,
nsIEventTarget* aTarget) {
auto timer = nsTimer::WithEventTarget(aTarget);
MOZ_TRY(timer->Init(aObserver, aDelay, aType));
timer.forget(aTimer);
return NS_OK;
}
mozilla::Result<nsCOMPtr<nsITimer>, nsresult> NS_NewTimerWithCallback(
nsITimerCallback* aCallback, uint32_t aDelay, uint32_t aType,
nsIEventTarget* aTarget) {
nsCOMPtr<nsITimer> timer;
MOZ_TRY(NS_NewTimerWithCallback(getter_AddRefs(timer), aCallback, aDelay,
aType, aTarget));
return std::move(timer);
}
nsresult NS_NewTimerWithCallback(nsITimer** aTimer, nsITimerCallback* aCallback,
uint32_t aDelay, uint32_t aType,
nsIEventTarget* aTarget) {
auto timer = nsTimer::WithEventTarget(aTarget);
MOZ_TRY(timer->InitWithCallback(aCallback, aDelay, aType));
timer.forget(aTimer);
return NS_OK;
}
mozilla::Result<nsCOMPtr<nsITimer>, nsresult> NS_NewTimerWithCallback(
nsITimerCallback* aCallback, const TimeDuration& aDelay, uint32_t aType,
nsIEventTarget* aTarget) {
nsCOMPtr<nsITimer> timer;
MOZ_TRY(NS_NewTimerWithCallback(getter_AddRefs(timer), aCallback, aDelay,
aType, aTarget));
return std::move(timer);
}
nsresult NS_NewTimerWithCallback(nsITimer** aTimer, nsITimerCallback* aCallback,
const TimeDuration& aDelay, uint32_t aType,
nsIEventTarget* aTarget) {
auto timer = nsTimer::WithEventTarget(aTarget);
MOZ_TRY(timer->InitHighResolutionWithCallback(aCallback, aDelay, aType));
timer.forget(aTimer);
return NS_OK;
}
mozilla::Result<nsCOMPtr<nsITimer>, nsresult> NS_NewTimerWithFuncCallback(
nsTimerCallbackFunc aCallback, void* aClosure, uint32_t aDelay,
uint32_t aType, const char* aNameString, nsIEventTarget* aTarget) {
nsCOMPtr<nsITimer> timer;
MOZ_TRY(NS_NewTimerWithFuncCallback(getter_AddRefs(timer), aCallback,
aClosure, aDelay, aType, aNameString,
aTarget));
return std::move(timer);
}
nsresult NS_NewTimerWithFuncCallback(nsITimer** aTimer,
nsTimerCallbackFunc aCallback,
void* aClosure, uint32_t aDelay,
uint32_t aType, const char* aNameString,
nsIEventTarget* aTarget) {
auto timer = nsTimer::WithEventTarget(aTarget);
MOZ_TRY(timer->InitWithNamedFuncCallback(aCallback, aClosure, aDelay, aType,
aNameString));
timer.forget(aTimer);
return NS_OK;
}
mozilla::Result<nsCOMPtr<nsITimer>, nsresult> NS_NewTimerWithFuncCallback(
nsTimerCallbackFunc aCallback, void* aClosure, uint32_t aDelay,
uint32_t aType, nsTimerNameCallbackFunc aNameCallback,
nsIEventTarget* aTarget) {
nsCOMPtr<nsITimer> timer;
MOZ_TRY(NS_NewTimerWithFuncCallback(getter_AddRefs(timer), aCallback,
aClosure, aDelay, aType, aNameCallback,
aTarget));
return std::move(timer);
}
nsresult NS_NewTimerWithFuncCallback(nsITimer** aTimer,
nsTimerCallbackFunc aCallback,
void* aClosure, uint32_t aDelay,
uint32_t aType,
nsTimerNameCallbackFunc aNameCallback,
nsIEventTarget* aTarget) {
auto timer = nsTimer::WithEventTarget(aTarget);
MOZ_TRY(timer->InitWithNameableFuncCallback(aCallback, aClosure, aDelay,
aType, aNameCallback));
timer.forget(aTimer);
return NS_OK;
}
// 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. The name used for a
// |Callback::Type::Function| timer depends on the circumstances.
//
// - If it was explicitly named (e.g. it was initialized with
// InitWithNamedFuncCallback()) then that explicit name will be shown.
//
// - Otherwise, if we are on a platform that supports function name lookup
// (Mac or Linux) then the looked-up name will be shown with a
// "[from dladdr]" annotation. On Mac the looked-up name will be immediately
// useful. On Linux it'll need post-processing with `tools/rb/fix_stacks.py`.
//
// - Otherwise, no name will be printed. If many timers hit this case then
// you'll need to re-run the workload on a Mac to find out which timers they
// are, and then give them explicit names.
//
// 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");
static mozilla::LogModule* GetTimerFiringsLog() { return sTimerFiringsLog; }
#include <math.h>
double nsTimerImpl::sDeltaSumSquared = 0;
double nsTimerImpl::sDeltaSum = 0;
double nsTimerImpl::sDeltaNum = 0;
static void myNS_MeanAndStdDev(double n, double sumOfValues,
double sumOfSquaredValues, double* meanResult,
double* stdDevResult) {
double mean = 0.0, var = 0.0, stdDev = 0.0;
if (n > 0.0 && sumOfValues >= 0) {
mean = sumOfValues / n;
double temp = (n * sumOfSquaredValues) - (sumOfValues * sumOfValues);
if (temp < 0.0 || n <= 1) {
var = 0.0;
} else {
var = temp / (n * (n - 1));
}
// for some reason, Windows says sqrt(0.0) is "-1.#J" (?!) so do this:
stdDev = var != 0.0 ? sqrt(var) : 0.0;
}
*meanResult = mean;
*stdDevResult = stdDev;
}
NS_IMPL_QUERY_INTERFACE(nsTimer, nsITimer)
NS_IMPL_ADDREF(nsTimer)
NS_IMETHODIMP_(MozExternalRefCountType)
nsTimer::Release(void) {
nsrefcnt count = --mRefCnt;
NS_LOG_RELEASE(this, count, "nsTimer");
if (count == 1) {
// Last ref, in nsTimerImpl::mITimer. Make sure the cycle is broken.
mImpl->CancelImpl(true);
} else if (count == 0) {
delete this;
}
return count;
}
nsTimerImpl::nsTimerImpl(nsITimer* aTimer, nsIEventTarget* aTarget)
: mEventTarget(aTarget),
mHolder(nullptr),
mType(0),
mGeneration(0),
mITimer(aTimer),
mMutex("nsTimerImpl::mMutex"),
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);
}
// static
nsresult nsTimerImpl::Startup() {
nsresult rv;
gThread = new TimerThread();
NS_ADDREF(gThread);
rv = gThread->InitLocks();
if (NS_FAILED(rv)) {
NS_RELEASE(gThread);
}
return rv;
}
void nsTimerImpl::Shutdown() {
if (MOZ_LOG_TEST(GetTimerLog(), LogLevel::Debug)) {
double mean = 0, stddev = 0;
myNS_MeanAndStdDev(sDeltaNum, sDeltaSum, sDeltaSumSquared, &mean, &stddev);
MOZ_LOG(GetTimerLog(), LogLevel::Debug,
("sDeltaNum = %f, sDeltaSum = %f, sDeltaSumSquared = %f\n",
sDeltaNum, sDeltaSum, sDeltaSumSquared));
MOZ_LOG(GetTimerLog(), LogLevel::Debug,
("mean: %fms, stddev: %fms\n", mean, stddev));
}
if (!gThread) {
return;
}
gThread->Shutdown();
NS_RELEASE(gThread);
}
nsresult nsTimerImpl::InitCommon(uint32_t aDelayMS, uint32_t aType,
Callback&& aNewCallback) {
return InitCommon(TimeDuration::FromMilliseconds(aDelayMS), aType,
std::move(aNewCallback));
}
nsresult nsTimerImpl::InitCommon(const TimeDuration& aDelay, uint32_t aType,
Callback&& newCallback) {
mMutex.AssertCurrentThreadOwns();
if (!gThread) {
return NS_ERROR_NOT_INITIALIZED;
}
if (!mEventTarget) {
NS_ERROR("mEventTarget is NULL");
return NS_ERROR_NOT_INITIALIZED;
}
gThread->RemoveTimer(this);
mCallback.swap(newCallback);
++mGeneration;
mType = (uint8_t)aType;
mDelay = aDelay;
mTimeout = TimeStamp::Now() + mDelay;
return gThread->AddTimer(this);
}
nsresult nsTimerImpl::InitWithFuncCallbackCommon(nsTimerCallbackFunc aFunc,
void* aClosure,
uint32_t aDelay,
uint32_t aType,
const Callback::Name& aName) {
if (NS_WARN_IF(!aFunc)) {
return NS_ERROR_INVALID_ARG;
}
Callback cb; // Goes out of scope after the unlock, prevents deadlock
cb.mType = Callback::Type::Function;
cb.mCallback.c = aFunc;
cb.mClosure = aClosure;
cb.mName = aName;
MutexAutoLock lock(mMutex);
return InitCommon(aDelay, aType, std::move(cb));
}
nsresult nsTimerImpl::InitWithNamedFuncCallback(nsTimerCallbackFunc aFunc,
void* aClosure, uint32_t aDelay,
uint32_t aType,
const char* aNameString) {
Callback::Name name(aNameString);
return InitWithFuncCallbackCommon(aFunc, aClosure, aDelay, aType, name);
}
nsresult nsTimerImpl::InitWithNameableFuncCallback(
nsTimerCallbackFunc aFunc, void* aClosure, uint32_t aDelay, uint32_t aType,
nsTimerNameCallbackFunc aNameFunc) {
Callback::Name name(aNameFunc);
return InitWithFuncCallbackCommon(aFunc, aClosure, aDelay, aType, name);
}
nsresult nsTimerImpl::InitWithCallback(nsITimerCallback* aCallback,
uint32_t aDelayInMs, uint32_t aType) {
return InitHighResolutionWithCallback(
aCallback, TimeDuration::FromMilliseconds(aDelayInMs), aType);
}
nsresult nsTimerImpl::InitHighResolutionWithCallback(
nsITimerCallback* aCallback, const TimeDuration& aDelay, uint32_t aType) {
if (NS_WARN_IF(!aCallback)) {
return NS_ERROR_INVALID_ARG;
}
Callback cb; // Goes out of scope after the unlock, prevents deadlock
cb.mType = Callback::Type::Interface;
cb.mCallback.i = aCallback;
NS_ADDREF(cb.mCallback.i);
MutexAutoLock lock(mMutex);
return InitCommon(aDelay, aType, std::move(cb));
}
nsresult nsTimerImpl::Init(nsIObserver* aObserver, uint32_t aDelayInMs,
uint32_t aType) {
if (NS_WARN_IF(!aObserver)) {
return NS_ERROR_INVALID_ARG;
}
Callback cb; // Goes out of scope after the unlock, prevents deadlock
cb.mType = Callback::Type::Observer;
cb.mCallback.o = aObserver;
NS_ADDREF(cb.mCallback.o);
MutexAutoLock lock(mMutex);
return InitCommon(aDelayInMs, aType, std::move(cb));
}
nsresult nsTimerImpl::Cancel() {
CancelImpl(false);
return NS_OK;
}
void nsTimerImpl::CancelImpl(bool aClearITimer) {
Callback cbTrash;
RefPtr<nsITimer> timerTrash;
{
MutexAutoLock lock(mMutex);
if (gThread) {
gThread->RemoveTimer(this);
}
cbTrash.swap(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().mType == Callback::Type::Unknown && !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;
}
bool reAdd = false;
if (gThread) {
reAdd = NS_SUCCEEDED(gThread->RemoveTimer(this));
}
mDelay = TimeDuration::FromMilliseconds(aDelay);
mTimeout = TimeStamp::Now() + mDelay;
if (reAdd) {
gThread->AddTimer(this);
}
return NS_OK;
}
nsresult nsTimerImpl::GetDelay(uint32_t* aDelay) {
MutexAutoLock lock(mMutex);
*aDelay = mDelay.ToMilliseconds();
return NS_OK;
}
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;
}
nsresult nsTimerImpl::GetType(uint32_t* aType) {
MutexAutoLock lock(mMutex);
*aType = mType;
return NS_OK;
}
nsresult nsTimerImpl::GetClosure(void** aClosure) {
MutexAutoLock lock(mMutex);
*aClosure = GetCallback().mClosure;
return NS_OK;
}
nsresult nsTimerImpl::GetCallback(nsITimerCallback** aCallback) {
MutexAutoLock lock(mMutex);
if (GetCallback().mType == Callback::Type::Interface) {
NS_IF_ADDREF(*aCallback = GetCallback().mCallback.i);
} else {
*aCallback = nullptr;
}
return NS_OK;
}
nsresult nsTimerImpl::GetTarget(nsIEventTarget** aTarget) {
MutexAutoLock lock(mMutex);
NS_IF_ADDREF(*aTarget = mEventTarget);
return NS_OK;
}
nsresult nsTimerImpl::SetTarget(nsIEventTarget* aTarget) {
MutexAutoLock lock(mMutex);
if (NS_WARN_IF(mCallback.mType != Callback::Type::Unknown)) {
return NS_ERROR_ALREADY_INITIALIZED;
}
if (aTarget) {
mEventTarget = aTarget;
} else {
mEventTarget = mozilla::GetCurrentSerialEventTarget();
}
return NS_OK;
}
nsresult nsTimerImpl::GetAllowedEarlyFiringMicroseconds(uint32_t* aValueOut) {
*aValueOut = gThread ? gThread->AllowedEarlyFiringMicroseconds() : 0;
return NS_OK;
}
void nsTimerImpl::Fire(int32_t aGeneration) {
uint8_t oldType;
uint32_t oldDelay;
TimeStamp oldTimeout;
Callback callbackDuringFire;
nsCOMPtr<nsITimer> kungFuDeathGrip;
{
// 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) {
return;
}
++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.
kungFuDeathGrip = mITimer;
}
AUTO_PROFILER_LABEL("nsTimerImpl::Fire", OTHER);
TimeStamp now = TimeStamp::Now();
if (MOZ_LOG_TEST(GetTimerLog(), LogLevel::Debug)) {
TimeDuration delta = now - oldTimeout;
int32_t d = delta.ToMilliseconds(); // delta in ms
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);
}
switch (callbackDuringFire.mType) {
case Callback::Type::Function:
callbackDuringFire.mCallback.c(mITimer, callbackDuringFire.mClosure);
break;
case Callback::Type::Interface:
callbackDuringFire.mCallback.i->Notify(mITimer);
break;
case Callback::Type::Observer:
callbackDuringFire.mCallback.o->Observe(mITimer, NS_TIMER_CALLBACK_TOPIC,
nullptr);
break;
default:;
}
MutexAutoLock lock(mMutex);
if (aGeneration == mGeneration) {
if (IsRepeating()) {
// Repeating timer has not been re-init or canceled; reschedule
if (IsSlack()) {
mTimeout = TimeStamp::Now() + mDelay;
} else {
mTimeout = mTimeout + mDelay;
}
if (gThread) {
gThread->AddTimer(this);
}
} else {
// Non-repeating timer that has not been re-scheduled. Clear.
mCallback.clear();
}
}
--mFiring;
MOZ_LOG(GetTimerLog(), LogLevel::Debug,
("[this=%p] Took %fms to fire timer callback\n", this,
(TimeStamp::Now() - now).ToMilliseconds()));
}
#if defined(HAVE_DLADDR) && defined(HAVE___CXA_DEMANGLE)
# define USE_DLADDR 1
#endif
#ifdef USE_DLADDR
# include <cxxabi.h>
# include <dlfcn.h>
#endif
// See the big comment above GetTimerFiringsLog() to understand this code.
void nsTimerImpl::LogFiring(const Callback& aCallback, uint8_t aType,
uint32_t aDelay) {
const char* 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");
}
switch (aCallback.mType) {
case Callback::Type::Function: {
bool needToFreeName = false;
const char* annotation = "";
const char* name;
static const size_t buflen = 1024;
char buf[buflen];
if (aCallback.mName.is<Callback::NameString>()) {
name = aCallback.mName.as<Callback::NameString>();
} else if (aCallback.mName.is<Callback::NameFunc>()) {
aCallback.mName.as<Callback::NameFunc>()(
mITimer, /* aAnonymize = */ false, aCallback.mClosure, buf, buflen);
name = buf;
} else {
MOZ_ASSERT(aCallback.mName.is<Callback::NameNothing>());
#ifdef USE_DLADDR
annotation = "[from dladdr] ";
Dl_info info;
void* addr = reinterpret_cast<void*>(aCallback.mCallback.c);
if (dladdr(addr, &info) == 0) {
name = "???[dladdr: failed]";
} else if (info.dli_sname) {
int status;
name = abi::__cxa_demangle(info.dli_sname, nullptr, nullptr, &status);
if (status == 0) {
// Success. Because we didn't pass in a buffer to __cxa_demangle it
// allocates its own one with malloc() which we must free() later.
MOZ_ASSERT(name);
needToFreeName = true;
} else if (status == -1) {
name = "???[__cxa_demangle: OOM]";
} else if (status == -2) {
name = "???[__cxa_demangle: invalid mangled name]";
} else if (status == -3) {
name = "???[__cxa_demangle: invalid argument]";
} else {
name = "???[__cxa_demangle: unexpected status value]";
}
} else if (info.dli_fname) {
// The "#0: " prefix is necessary for `fix_stacks.py` to interpret
// this string as something to convert.
SprintfLiteral(buf, "#0: ???[%s +0x%" PRIxPTR "]\n", info.dli_fname,
uintptr_t(addr) - uintptr_t(info.dli_fbase));
name = buf;
} else {
name = "???[dladdr: no symbol or shared object obtained]";
}
#else
name = "???[dladdr is unimplemented or doesn't work well on this OS]";
#endif
}
MOZ_LOG(GetTimerFiringsLog(), LogLevel::Debug,
("[%d] fn timer (%s %5d ms): %s%s\n", getpid(), typeStr,
aDelay, annotation, name));
if (needToFreeName) {
free(const_cast<char*>(name));
}
break;
}
case Callback::Type::Interface: {
MOZ_LOG(GetTimerFiringsLog(), LogLevel::Debug,
("[%d] iface timer (%s %5d ms): %p\n", getpid(), typeStr, aDelay,
aCallback.mCallback.i));
break;
}
case Callback::Type::Observer: {
MOZ_LOG(GetTimerFiringsLog(), LogLevel::Debug,
("[%d] obs timer (%s %5d ms): %p\n", getpid(), typeStr, aDelay,
aCallback.mCallback.o));
break;
}
case Callback::Type::Unknown:
default: {
MOZ_LOG(GetTimerFiringsLog(), LogLevel::Debug,
("[%d] ??? timer (%s, %5d ms)\n", getpid(), typeStr, aDelay));
break;
}
}
}
void nsTimerImpl::GetName(nsACString& aName) {
MutexAutoLock lock(mMutex);
Callback& cb(GetCallback());
switch (cb.mType) {
case Callback::Type::Function:
if (cb.mName.is<Callback::NameString>()) {
aName.Assign(cb.mName.as<Callback::NameString>());
} else if (cb.mName.is<Callback::NameFunc>()) {
static const size_t buflen = 1024;
char buf[buflen];
cb.mName.as<Callback::NameFunc>()(mITimer, /* aAnonymize = */ true,
cb.mClosure, buf, buflen);
aName.Assign(buf);
} else {
MOZ_ASSERT(cb.mName.is<Callback::NameNothing>());
aName.AssignLiteral("Anonymous_callback_timer");
}
break;
case Callback::Type::Interface:
if (nsCOMPtr<nsINamed> named = do_QueryInterface(cb.mCallback.i)) {
named->GetName(aName);
} else {
aName.AssignLiteral("Anonymous_interface_timer");
}
break;
case Callback::Type::Observer:
if (nsCOMPtr<nsINamed> named = do_QueryInterface(cb.mCallback.o)) {
named->GetName(aName);
} else {
aName.AssignLiteral("Anonymous_observer_timer");
}
break;
case Callback::Type::Unknown:
aName.AssignLiteral("Canceled_timer");
break;
}
}
void nsTimerImpl::SetHolder(nsTimerImplHolder* aHolder) { mHolder = aHolder; }
nsTimer::~nsTimer() = default;
size_t nsTimer::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const {
return aMallocSizeOf(this);
}
/* static */
RefPtr<nsTimer> nsTimer::WithEventTarget(nsIEventTarget* aTarget) {
if (!aTarget) {
aTarget = mozilla::GetCurrentSerialEventTarget();
}
return do_AddRef(new nsTimer(aTarget));
}
/* static */
nsresult nsTimer::XPCOMConstructor(nsISupports* aOuter, REFNSIID aIID,
void** aResult) {
*aResult = nullptr;
if (aOuter != nullptr) {
return NS_ERROR_NO_AGGREGATION;
}
auto timer = WithEventTarget(nullptr);
return timer->QueryInterface(aIID, aResult);
}
/* static */
const nsTimerImpl::Callback::NameNothing nsTimerImpl::Callback::Nothing = 0;
#ifdef MOZ_TASK_TRACER
void nsTimerImpl::GetTLSTraceInfo() { mTracedTask.GetTLSTraceInfo(); }
TracedTaskCommon nsTimerImpl::GetTracedTask() { return mTracedTask; }
#endif