2013-03-07 02:29:30 +04:00
|
|
|
/* -*- 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
|
2012-05-21 15:12:37 +04:00
|
|
|
* 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/. */
|
2001-12-16 09:13:17 +03:00
|
|
|
|
|
|
|
#include "nsTimerImpl.h"
|
|
|
|
#include "TimerThread.h"
|
2006-05-10 21:30:15 +04:00
|
|
|
#include "nsAutoPtr.h"
|
|
|
|
#include "nsThreadManager.h"
|
|
|
|
#include "nsThreadUtils.h"
|
2013-07-12 00:21:45 +04:00
|
|
|
#include "pratom.h"
|
2013-03-18 18:25:50 +04:00
|
|
|
#include "GeckoProfiler.h"
|
2013-08-22 19:14:42 +04:00
|
|
|
#include "mozilla/Atomics.h"
|
2015-09-22 03:13:51 +03:00
|
|
|
#include "mozilla/IntegerPrintfMacros.h"
|
2015-06-04 01:25:57 +03:00
|
|
|
#include "mozilla/Logging.h"
|
Bug 1382922 - Refactor event queue to allow multiple implementations (r=erahm)
This patch refactors the nsThread event queue to clean it up and to make it easier to restructure. The fundamental concepts are as follows:
Each nsThread will have a pointer to a refcounted SynchronizedEventQueue. A SynchronizedEQ takes care of doing the locking and condition variable work when posting and popping events. For the actual storage of events, it delegates to an AbstractEventQueue data structure. It keeps a UniquePtr to the AbstractEventQueue that it uses for storage.
Both SynchronizedEQ and AbstractEventQueue are abstract classes. There is only one concrete implementation of SynchronizedEQ in this patch, which is called ThreadEventQueue. ThreadEventQueue uses locks and condition variables to post and pop events the same way nsThread does. It also encapsulates the functionality that DOM workers need to implement their special event loops (PushEventQueue and PopEventQueue). In later Quantum DOM work, I plan to have another SynchronizedEQ implementation for the main thread, called SchedulerEventQueue. It will have special code for the cooperatively scheduling threads in Quantum DOM.
There are two concrete implementations of AbstractEventQueue in this patch: EventQueue and PrioritizedEventQueue. EventQueue replaces the old nsEventQueue. The other AbstractEventQueue implementation is PrioritizedEventQueue, which uses multiple queues for different event priorities.
The final major piece here is ThreadEventTarget, which splits some of the code for posting events out of nsThread. Eventually, my plan is for multiple cooperatively scheduled nsThreads to be able to share a ThreadEventTarget. In this patch, though, each nsThread has its own ThreadEventTarget. The class's purpose is just to collect some related code together.
One final note: I tried to avoid virtual dispatch overhead as much as possible. Calls to SynchronizedEQ methods do use virtual dispatch, since I plan to use different implementations for different threads with Quantum DOM. But all the calls to EventQueue methods should be non-virtual. Although the methods are declared virtual, all the classes used are final and the concrete classes involved should all be known through templatization.
MozReview-Commit-ID: 9Evtr9oIJvx
2017-06-21 05:42:13 +03:00
|
|
|
#include "mozilla/Move.h"
|
|
|
|
#include "mozilla/Mutex.h"
|
2017-10-16 09:10:17 +03:00
|
|
|
#include "mozilla/ResultExtensions.h"
|
2015-06-16 05:57:19 +03:00
|
|
|
#ifdef MOZ_TASK_TRACER
|
|
|
|
#include "GeckoTaskTracerImpl.h"
|
|
|
|
using namespace mozilla::tasktracer;
|
|
|
|
#endif
|
2004-05-11 13:38:50 +04:00
|
|
|
|
2015-09-10 10:50:51 +03:00
|
|
|
#ifdef XP_WIN
|
|
|
|
#include <process.h>
|
|
|
|
#ifndef getpid
|
|
|
|
#define getpid _getpid
|
|
|
|
#endif
|
|
|
|
#else
|
|
|
|
#include <unistd.h>
|
|
|
|
#endif
|
|
|
|
|
2013-08-22 19:14:42 +04:00
|
|
|
using mozilla::Atomic;
|
2015-06-04 01:25:57 +03:00
|
|
|
using mozilla::LogLevel;
|
2017-10-16 09:10:17 +03:00
|
|
|
using mozilla::MakeRefPtr;
|
Bug 1382922 - Refactor event queue to allow multiple implementations (r=erahm)
This patch refactors the nsThread event queue to clean it up and to make it easier to restructure. The fundamental concepts are as follows:
Each nsThread will have a pointer to a refcounted SynchronizedEventQueue. A SynchronizedEQ takes care of doing the locking and condition variable work when posting and popping events. For the actual storage of events, it delegates to an AbstractEventQueue data structure. It keeps a UniquePtr to the AbstractEventQueue that it uses for storage.
Both SynchronizedEQ and AbstractEventQueue are abstract classes. There is only one concrete implementation of SynchronizedEQ in this patch, which is called ThreadEventQueue. ThreadEventQueue uses locks and condition variables to post and pop events the same way nsThread does. It also encapsulates the functionality that DOM workers need to implement their special event loops (PushEventQueue and PopEventQueue). In later Quantum DOM work, I plan to have another SynchronizedEQ implementation for the main thread, called SchedulerEventQueue. It will have special code for the cooperatively scheduling threads in Quantum DOM.
There are two concrete implementations of AbstractEventQueue in this patch: EventQueue and PrioritizedEventQueue. EventQueue replaces the old nsEventQueue. The other AbstractEventQueue implementation is PrioritizedEventQueue, which uses multiple queues for different event priorities.
The final major piece here is ThreadEventTarget, which splits some of the code for posting events out of nsThread. Eventually, my plan is for multiple cooperatively scheduled nsThreads to be able to share a ThreadEventTarget. In this patch, though, each nsThread has its own ThreadEventTarget. The class's purpose is just to collect some related code together.
One final note: I tried to avoid virtual dispatch overhead as much as possible. Calls to SynchronizedEQ methods do use virtual dispatch, since I plan to use different implementations for different threads with Quantum DOM. But all the calls to EventQueue methods should be non-virtual. Although the methods are declared virtual, all the classes used are final and the concrete classes involved should all be known through templatization.
MozReview-Commit-ID: 9Evtr9oIJvx
2017-06-21 05:42:13 +03:00
|
|
|
using mozilla::Move;
|
|
|
|
using mozilla::MutexAutoLock;
|
2010-07-15 17:59:24 +04:00
|
|
|
using mozilla::TimeDuration;
|
|
|
|
using mozilla::TimeStamp;
|
|
|
|
|
2012-07-30 18:20:58 +04:00
|
|
|
static TimerThread* gThread = nullptr;
|
2002-04-14 04:27:16 +04:00
|
|
|
|
2015-09-10 10:50:51 +03:00
|
|
|
// This module prints info about the precision of timers.
|
2015-10-19 22:50:14 +03:00
|
|
|
static mozilla::LazyLogModule sTimerLog("nsTimerImpl");
|
|
|
|
|
|
|
|
mozilla::LogModule*
|
2012-10-30 03:32:10 +04:00
|
|
|
GetTimerLog()
|
|
|
|
{
|
2015-10-19 22:50:14 +03:00
|
|
|
return sTimerLog;
|
2012-10-30 03:32:10 +04:00
|
|
|
}
|
|
|
|
|
2017-05-25 04:12:55 +03:00
|
|
|
TimeStamp
|
2017-05-25 04:14:29 +03:00
|
|
|
NS_GetTimerDeadlineHintOnCurrentThread(TimeStamp aDefault, uint32_t aSearchBound)
|
2017-05-25 04:12:55 +03:00
|
|
|
{
|
|
|
|
return gThread
|
2017-05-25 04:14:29 +03:00
|
|
|
? gThread->FindNextFireTimeForCurrentThread(aDefault, aSearchBound)
|
2017-05-25 04:12:55 +03:00
|
|
|
: TimeStamp();
|
|
|
|
}
|
|
|
|
|
2017-10-16 09:10:17 +03:00
|
|
|
already_AddRefed<nsITimer>
|
|
|
|
NS_NewTimer()
|
|
|
|
{
|
|
|
|
return do_AddRef(new nsTimer());
|
|
|
|
}
|
|
|
|
|
|
|
|
already_AddRefed<nsITimer>
|
|
|
|
NS_NewTimer(nsIEventTarget* aTarget)
|
|
|
|
{
|
|
|
|
auto timer = MakeRefPtr<nsTimer>();
|
|
|
|
if (aTarget && MOZ_LIKELY(timer)) {
|
|
|
|
MOZ_ALWAYS_SUCCEEDS(timer->SetTarget(aTarget));
|
|
|
|
}
|
|
|
|
return timer.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 Move(timer);
|
|
|
|
}
|
|
|
|
nsresult
|
|
|
|
NS_NewTimerWithObserver(nsITimer** aTimer,
|
|
|
|
nsIObserver* aObserver,
|
|
|
|
uint32_t aDelay,
|
|
|
|
uint32_t aType,
|
|
|
|
nsIEventTarget* aTarget)
|
|
|
|
{
|
|
|
|
auto timer = MakeRefPtr<nsTimer>();
|
|
|
|
if (aTarget) {
|
|
|
|
MOZ_ALWAYS_SUCCEEDS(timer->SetTarget(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 Move(timer);
|
|
|
|
}
|
|
|
|
nsresult
|
|
|
|
NS_NewTimerWithCallback(nsITimer** aTimer,
|
|
|
|
nsITimerCallback* aCallback,
|
|
|
|
uint32_t aDelay,
|
|
|
|
uint32_t aType,
|
|
|
|
nsIEventTarget* aTarget)
|
|
|
|
{
|
|
|
|
auto timer = MakeRefPtr<nsTimer>();
|
|
|
|
if (aTarget) {
|
|
|
|
MOZ_ALWAYS_SUCCEEDS(timer->SetTarget(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 Move(timer);
|
|
|
|
}
|
|
|
|
nsresult
|
|
|
|
NS_NewTimerWithCallback(nsITimer** aTimer,
|
|
|
|
nsITimerCallback* aCallback,
|
|
|
|
const TimeDuration& aDelay,
|
|
|
|
uint32_t aType,
|
|
|
|
nsIEventTarget* aTarget)
|
|
|
|
{
|
|
|
|
auto timer = MakeRefPtr<nsTimer>();
|
|
|
|
if (aTarget) {
|
|
|
|
MOZ_ALWAYS_SUCCEEDS(timer->SetTarget(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 Move(timer);
|
|
|
|
}
|
|
|
|
nsresult
|
|
|
|
NS_NewTimerWithFuncCallback(nsITimer** aTimer,
|
|
|
|
nsTimerCallbackFunc aCallback,
|
|
|
|
void* aClosure,
|
|
|
|
uint32_t aDelay,
|
|
|
|
uint32_t aType,
|
|
|
|
const char* aNameString,
|
|
|
|
nsIEventTarget* aTarget)
|
|
|
|
{
|
|
|
|
auto timer = MakeRefPtr<nsTimer>();
|
|
|
|
if (aTarget) {
|
|
|
|
MOZ_ALWAYS_SUCCEEDS(timer->SetTarget(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 Move(timer);
|
|
|
|
}
|
|
|
|
nsresult
|
|
|
|
NS_NewTimerWithFuncCallback(nsITimer** aTimer,
|
|
|
|
nsTimerCallbackFunc aCallback,
|
|
|
|
void* aClosure,
|
|
|
|
uint32_t aDelay,
|
|
|
|
uint32_t aType,
|
|
|
|
nsTimerNameCallbackFunc aNameCallback,
|
|
|
|
nsIEventTarget* aTarget)
|
|
|
|
{
|
|
|
|
auto timer = MakeRefPtr<nsTimer>();
|
|
|
|
if (aTarget) {
|
|
|
|
MOZ_ALWAYS_SUCCEEDS(timer->SetTarget(aTarget));
|
|
|
|
}
|
|
|
|
|
|
|
|
MOZ_TRY(timer->InitWithNameableFuncCallback(aCallback, aClosure,
|
|
|
|
aDelay, aType,
|
|
|
|
aNameCallback));
|
|
|
|
timer.forget(aTimer);
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2015-09-10 10:50:51 +03:00
|
|
|
// 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.
|
|
|
|
//
|
2016-05-26 01:26:10 +03:00
|
|
|
// MOZ_LOG=TimerFirings:4
|
2015-09-10 10:50:51 +03:00
|
|
|
//
|
|
|
|
// Then a line will be printed for every timer that fires. The name used for a
|
2017-01-11 22:59:19 +03:00
|
|
|
// |Callback::Type::Function| timer depends on the circumstances.
|
2015-09-10 10:50:51 +03:00
|
|
|
//
|
|
|
|
// - 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
|
2015-09-22 03:13:51 +03:00
|
|
|
// (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_linux_stack.py.
|
2015-09-10 10:50:51 +03:00
|
|
|
//
|
|
|
|
// - 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.
|
|
|
|
//
|
2015-09-17 07:49:24 +03:00
|
|
|
// More detailed docs are here:
|
|
|
|
// https://developer.mozilla.org/en-US/docs/Mozilla/Performance/TimerFirings_logging
|
|
|
|
//
|
2015-10-19 22:50:14 +03:00
|
|
|
static mozilla::LazyLogModule sTimerFiringsLog("TimerFirings");
|
|
|
|
|
|
|
|
mozilla::LogModule*
|
2015-09-10 10:50:51 +03:00
|
|
|
GetTimerFiringsLog()
|
|
|
|
{
|
2015-10-19 22:50:14 +03:00
|
|
|
return sTimerFiringsLog;
|
2015-09-10 10:50:51 +03:00
|
|
|
}
|
|
|
|
|
2001-12-16 09:13:17 +03:00
|
|
|
#include <math.h>
|
|
|
|
|
|
|
|
double nsTimerImpl::sDeltaSumSquared = 0;
|
|
|
|
double nsTimerImpl::sDeltaSum = 0;
|
2002-03-08 23:11:49 +03:00
|
|
|
double nsTimerImpl::sDeltaNum = 0;
|
2001-12-16 09:13:17 +03:00
|
|
|
|
|
|
|
static void
|
|
|
|
myNS_MeanAndStdDev(double n, double sumOfValues, double sumOfSquaredValues,
|
2014-05-27 11:15:35 +04:00
|
|
|
double* meanResult, double* stdDevResult)
|
2001-12-16 09:13:17 +03:00
|
|
|
{
|
|
|
|
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);
|
2014-05-27 11:15:35 +04:00
|
|
|
if (temp < 0.0 || n <= 1) {
|
2001-12-16 09:13:17 +03:00
|
|
|
var = 0.0;
|
2014-05-27 11:15:35 +04:00
|
|
|
} else {
|
2001-12-16 09:13:17 +03:00
|
|
|
var = temp / (n * (n - 1));
|
2014-05-27 11:15:35 +04:00
|
|
|
}
|
2001-12-16 09:13:17 +03:00
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
|
2016-07-20 23:16:40 +03:00
|
|
|
NS_IMPL_QUERY_INTERFACE(nsTimer, nsITimer)
|
|
|
|
NS_IMPL_ADDREF(nsTimer)
|
2001-12-16 09:13:17 +03:00
|
|
|
|
2014-05-27 11:15:35 +04:00
|
|
|
NS_IMETHODIMP_(MozExternalRefCountType)
|
2016-07-20 23:16:40 +03:00
|
|
|
nsTimer::Release(void)
|
2002-03-08 23:11:49 +03:00
|
|
|
{
|
2016-07-20 23:16:40 +03:00
|
|
|
nsrefcnt count = --mRefCnt;
|
|
|
|
NS_LOG_RELEASE(this, count, "nsTimer");
|
|
|
|
|
|
|
|
if (count == 1) {
|
2017-09-14 20:19:24 +03:00
|
|
|
// Last ref, in nsTimerImpl::mITimer. Make sure the cycle is broken.
|
|
|
|
mImpl->CancelImpl(true);
|
2016-07-20 23:16:40 +03:00
|
|
|
} else if (count == 0) {
|
2010-07-05 13:42:18 +04:00
|
|
|
delete this;
|
2002-03-08 23:11:49 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return count;
|
|
|
|
}
|
2001-12-16 09:13:17 +03:00
|
|
|
|
2018-04-13 16:01:28 +03:00
|
|
|
nsTimerImpl::nsTimerImpl(nsITimer* aTimer) :
|
|
|
|
mHolder(nullptr),
|
|
|
|
mGeneration(0),
|
|
|
|
mITimer(aTimer),
|
|
|
|
mMutex("nsTimerImpl::mMutex")
|
2001-12-16 09:13:17 +03:00
|
|
|
{
|
2004-05-11 13:38:50 +04:00
|
|
|
// XXXbsmedberg: shouldn't this be in Init()?
|
Bug 1382922 - Refactor event queue to allow multiple implementations (r=erahm)
This patch refactors the nsThread event queue to clean it up and to make it easier to restructure. The fundamental concepts are as follows:
Each nsThread will have a pointer to a refcounted SynchronizedEventQueue. A SynchronizedEQ takes care of doing the locking and condition variable work when posting and popping events. For the actual storage of events, it delegates to an AbstractEventQueue data structure. It keeps a UniquePtr to the AbstractEventQueue that it uses for storage.
Both SynchronizedEQ and AbstractEventQueue are abstract classes. There is only one concrete implementation of SynchronizedEQ in this patch, which is called ThreadEventQueue. ThreadEventQueue uses locks and condition variables to post and pop events the same way nsThread does. It also encapsulates the functionality that DOM workers need to implement their special event loops (PushEventQueue and PopEventQueue). In later Quantum DOM work, I plan to have another SynchronizedEQ implementation for the main thread, called SchedulerEventQueue. It will have special code for the cooperatively scheduling threads in Quantum DOM.
There are two concrete implementations of AbstractEventQueue in this patch: EventQueue and PrioritizedEventQueue. EventQueue replaces the old nsEventQueue. The other AbstractEventQueue implementation is PrioritizedEventQueue, which uses multiple queues for different event priorities.
The final major piece here is ThreadEventTarget, which splits some of the code for posting events out of nsThread. Eventually, my plan is for multiple cooperatively scheduled nsThreads to be able to share a ThreadEventTarget. In this patch, though, each nsThread has its own ThreadEventTarget. The class's purpose is just to collect some related code together.
One final note: I tried to avoid virtual dispatch overhead as much as possible. Calls to SynchronizedEQ methods do use virtual dispatch, since I plan to use different implementations for different threads with Quantum DOM. But all the calls to EventQueue methods should be non-virtual. Although the methods are declared virtual, all the classes used are final and the concrete classes involved should all be known through templatization.
MozReview-Commit-ID: 9Evtr9oIJvx
2017-06-21 05:42:13 +03:00
|
|
|
mEventTarget = mozilla::GetCurrentThreadEventTarget();
|
2001-12-16 09:13:17 +03:00
|
|
|
}
|
|
|
|
|
2004-05-11 13:38:50 +04:00
|
|
|
//static
|
|
|
|
nsresult
|
|
|
|
nsTimerImpl::Startup()
|
|
|
|
{
|
|
|
|
nsresult rv;
|
|
|
|
|
|
|
|
gThread = new TimerThread();
|
|
|
|
|
|
|
|
NS_ADDREF(gThread);
|
|
|
|
rv = gThread->InitLocks();
|
|
|
|
|
|
|
|
if (NS_FAILED(rv)) {
|
|
|
|
NS_RELEASE(gThread);
|
|
|
|
}
|
|
|
|
|
|
|
|
return rv;
|
|
|
|
}
|
2001-12-16 09:13:17 +03:00
|
|
|
|
2014-05-27 11:15:35 +04:00
|
|
|
void
|
|
|
|
nsTimerImpl::Shutdown()
|
2001-12-16 09:13:17 +03:00
|
|
|
{
|
2015-06-04 01:25:57 +03:00
|
|
|
if (MOZ_LOG_TEST(GetTimerLog(), LogLevel::Debug)) {
|
2002-01-15 04:20:47 +03:00
|
|
|
double mean = 0, stddev = 0;
|
2002-03-08 23:11:49 +03:00
|
|
|
myNS_MeanAndStdDev(sDeltaNum, sDeltaSum, sDeltaSumSquared, &mean, &stddev);
|
2001-12-16 09:13:17 +03:00
|
|
|
|
2015-06-04 01:25:57 +03:00
|
|
|
MOZ_LOG(GetTimerLog(), LogLevel::Debug,
|
2014-05-27 11:15:35 +04:00
|
|
|
("sDeltaNum = %f, sDeltaSum = %f, sDeltaSumSquared = %f\n",
|
|
|
|
sDeltaNum, sDeltaSum, sDeltaSumSquared));
|
2015-06-04 01:25:57 +03:00
|
|
|
MOZ_LOG(GetTimerLog(), LogLevel::Debug,
|
2014-05-27 11:15:35 +04:00
|
|
|
("mean: %fms, stddev: %fms\n", mean, stddev));
|
2002-01-15 04:20:47 +03:00
|
|
|
}
|
2001-12-16 09:13:17 +03:00
|
|
|
|
2014-05-27 11:15:35 +04:00
|
|
|
if (!gThread) {
|
2001-12-16 09:13:17 +03:00
|
|
|
return;
|
2014-05-27 11:15:35 +04:00
|
|
|
}
|
2001-12-16 09:13:17 +03:00
|
|
|
|
|
|
|
gThread->Shutdown();
|
2002-02-18 03:10:55 +03:00
|
|
|
NS_RELEASE(gThread);
|
2001-12-16 09:13:17 +03:00
|
|
|
}
|
|
|
|
|
2017-06-01 03:13:19 +03:00
|
|
|
nsresult
|
|
|
|
nsTimerImpl::InitCommon(uint32_t aDelayMS, uint32_t aType,
|
|
|
|
Callback&& aNewCallback)
|
|
|
|
{
|
|
|
|
return InitCommon(TimeDuration::FromMilliseconds(aDelayMS),
|
|
|
|
aType, Move(aNewCallback));
|
|
|
|
}
|
|
|
|
|
2001-12-16 09:13:17 +03:00
|
|
|
|
2014-05-27 11:15:35 +04:00
|
|
|
nsresult
|
2017-06-01 03:13:19 +03:00
|
|
|
nsTimerImpl::InitCommon(const TimeDuration& aDelay, uint32_t aType,
|
|
|
|
Callback&& newCallback)
|
2002-12-04 03:57:20 +03:00
|
|
|
{
|
2017-01-11 22:59:19 +03:00
|
|
|
mMutex.AssertCurrentThreadOwns();
|
2004-05-11 13:38:50 +04:00
|
|
|
|
2014-05-27 11:15:35 +04:00
|
|
|
if (NS_WARN_IF(!gThread)) {
|
2013-11-20 01:27:37 +04:00
|
|
|
return NS_ERROR_NOT_INITIALIZED;
|
2014-05-27 11:15:35 +04:00
|
|
|
}
|
2017-05-01 21:42:11 +03:00
|
|
|
|
2013-04-23 21:24:20 +04:00
|
|
|
if (!mEventTarget) {
|
|
|
|
NS_ERROR("mEventTarget is NULL");
|
|
|
|
return NS_ERROR_NOT_INITIALIZED;
|
|
|
|
}
|
2004-05-11 13:38:50 +04:00
|
|
|
|
2015-07-28 18:10:54 +03:00
|
|
|
gThread->RemoveTimer(this);
|
2017-05-01 22:50:37 +03:00
|
|
|
mCallback.swap(newCallback);
|
2017-01-11 22:59:19 +03:00
|
|
|
++mGeneration;
|
2002-12-04 03:57:20 +03:00
|
|
|
|
2012-08-22 19:56:38 +04:00
|
|
|
mType = (uint8_t)aType;
|
2017-01-11 22:59:19 +03:00
|
|
|
mDelay = aDelay;
|
2017-06-01 03:13:19 +03:00
|
|
|
mTimeout = TimeStamp::Now() + mDelay;
|
2002-12-04 03:57:20 +03:00
|
|
|
|
|
|
|
return gThread->AddTimer(this);
|
|
|
|
}
|
|
|
|
|
2015-09-10 10:50:51 +03:00
|
|
|
nsresult
|
|
|
|
nsTimerImpl::InitWithFuncCallbackCommon(nsTimerCallbackFunc aFunc,
|
|
|
|
void* aClosure,
|
|
|
|
uint32_t aDelay,
|
|
|
|
uint32_t aType,
|
2017-01-31 02:56:05 +03:00
|
|
|
const Callback::Name& aName)
|
2001-12-16 09:13:17 +03:00
|
|
|
{
|
2014-05-27 11:15:35 +04:00
|
|
|
if (NS_WARN_IF(!aFunc)) {
|
2013-11-20 01:27:37 +04:00
|
|
|
return NS_ERROR_INVALID_ARG;
|
2014-05-27 11:15:35 +04:00
|
|
|
}
|
|
|
|
|
2017-01-11 22:59:19 +03:00
|
|
|
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);
|
2017-05-01 22:50:37 +03:00
|
|
|
return InitCommon(aDelay, aType, mozilla::Move(cb));
|
2001-12-16 09:13:17 +03:00
|
|
|
}
|
|
|
|
|
2017-01-10 06:08:43 +03:00
|
|
|
nsresult
|
2015-09-10 10:50:51 +03:00
|
|
|
nsTimerImpl::InitWithNamedFuncCallback(nsTimerCallbackFunc aFunc,
|
|
|
|
void* aClosure,
|
|
|
|
uint32_t aDelay,
|
|
|
|
uint32_t aType,
|
|
|
|
const char* aNameString)
|
|
|
|
{
|
2017-01-11 22:59:19 +03:00
|
|
|
Callback::Name name(aNameString);
|
2015-09-10 10:50:51 +03:00
|
|
|
return InitWithFuncCallbackCommon(aFunc, aClosure, aDelay, aType, name);
|
|
|
|
}
|
|
|
|
|
2017-01-10 06:08:43 +03:00
|
|
|
nsresult
|
2015-09-10 10:50:51 +03:00
|
|
|
nsTimerImpl::InitWithNameableFuncCallback(nsTimerCallbackFunc aFunc,
|
|
|
|
void* aClosure,
|
|
|
|
uint32_t aDelay,
|
|
|
|
uint32_t aType,
|
|
|
|
nsTimerNameCallbackFunc aNameFunc)
|
|
|
|
{
|
2017-01-11 22:59:19 +03:00
|
|
|
Callback::Name name(aNameFunc);
|
2015-09-10 10:50:51 +03:00
|
|
|
return InitWithFuncCallbackCommon(aFunc, aClosure, aDelay, aType, name);
|
|
|
|
}
|
|
|
|
|
2017-01-10 06:08:43 +03:00
|
|
|
nsresult
|
2014-05-27 11:15:35 +04:00
|
|
|
nsTimerImpl::InitWithCallback(nsITimerCallback* aCallback,
|
|
|
|
uint32_t aDelay,
|
|
|
|
uint32_t aType)
|
2017-06-01 03:13:19 +03:00
|
|
|
{
|
|
|
|
return InitHighResolutionWithCallback(aCallback,
|
|
|
|
TimeDuration::FromMilliseconds(aDelay),
|
|
|
|
aType);
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
nsTimerImpl::InitHighResolutionWithCallback(nsITimerCallback* aCallback,
|
|
|
|
const TimeDuration& aDelay,
|
|
|
|
uint32_t aType)
|
2001-12-16 09:13:17 +03:00
|
|
|
{
|
2014-05-27 11:15:35 +04:00
|
|
|
if (NS_WARN_IF(!aCallback)) {
|
2013-11-20 01:27:37 +04:00
|
|
|
return NS_ERROR_INVALID_ARG;
|
2014-05-27 11:15:35 +04:00
|
|
|
}
|
2005-11-02 03:46:37 +03:00
|
|
|
|
2017-01-11 22:59:19 +03:00
|
|
|
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);
|
2017-05-01 22:50:37 +03:00
|
|
|
return InitCommon(aDelay, aType, mozilla::Move(cb));
|
2001-12-16 09:13:17 +03:00
|
|
|
}
|
|
|
|
|
2017-01-10 06:08:43 +03:00
|
|
|
nsresult
|
2014-05-27 11:15:35 +04:00
|
|
|
nsTimerImpl::Init(nsIObserver* aObserver, uint32_t aDelay, uint32_t aType)
|
2002-02-20 07:01:34 +03:00
|
|
|
{
|
2014-05-27 11:15:35 +04:00
|
|
|
if (NS_WARN_IF(!aObserver)) {
|
2013-11-20 01:27:37 +04:00
|
|
|
return NS_ERROR_INVALID_ARG;
|
2014-05-27 11:15:35 +04:00
|
|
|
}
|
2005-11-02 03:46:37 +03:00
|
|
|
|
2017-01-11 22:59:19 +03:00
|
|
|
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);
|
2017-05-01 22:50:37 +03:00
|
|
|
return InitCommon(aDelay, aType, mozilla::Move(cb));
|
2002-02-20 07:01:34 +03:00
|
|
|
}
|
|
|
|
|
2017-09-14 20:19:24 +03:00
|
|
|
nsresult
|
|
|
|
nsTimerImpl::Cancel()
|
2001-12-16 09:13:17 +03:00
|
|
|
{
|
2017-09-14 20:19:24 +03:00
|
|
|
CancelImpl(false);
|
|
|
|
return NS_OK;
|
|
|
|
}
|
2017-01-11 22:59:19 +03:00
|
|
|
|
2017-09-14 20:19:24 +03:00
|
|
|
void
|
|
|
|
nsTimerImpl::CancelImpl(bool aClearITimer)
|
|
|
|
{
|
|
|
|
Callback cbTrash;
|
|
|
|
RefPtr<nsITimer> timerTrash;
|
2001-12-16 09:13:17 +03:00
|
|
|
|
2017-09-14 20:19:24 +03:00
|
|
|
{
|
|
|
|
MutexAutoLock lock(mMutex);
|
|
|
|
if (gThread) {
|
|
|
|
gThread->RemoveTimer(this);
|
|
|
|
}
|
2002-02-20 07:01:34 +03:00
|
|
|
|
2017-09-14 20:19:24 +03:00
|
|
|
cbTrash.swap(mCallback);
|
|
|
|
++mGeneration;
|
2007-10-24 23:11:41 +04:00
|
|
|
|
2017-09-14 20:19:24 +03:00
|
|
|
// Don't clear this if we're firing; once Fire returns, we'll get this call
|
|
|
|
// again.
|
|
|
|
if (aClearITimer &&
|
|
|
|
(mCallbackDuringFire.mType == Callback::Type::Unknown)) {
|
|
|
|
MOZ_RELEASE_ASSERT(mITimer, "mITimer was nulled already! "
|
|
|
|
"This indicates that someone has messed up the refcount on nsTimer!");
|
|
|
|
timerTrash.swap(mITimer);
|
|
|
|
}
|
2017-04-06 20:29:57 +03:00
|
|
|
}
|
2001-12-16 09:13:17 +03:00
|
|
|
}
|
|
|
|
|
2017-01-10 06:08:43 +03:00
|
|
|
nsresult
|
2014-05-27 11:15:35 +04:00
|
|
|
nsTimerImpl::SetDelay(uint32_t aDelay)
|
2001-12-16 09:13:17 +03:00
|
|
|
{
|
2017-01-11 22:59:19 +03:00
|
|
|
MutexAutoLock lock(mMutex);
|
|
|
|
if (GetCallback().mType == Callback::Type::Unknown && !IsRepeating()) {
|
2009-02-03 17:42:21 +03:00
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
|
2016-08-05 18:07:38 +03:00
|
|
|
bool reAdd = false;
|
|
|
|
if (gThread) {
|
|
|
|
reAdd = NS_SUCCEEDED(gThread->RemoveTimer(this));
|
|
|
|
}
|
|
|
|
|
2017-06-01 03:13:19 +03:00
|
|
|
mDelay = TimeDuration::FromMilliseconds(aDelay);
|
|
|
|
mTimeout = TimeStamp::Now() + mDelay;
|
2001-12-16 09:13:17 +03:00
|
|
|
|
2016-08-05 18:07:38 +03:00
|
|
|
if (reAdd) {
|
|
|
|
gThread->AddTimer(this);
|
2014-05-27 11:15:35 +04:00
|
|
|
}
|
2002-09-07 09:38:16 +04:00
|
|
|
|
|
|
|
return NS_OK;
|
2001-12-16 09:13:17 +03:00
|
|
|
}
|
|
|
|
|
2017-01-10 06:08:43 +03:00
|
|
|
nsresult
|
2014-05-27 11:15:35 +04:00
|
|
|
nsTimerImpl::GetDelay(uint32_t* aDelay)
|
2002-09-07 09:38:16 +04:00
|
|
|
{
|
2017-01-11 22:59:19 +03:00
|
|
|
MutexAutoLock lock(mMutex);
|
2017-06-01 03:13:19 +03:00
|
|
|
*aDelay = mDelay.ToMilliseconds();
|
2002-09-07 09:38:16 +04:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2017-01-10 06:08:43 +03:00
|
|
|
nsresult
|
2014-05-27 11:15:35 +04:00
|
|
|
nsTimerImpl::SetType(uint32_t aType)
|
2001-12-16 09:13:17 +03:00
|
|
|
{
|
2017-01-11 22:59:19 +03:00
|
|
|
MutexAutoLock lock(mMutex);
|
2012-08-22 19:56:38 +04:00
|
|
|
mType = (uint8_t)aType;
|
2001-12-16 09:13:17 +03:00
|
|
|
// XXX if this is called, we should change the actual type.. this could effect
|
2002-04-14 04:27:16 +04:00
|
|
|
// repeating timers. we need to ensure in Fire() that if mType has changed
|
2001-12-16 09:13:17 +03:00
|
|
|
// during the callback that we don't end up with the timer in the queue twice.
|
2002-09-07 09:38:16 +04:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2017-01-10 06:08:43 +03:00
|
|
|
nsresult
|
2014-05-27 11:15:35 +04:00
|
|
|
nsTimerImpl::GetType(uint32_t* aType)
|
2002-09-07 09:38:16 +04:00
|
|
|
{
|
2017-01-11 22:59:19 +03:00
|
|
|
MutexAutoLock lock(mMutex);
|
2002-09-07 09:38:16 +04:00
|
|
|
*aType = mType;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-01-10 06:08:43 +03:00
|
|
|
nsresult
|
2014-05-27 11:15:35 +04:00
|
|
|
nsTimerImpl::GetClosure(void** aClosure)
|
2002-09-07 09:38:16 +04:00
|
|
|
{
|
2017-01-11 22:59:19 +03:00
|
|
|
MutexAutoLock lock(mMutex);
|
|
|
|
*aClosure = GetCallback().mClosure;
|
2002-09-07 09:38:16 +04:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-01-10 06:08:43 +03:00
|
|
|
nsresult
|
2014-05-27 11:15:35 +04:00
|
|
|
nsTimerImpl::GetCallback(nsITimerCallback** aCallback)
|
2005-05-05 00:22:32 +04:00
|
|
|
{
|
2017-01-11 22:59:19 +03:00
|
|
|
MutexAutoLock lock(mMutex);
|
|
|
|
if (GetCallback().mType == Callback::Type::Interface) {
|
|
|
|
NS_IF_ADDREF(*aCallback = GetCallback().mCallback.i);
|
2014-05-27 11:15:35 +04:00
|
|
|
} else {
|
2012-07-30 18:20:58 +04:00
|
|
|
*aCallback = nullptr;
|
2014-05-27 11:15:35 +04:00
|
|
|
}
|
2005-05-05 00:22:32 +04:00
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-01-10 06:08:43 +03:00
|
|
|
nsresult
|
2014-05-27 11:15:35 +04:00
|
|
|
nsTimerImpl::GetTarget(nsIEventTarget** aTarget)
|
2008-07-24 21:20:33 +04:00
|
|
|
{
|
2017-01-11 22:59:19 +03:00
|
|
|
MutexAutoLock lock(mMutex);
|
2008-07-24 21:20:33 +04:00
|
|
|
NS_IF_ADDREF(*aTarget = mEventTarget);
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-01-10 06:08:43 +03:00
|
|
|
nsresult
|
2014-05-27 11:15:35 +04:00
|
|
|
nsTimerImpl::SetTarget(nsIEventTarget* aTarget)
|
2008-07-24 21:20:33 +04:00
|
|
|
{
|
2017-01-11 22:59:19 +03:00
|
|
|
MutexAutoLock lock(mMutex);
|
|
|
|
if (NS_WARN_IF(mCallback.mType != Callback::Type::Unknown)) {
|
2013-11-20 01:27:37 +04:00
|
|
|
return NS_ERROR_ALREADY_INITIALIZED;
|
2014-05-27 11:15:35 +04:00
|
|
|
}
|
2008-07-24 21:20:33 +04:00
|
|
|
|
2014-05-27 11:15:35 +04:00
|
|
|
if (aTarget) {
|
2008-07-24 21:20:33 +04:00
|
|
|
mEventTarget = aTarget;
|
2014-05-27 11:15:35 +04:00
|
|
|
} else {
|
Bug 1382922 - Refactor event queue to allow multiple implementations (r=erahm)
This patch refactors the nsThread event queue to clean it up and to make it easier to restructure. The fundamental concepts are as follows:
Each nsThread will have a pointer to a refcounted SynchronizedEventQueue. A SynchronizedEQ takes care of doing the locking and condition variable work when posting and popping events. For the actual storage of events, it delegates to an AbstractEventQueue data structure. It keeps a UniquePtr to the AbstractEventQueue that it uses for storage.
Both SynchronizedEQ and AbstractEventQueue are abstract classes. There is only one concrete implementation of SynchronizedEQ in this patch, which is called ThreadEventQueue. ThreadEventQueue uses locks and condition variables to post and pop events the same way nsThread does. It also encapsulates the functionality that DOM workers need to implement their special event loops (PushEventQueue and PopEventQueue). In later Quantum DOM work, I plan to have another SynchronizedEQ implementation for the main thread, called SchedulerEventQueue. It will have special code for the cooperatively scheduling threads in Quantum DOM.
There are two concrete implementations of AbstractEventQueue in this patch: EventQueue and PrioritizedEventQueue. EventQueue replaces the old nsEventQueue. The other AbstractEventQueue implementation is PrioritizedEventQueue, which uses multiple queues for different event priorities.
The final major piece here is ThreadEventTarget, which splits some of the code for posting events out of nsThread. Eventually, my plan is for multiple cooperatively scheduled nsThreads to be able to share a ThreadEventTarget. In this patch, though, each nsThread has its own ThreadEventTarget. The class's purpose is just to collect some related code together.
One final note: I tried to avoid virtual dispatch overhead as much as possible. Calls to SynchronizedEQ methods do use virtual dispatch, since I plan to use different implementations for different threads with Quantum DOM. But all the calls to EventQueue methods should be non-virtual. Although the methods are declared virtual, all the classes used are final and the concrete classes involved should all be known through templatization.
MozReview-Commit-ID: 9Evtr9oIJvx
2017-06-21 05:42:13 +03:00
|
|
|
mEventTarget = mozilla::GetCurrentThreadEventTarget();
|
2014-05-27 11:15:35 +04:00
|
|
|
}
|
2008-07-24 21:20:33 +04:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2017-06-01 03:13:20 +03:00
|
|
|
nsresult
|
|
|
|
nsTimerImpl::GetAllowedEarlyFiringMicroseconds(uint32_t* aValueOut)
|
|
|
|
{
|
|
|
|
*aValueOut = gThread ? gThread->AllowedEarlyFiringMicroseconds() : 0;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
2008-07-24 21:20:33 +04:00
|
|
|
|
2014-05-27 11:15:35 +04:00
|
|
|
void
|
2017-01-11 22:59:19 +03:00
|
|
|
nsTimerImpl::Fire(int32_t aGeneration)
|
|
|
|
{
|
|
|
|
uint8_t oldType;
|
|
|
|
uint32_t oldDelay;
|
|
|
|
TimeStamp oldTimeout;
|
2017-02-15 01:27:33 +03:00
|
|
|
nsCOMPtr<nsITimer> kungFuDeathGrip;
|
2017-01-11 22:59:19 +03:00
|
|
|
|
|
|
|
{
|
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
|
|
|
|
mCallbackDuringFire.swap(mCallback);
|
|
|
|
oldType = mType;
|
2017-06-01 03:13:19 +03:00
|
|
|
oldDelay = mDelay.ToMilliseconds();
|
2017-01-11 22:59:19 +03:00
|
|
|
oldTimeout = mTimeout;
|
2017-02-15 01:27:33 +03:00
|
|
|
// 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;
|
2014-05-27 11:15:35 +04:00
|
|
|
}
|
2001-12-17 09:59:55 +03:00
|
|
|
|
Bug 1375392 - Tweak the PROFILER_LABEL* macros. r=mstange.
This patch makes the following changes to the macros.
- Removes PROFILER_LABEL_FUNC. It's only suitable for use in functions outside
classes, due to PROFILER_FUNCTION_NAME not getting class names, and it was
mostly misused.
- Removes PROFILER_FUNCTION_NAME. It's no longer used, and __func__ is
universally available now anyway.
- Combines the first two string literal arguments of PROFILER_LABEL and
PROFILER_LABEL_DYNAMIC into a single argument. There was no good reason for
them to be separate, and it forced a '::' in the label, which isn't always
appropriate. Also, the meaning of the "name_space" argument was interpreted
in an interesting variety of ways.
- Adds an "AUTO_" prefix to PROFILER_LABEL and PROFILER_LABEL_DYNAMIC, to make
it clearer they construct RAII objects rather than just being function calls.
(I myself have screwed up the scoping because of this in the past.)
- Fills in the 'js::ProfileEntry::Category::' qualifier within the macro, so
the caller doesn't need to. This makes a *lot* more of the uses fit onto a
single line.
The patch also makes the following changes to the macro uses (beyond those
required by the changes described above).
- Fixes a bunch of labels that had gotten out of sync with the name of the
class and/or function that encloses them.
- Removes a useless PROFILER_LABEL use within a trivial scope in
EventStateManager::DispatchMouseOrPointerEvent(). It clearly wasn't serving
any useful purpose. It also serves as extra evidence that the AUTO_ prefix is
a good idea.
- Tweaks DecodePool::SyncRunIf{Preferred,Possible} so that the labelling is
done within them, instead of at their callsites, because that's a more
standard way of doing things.
--HG--
extra : rebase_source : 318d1bc6fc1425a94aacbf489dd46e4f83211de4
2017-06-22 10:08:53 +03:00
|
|
|
AUTO_PROFILER_LABEL("nsTimerImpl::Fire", OTHER);
|
2011-12-03 02:05:33 +04:00
|
|
|
|
2013-08-20 23:22:27 +04:00
|
|
|
TimeStamp now = TimeStamp::Now();
|
2015-06-04 01:25:57 +03:00
|
|
|
if (MOZ_LOG_TEST(GetTimerLog(), LogLevel::Debug)) {
|
2017-01-11 22:59:19 +03:00
|
|
|
TimeDuration delta = now - oldTimeout;
|
|
|
|
int32_t d = delta.ToMilliseconds(); // delta in ms
|
|
|
|
sDeltaSum += abs(d);
|
2002-01-15 04:20:47 +03:00
|
|
|
sDeltaSumSquared += double(d) * double(d);
|
2002-03-08 23:11:49 +03:00
|
|
|
sDeltaNum++;
|
2002-01-15 04:20:47 +03:00
|
|
|
|
2015-06-04 01:25:57 +03:00
|
|
|
MOZ_LOG(GetTimerLog(), LogLevel::Debug,
|
2017-01-11 22:59:19 +03:00
|
|
|
("[this=%p] expected delay time %4ums\n", this, oldDelay));
|
2015-06-04 01:25:57 +03:00
|
|
|
MOZ_LOG(GetTimerLog(), LogLevel::Debug,
|
2017-01-11 22:59:19 +03:00
|
|
|
("[this=%p] actual delay time %4dms\n", this, oldDelay + d));
|
2015-06-04 01:25:57 +03:00
|
|
|
MOZ_LOG(GetTimerLog(), LogLevel::Debug,
|
2017-01-11 22:59:19 +03:00
|
|
|
("[this=%p] (mType is %d) -------\n", this, oldType));
|
2015-06-04 01:25:57 +03:00
|
|
|
MOZ_LOG(GetTimerLog(), LogLevel::Debug,
|
2017-01-11 22:59:19 +03:00
|
|
|
("[this=%p] delta %4dms\n", this, d));
|
2002-01-15 04:20:47 +03:00
|
|
|
}
|
2001-12-16 09:13:17 +03:00
|
|
|
|
2015-09-10 10:50:51 +03:00
|
|
|
if (MOZ_LOG_TEST(GetTimerFiringsLog(), LogLevel::Debug)) {
|
2017-01-11 22:59:19 +03:00
|
|
|
LogFiring(mCallbackDuringFire, oldType, oldDelay);
|
2015-09-10 10:50:51 +03:00
|
|
|
}
|
|
|
|
|
2017-01-11 22:59:19 +03:00
|
|
|
switch (mCallbackDuringFire.mType) {
|
|
|
|
case Callback::Type::Function:
|
|
|
|
mCallbackDuringFire.mCallback.c(mITimer, mCallbackDuringFire.mClosure);
|
2002-03-08 23:11:49 +03:00
|
|
|
break;
|
2017-01-11 22:59:19 +03:00
|
|
|
case Callback::Type::Interface:
|
|
|
|
mCallbackDuringFire.mCallback.i->Notify(mITimer);
|
2002-03-08 23:11:49 +03:00
|
|
|
break;
|
2017-01-11 22:59:19 +03:00
|
|
|
case Callback::Type::Observer:
|
|
|
|
mCallbackDuringFire.mCallback.o->Observe(mITimer, NS_TIMER_CALLBACK_TOPIC,
|
|
|
|
nullptr);
|
2002-03-08 23:11:49 +03:00
|
|
|
break;
|
2014-05-27 11:15:35 +04:00
|
|
|
default:
|
|
|
|
;
|
2001-12-16 09:13:17 +03:00
|
|
|
}
|
|
|
|
|
2017-01-11 22:59:19 +03:00
|
|
|
Callback trash; // Swap into here to dispose of callback after the unlock
|
|
|
|
MutexAutoLock lock(mMutex);
|
|
|
|
if (aGeneration == mGeneration && IsRepeating()) {
|
|
|
|
// Repeating timer has not been re-init or canceled; reschedule
|
|
|
|
mCallbackDuringFire.swap(mCallback);
|
2017-05-25 04:12:55 +03:00
|
|
|
if (IsSlack()) {
|
2017-06-01 03:13:19 +03:00
|
|
|
mTimeout = TimeStamp::Now() + mDelay;
|
2016-08-05 20:50:00 +03:00
|
|
|
} else {
|
2017-06-01 03:13:19 +03:00
|
|
|
mTimeout = mTimeout + mDelay;
|
2017-01-11 22:59:19 +03:00
|
|
|
}
|
|
|
|
if (gThread) {
|
|
|
|
gThread->AddTimer(this);
|
2014-05-27 11:15:35 +04:00
|
|
|
}
|
2007-10-24 23:11:41 +04:00
|
|
|
}
|
|
|
|
|
2017-01-11 22:59:19 +03:00
|
|
|
mCallbackDuringFire.swap(trash);
|
|
|
|
|
2015-06-04 01:25:57 +03:00
|
|
|
MOZ_LOG(GetTimerLog(), LogLevel::Debug,
|
2015-05-07 19:43:37 +03:00
|
|
|
("[this=%p] Took %fms to fire timer callback\n",
|
|
|
|
this, (TimeStamp::Now() - now).ToMilliseconds()));
|
2001-12-16 09:13:17 +03:00
|
|
|
}
|
|
|
|
|
2016-03-07 22:00:41 +03:00
|
|
|
#if defined(HAVE_DLADDR) && defined(HAVE___CXA_DEMANGLE)
|
2015-09-22 03:13:51 +03:00
|
|
|
#define USE_DLADDR 1
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef USE_DLADDR
|
2015-09-10 10:50:51 +03:00
|
|
|
#include <cxxabi.h>
|
|
|
|
#include <dlfcn.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// See the big comment above GetTimerFiringsLog() to understand this code.
|
|
|
|
void
|
2017-01-11 22:59:19 +03:00
|
|
|
nsTimerImpl::LogFiring(const Callback& aCallback, uint8_t aType, uint32_t aDelay)
|
2015-09-10 10:50:51 +03:00
|
|
|
{
|
|
|
|
const char* typeStr;
|
2017-01-11 22:59:19 +03:00
|
|
|
switch (aType) {
|
2017-05-25 04:12:55 +03:00
|
|
|
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;
|
2016-07-20 23:16:40 +03:00
|
|
|
case nsITimer::TYPE_REPEATING_PRECISE: /* fall through */
|
2017-05-25 04:12:55 +03:00
|
|
|
case nsITimer::TYPE_REPEATING_PRECISE_CAN_SKIP: typeStr = "PRECISE "; break;
|
2015-09-10 10:50:51 +03:00
|
|
|
default: MOZ_CRASH("bad type");
|
|
|
|
}
|
|
|
|
|
2017-01-11 22:59:19 +03:00
|
|
|
switch (aCallback.mType) {
|
|
|
|
case Callback::Type::Function: {
|
2015-09-10 10:50:51 +03:00
|
|
|
bool needToFreeName = false;
|
|
|
|
const char* annotation = "";
|
|
|
|
const char* name;
|
|
|
|
static const size_t buflen = 1024;
|
|
|
|
char buf[buflen];
|
|
|
|
|
2017-01-11 22:59:19 +03:00
|
|
|
if (aCallback.mName.is<Callback::NameString>()) {
|
|
|
|
name = aCallback.mName.as<Callback::NameString>();
|
2015-09-10 10:50:51 +03:00
|
|
|
|
2017-01-11 22:59:19 +03:00
|
|
|
} else if (aCallback.mName.is<Callback::NameFunc>()) {
|
|
|
|
aCallback.mName.as<Callback::NameFunc>()(
|
|
|
|
mITimer, /* aAnonymize = */ false, aCallback.mClosure, buf, buflen);
|
2015-09-10 10:50:51 +03:00
|
|
|
name = buf;
|
|
|
|
|
|
|
|
} else {
|
2017-01-11 22:59:19 +03:00
|
|
|
MOZ_ASSERT(aCallback.mName.is<Callback::NameNothing>());
|
2015-09-22 03:13:51 +03:00
|
|
|
#ifdef USE_DLADDR
|
2015-09-10 10:50:51 +03:00
|
|
|
annotation = "[from dladdr] ";
|
|
|
|
|
|
|
|
Dl_info info;
|
2017-01-11 22:59:19 +03:00
|
|
|
void* addr = reinterpret_cast<void*>(aCallback.mCallback.c);
|
2015-09-22 03:13:51 +03:00
|
|
|
if (dladdr(addr, &info) == 0) {
|
2015-09-10 10:50:51 +03:00
|
|
|
name = "???[dladdr: failed]";
|
|
|
|
|
2015-09-22 03:13:51 +03:00
|
|
|
} else if (info.dli_sname) {
|
2015-09-10 10:50:51 +03:00
|
|
|
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]";
|
|
|
|
}
|
2015-09-22 03:13:51 +03:00
|
|
|
|
|
|
|
} else if (info.dli_fname) {
|
|
|
|
// The "#0: " prefix is necessary for fix_linux_stack.py to interpret
|
|
|
|
// this string as something to convert.
|
|
|
|
snprintf(buf, buflen, "#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]";
|
2015-09-10 10:50:51 +03:00
|
|
|
}
|
|
|
|
#else
|
2015-09-22 03:13:51 +03:00
|
|
|
name = "???[dladdr is unimplemented or doesn't work well on this OS]";
|
2015-09-10 10:50:51 +03:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
MOZ_LOG(GetTimerFiringsLog(), LogLevel::Debug,
|
|
|
|
("[%d] fn timer (%s %5d ms): %s%s\n",
|
2017-01-11 22:59:19 +03:00
|
|
|
getpid(), typeStr, aDelay, annotation, name));
|
2015-09-10 10:50:51 +03:00
|
|
|
|
|
|
|
if (needToFreeName) {
|
|
|
|
free(const_cast<char*>(name));
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2017-01-11 22:59:19 +03:00
|
|
|
case Callback::Type::Interface: {
|
2015-09-10 10:50:51 +03:00
|
|
|
MOZ_LOG(GetTimerFiringsLog(), LogLevel::Debug,
|
|
|
|
("[%d] iface timer (%s %5d ms): %p\n",
|
2017-01-11 22:59:19 +03:00
|
|
|
getpid(), typeStr, aDelay, aCallback.mCallback.i));
|
2015-09-10 10:50:51 +03:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2017-01-11 22:59:19 +03:00
|
|
|
case Callback::Type::Observer: {
|
2015-09-10 10:50:51 +03:00
|
|
|
MOZ_LOG(GetTimerFiringsLog(), LogLevel::Debug,
|
|
|
|
("[%d] obs timer (%s %5d ms): %p\n",
|
2017-01-11 22:59:19 +03:00
|
|
|
getpid(), typeStr, aDelay, aCallback.mCallback.o));
|
2015-09-10 10:50:51 +03:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2017-01-11 22:59:19 +03:00
|
|
|
case Callback::Type::Unknown:
|
2015-09-10 10:50:51 +03:00
|
|
|
default: {
|
|
|
|
MOZ_LOG(GetTimerFiringsLog(), LogLevel::Debug,
|
|
|
|
("[%d] ??? timer (%s, %5d ms)\n",
|
2017-01-11 22:59:19 +03:00
|
|
|
getpid(), typeStr, aDelay));
|
2015-09-10 10:50:51 +03:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-11-23 07:40:13 +03:00
|
|
|
void
|
|
|
|
nsTimerImpl::GetName(nsACString& aName)
|
|
|
|
{
|
2017-01-11 22:59:19 +03:00
|
|
|
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>()) {
|
2016-11-23 07:40:13 +03:00
|
|
|
static const size_t buflen = 1024;
|
|
|
|
char buf[buflen];
|
2017-01-11 22:59:19 +03:00
|
|
|
cb.mName.as<Callback::NameFunc>()(
|
|
|
|
mITimer, /* aAnonymize = */ true, cb.mClosure, buf, buflen);
|
2016-11-23 07:40:13 +03:00
|
|
|
aName.Assign(buf);
|
|
|
|
} else {
|
2017-01-11 22:59:19 +03:00
|
|
|
MOZ_ASSERT(cb.mName.is<Callback::NameNothing>());
|
2017-02-14 02:45:23 +03:00
|
|
|
aName.AssignLiteral("Anonymous_callback_timer");
|
2016-11-23 07:40:13 +03:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2017-01-11 22:59:19 +03:00
|
|
|
case Callback::Type::Interface:
|
|
|
|
if (nsCOMPtr<nsINamed> named = do_QueryInterface(cb.mCallback.i)) {
|
2016-11-23 07:40:13 +03:00
|
|
|
named->GetName(aName);
|
2017-01-19 01:01:05 +03:00
|
|
|
} else {
|
2017-02-14 02:45:23 +03:00
|
|
|
aName.AssignLiteral("Anonymous_interface_timer");
|
2016-11-23 07:40:13 +03:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2017-01-11 22:59:19 +03:00
|
|
|
case Callback::Type::Observer:
|
|
|
|
if (nsCOMPtr<nsINamed> named = do_QueryInterface(cb.mCallback.o)) {
|
2016-11-23 07:40:13 +03:00
|
|
|
named->GetName(aName);
|
2017-01-19 01:01:05 +03:00
|
|
|
} else {
|
2017-02-14 02:45:23 +03:00
|
|
|
aName.AssignLiteral("Anonymous_observer_timer");
|
2016-11-23 07:40:13 +03:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2017-01-11 22:59:19 +03:00
|
|
|
case Callback::Type::Unknown:
|
2017-02-14 02:45:23 +03:00
|
|
|
aName.AssignLiteral("Canceled_timer");
|
2016-11-23 07:40:13 +03:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-21 00:56:07 +03:00
|
|
|
void
|
|
|
|
nsTimerImpl::SetHolder(nsTimerImplHolder* aHolder)
|
|
|
|
{
|
|
|
|
mHolder = aHolder;
|
|
|
|
}
|
|
|
|
|
2016-07-20 23:16:40 +03:00
|
|
|
nsTimer::~nsTimer()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2014-06-23 02:02:59 +04:00
|
|
|
size_t
|
2016-07-20 23:16:40 +03:00
|
|
|
nsTimer::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
|
2014-06-23 02:02:59 +04:00
|
|
|
{
|
|
|
|
return aMallocSizeOf(this);
|
|
|
|
}
|
2015-06-16 05:57:19 +03:00
|
|
|
|
2017-01-11 22:59:19 +03:00
|
|
|
/* static */
|
|
|
|
const nsTimerImpl::Callback::NameNothing nsTimerImpl::Callback::Nothing = 0;
|
2015-09-10 10:50:51 +03:00
|
|
|
|
2015-06-16 05:57:19 +03:00
|
|
|
#ifdef MOZ_TASK_TRACER
|
|
|
|
void
|
|
|
|
nsTimerImpl::GetTLSTraceInfo()
|
|
|
|
{
|
|
|
|
mTracedTask.GetTLSTraceInfo();
|
|
|
|
}
|
|
|
|
|
|
|
|
TracedTaskCommon
|
|
|
|
nsTimerImpl::GetTracedTask()
|
|
|
|
{
|
|
|
|
return mTracedTask;
|
|
|
|
}
|
2016-07-20 23:16:40 +03:00
|
|
|
|
2015-06-16 05:57:19 +03:00
|
|
|
#endif
|