Bug 995730 - Convert xpcom/threads/ to Gecko style. r=froydnj

--HG--
extra : rebase_source : 1f6f179f44db87f55ebfe5d855192adfad7d1b74
This commit is contained in:
Birunthan Mohanathas 2014-05-27 10:15:35 +03:00
parent 9c80e3cef5
commit 67dad11bae
25 changed files with 1510 additions and 1175 deletions

View File

@ -38,7 +38,7 @@ private:
#ifdef MOZ_NUWA_PROCESS
if (IsNuwaProcess()) {
NS_ASSERTION(NuwaMarkCurrentThread != nullptr,
NS_ASSERTION(NuwaMarkCurrentThread,
"NuwaMarkCurrentThread is undefined!");
NuwaMarkCurrentThread(nullptr, nullptr);
}
@ -117,9 +117,8 @@ public:
static void Startup()
{
/* We can tolerate init() failing.
The if block turns off warn_unused_result. */
if (!sTlsKey.init()) {}
/* We can tolerate init() failing. */
(void)!sTlsKey.init();
}
// Hang timeout in ticks
@ -176,18 +175,14 @@ BackgroundHangManager::BackgroundHangManager()
PR_USER_THREAD, MonitorThread, this,
PR_PRIORITY_LOW, PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, 0);
MOZ_ASSERT(mHangMonitorThread,
"Failed to create monitor thread");
MOZ_ASSERT(mHangMonitorThread, "Failed to create monitor thread");
}
BackgroundHangManager::~BackgroundHangManager()
{
MOZ_ASSERT(mShutdown,
"Destruction without Shutdown call");
MOZ_ASSERT(mHangThreads.isEmpty(),
"Destruction with outstanding monitors");
MOZ_ASSERT(mHangMonitorThread,
"No monitor thread");
MOZ_ASSERT(mShutdown, "Destruction without Shutdown call");
MOZ_ASSERT(mHangThreads.isEmpty(), "Destruction with outstanding monitors");
MOZ_ASSERT(mHangMonitorThread, "No monitor thread");
// PR_CreateThread could have failed above due to resource limitation
if (mHangMonitorThread) {

View File

@ -26,7 +26,8 @@
#define REPORT_CHROME_HANGS
#endif
namespace mozilla { namespace HangMonitor {
namespace mozilla {
namespace HangMonitor {
/**
* A flag which may be set from within a debugger to disable the hang
@ -112,21 +113,22 @@ Crash()
#ifdef REPORT_CHROME_HANGS
static void
ChromeStackWalker(void *aPC, void *aSP, void *aClosure)
ChromeStackWalker(void* aPC, void* aSP, void* aClosure)
{
MOZ_ASSERT(aClosure);
std::vector<uintptr_t> *stack =
std::vector<uintptr_t>* stack =
static_cast<std::vector<uintptr_t>*>(aClosure);
if (stack->size() == MAX_CALL_STACK_PCS)
if (stack->size() == MAX_CALL_STACK_PCS) {
return;
}
MOZ_ASSERT(stack->size() < MAX_CALL_STACK_PCS);
stack->push_back(reinterpret_cast<uintptr_t>(aPC));
}
static void
GetChromeHangReport(Telemetry::ProcessedStack &aStack,
int32_t &aSystemUptime,
int32_t &aFirefoxUptime)
GetChromeHangReport(Telemetry::ProcessedStack& aStack,
int32_t& aSystemUptime,
int32_t& aFirefoxUptime)
{
MOZ_ASSERT(winMainThreadHandle);
@ -135,14 +137,16 @@ GetChromeHangReport(Telemetry::ProcessedStack &aStack,
std::vector<uintptr_t> rawStack;
rawStack.reserve(MAX_CALL_STACK_PCS);
DWORD ret = ::SuspendThread(winMainThreadHandle);
if (ret == -1)
if (ret == -1) {
return;
}
NS_StackWalk(ChromeStackWalker, /* skipFrames */ 0, /* maxFrames */ 0,
reinterpret_cast<void*>(&rawStack),
reinterpret_cast<uintptr_t>(winMainThreadHandle), nullptr);
ret = ::ResumeThread(winMainThreadHandle);
if (ret == -1)
if (ret == -1) {
return;
}
aStack = Telemetry::GetStackAndModules(rawStack);
// Record system uptime (in minutes) at the time of the hang
@ -217,8 +221,7 @@ ThreadMain(void*)
}
}
#endif
}
else {
} else {
#ifdef REPORT_CHROME_HANGS
if (waitCount >= 2) {
uint32_t hangDuration = PR_IntervalToSeconds(now - lastTimestamp);
@ -234,8 +237,7 @@ ThreadMain(void*)
PRIntervalTime timeout;
if (gTimeout <= 0) {
timeout = PR_INTERVAL_NO_TIMEOUT;
}
else {
} else {
timeout = PR_MillisecondsToInterval(gTimeout * 500);
}
lock.Wait(timeout);
@ -248,8 +250,9 @@ Startup()
// The hang detector only runs in chrome processes. If you change this,
// you must also deal with the threadsafety of AnnotateCrashReport in
// non-chrome processes!
if (GeckoProcessType_Default != XRE_GetProcessType())
if (GeckoProcessType_Default != XRE_GetProcessType()) {
return;
}
MOZ_ASSERT(!gMonitor, "Hang monitor already initialized");
gMonitor = new Monitor("HangMonitor");
@ -261,8 +264,9 @@ Startup()
Preferences::RegisterCallback(PrefChanged, kTelemetryPrefName, nullptr);
winMainThreadHandle =
OpenThread(THREAD_ALL_ACCESS, FALSE, GetCurrentThreadId());
if (!winMainThreadHandle)
if (!winMainThreadHandle) {
return;
}
#endif
// Don't actually start measuring hangs until we hit the main event loop.
@ -280,12 +284,14 @@ Startup()
void
Shutdown()
{
if (GeckoProcessType_Default != XRE_GetProcessType())
if (GeckoProcessType_Default != XRE_GetProcessType()) {
return;
}
MOZ_ASSERT(gMonitor, "Hang monitor not started");
{ // Scope the lock we're going to delete later
{
// Scope the lock we're going to delete later
MonitorAutoLock lock(*gMonitor);
gShutdown = true;
lock.Notify();
@ -307,11 +313,11 @@ IsUIMessageWaiting()
#ifndef XP_WIN
return false;
#else
#define NS_WM_IMEFIRST WM_IME_SETCONTEXT
#define NS_WM_IMELAST WM_IME_KEYUP
#define NS_WM_IMEFIRST WM_IME_SETCONTEXT
#define NS_WM_IMELAST WM_IME_KEYUP
BOOL haveUIMessageWaiting = FALSE;
MSG msg;
haveUIMessageWaiting |= ::PeekMessageW(&msg, nullptr, WM_KEYFIRST,
haveUIMessageWaiting |= ::PeekMessageW(&msg, nullptr, WM_KEYFIRST,
WM_IME_KEYLAST, PM_NOREMOVE);
haveUIMessageWaiting |= ::PeekMessageW(&msg, nullptr, NS_WM_IMEFIRST,
NS_WM_IMELAST, PM_NOREMOVE);
@ -322,32 +328,32 @@ IsUIMessageWaiting()
}
void
NotifyActivity(ActivityType activityType)
NotifyActivity(ActivityType aActivityType)
{
MOZ_ASSERT(NS_IsMainThread(),
"HangMonitor::Notify called from off the main thread.");
// Determine the activity type more specifically
if (activityType == kGeneralActivity) {
activityType = IsUIMessageWaiting() ? kActivityUIAVail :
kActivityNoUIAVail;
if (aActivityType == kGeneralActivity) {
aActivityType = IsUIMessageWaiting() ? kActivityUIAVail :
kActivityNoUIAVail;
}
// Calculate the cumulative amount of lag time since the last UI message
static uint32_t cumulativeUILagMS = 0;
switch(activityType) {
case kActivityNoUIAVail:
cumulativeUILagMS = 0;
break;
case kActivityUIAVail:
case kUIActivity:
if (gTimestamp != PR_INTERVAL_NO_WAIT) {
cumulativeUILagMS += PR_IntervalToMilliseconds(PR_IntervalNow() -
gTimestamp);
}
break;
default:
break;
switch (aActivityType) {
case kActivityNoUIAVail:
cumulativeUILagMS = 0;
break;
case kActivityUIAVail:
case kUIActivity:
if (gTimestamp != PR_INTERVAL_NO_WAIT) {
cumulativeUILagMS += PR_IntervalToMilliseconds(PR_IntervalNow() -
gTimestamp);
}
break;
default:
break;
}
// This is not a locked activity because PRTimeStamp is a 32-bit quantity
@ -357,7 +363,7 @@ NotifyActivity(ActivityType activityType)
// If we have UI activity we should reset the timer and report it if it is
// significant enough.
if (activityType == kUIActivity) {
if (aActivityType == kUIActivity) {
// The minimum amount of lag time that we should report for telemetry data.
// Mozilla's UI responsiveness goal is 50ms
static const uint32_t kUIResponsivenessThresholdMS = 50;
@ -387,4 +393,5 @@ Suspend()
}
}
} } // namespace mozilla::HangMonitor
} // namespace HangMonitor
} // namespace mozilla

View File

@ -1,4 +1,5 @@
/* -*- 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/. */
@ -6,12 +7,14 @@
#ifndef mozilla_HangMonitor_h
#define mozilla_HangMonitor_h
namespace mozilla { namespace HangMonitor {
namespace mozilla {
namespace HangMonitor {
/**
* Signifies the type of activity in question
*/
enum ActivityType {
enum ActivityType
{
/* There is activity and it is known to be UI related activity. */
kUIActivity,
@ -49,6 +52,7 @@ void NotifyActivity(ActivityType activityType = kGeneralActivity);
*/
void Suspend();
} } // namespace mozilla::HangMonitor
} // namespace HangMonitor
} // namespace mozilla
#endif // mozilla_HangMonitor_h

View File

@ -34,18 +34,18 @@ LazyIdleThread::LazyIdleThread(uint32_t aIdleTimeoutMS,
const nsCSubstring& aName,
ShutdownMethod aShutdownMethod,
nsIObserver* aIdleObserver)
: mMutex("LazyIdleThread::mMutex"),
mOwningThread(NS_GetCurrentThread()),
mIdleObserver(aIdleObserver),
mQueuedRunnables(nullptr),
mIdleTimeoutMS(aIdleTimeoutMS),
mPendingEventCount(0),
mIdleNotificationCount(0),
mShutdownMethod(aShutdownMethod),
mShutdown(false),
mThreadIsShuttingDown(false),
mIdleTimeoutEnabled(true),
mName(aName)
: mMutex("LazyIdleThread::mMutex")
, mOwningThread(NS_GetCurrentThread())
, mIdleObserver(aIdleObserver)
, mQueuedRunnables(nullptr)
, mIdleTimeoutMS(aIdleTimeoutMS)
, mPendingEventCount(0)
, mIdleNotificationCount(0)
, mShutdownMethod(aShutdownMethod)
, mShutdown(false)
, mThreadIsShuttingDown(false)
, mIdleTimeoutEnabled(true)
, mName(aName)
{
MOZ_ASSERT(mOwningThread, "Need owning thread!");
}
@ -147,26 +147,31 @@ LazyIdleThread::EnsureThread()
if (mShutdownMethod == AutomaticShutdown && NS_IsMainThread()) {
nsCOMPtr<nsIObserverService> obs =
do_GetService(NS_OBSERVERSERVICE_CONTRACTID, &rv);
if (NS_WARN_IF(NS_FAILED(rv)))
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = obs->AddObserver(this, "xpcom-shutdown-threads", false);
if (NS_WARN_IF(NS_FAILED(rv)))
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
}
mIdleTimer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv);
if (NS_WARN_IF(!mIdleTimer))
if (NS_WARN_IF(!mIdleTimer)) {
return NS_ERROR_UNEXPECTED;
}
nsCOMPtr<nsIRunnable> runnable =
NS_NewRunnableMethod(this, &LazyIdleThread::InitThread);
if (NS_WARN_IF(!runnable))
if (NS_WARN_IF(!runnable)) {
return NS_ERROR_UNEXPECTED;
}
rv = NS_NewThread(getter_AddRefs(mThread), runnable);
if (NS_WARN_IF(NS_FAILED(rv)))
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
}
@ -273,14 +278,16 @@ LazyIdleThread::ShutdownThread()
nsCOMPtr<nsIRunnable> runnable =
NS_NewRunnableMethod(this, &LazyIdleThread::CleanupThread);
if (NS_WARN_IF(!runnable))
if (NS_WARN_IF(!runnable)) {
return NS_ERROR_UNEXPECTED;
}
PreDispatch();
rv = mThread->Dispatch(runnable, NS_DISPATCH_NORMAL);
if (NS_WARN_IF(NS_FAILED(rv)))
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
// Put the temporary queue in place before calling Shutdown().
mQueuedRunnables = &queuedRunnables;
@ -306,8 +313,9 @@ LazyIdleThread::ShutdownThread()
if (mIdleTimer) {
rv = mIdleTimer->Cancel();
if (NS_WARN_IF(NS_FAILED(rv)))
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
mIdleTimer = nullptr;
}
@ -384,8 +392,9 @@ LazyIdleThread::Dispatch(nsIRunnable* aEvent,
ASSERT_OWNING_THREAD();
// LazyIdleThread can't always support synchronous dispatch currently.
if (NS_WARN_IF(aFlags != NS_DISPATCH_NORMAL))
if (NS_WARN_IF(aFlags != NS_DISPATCH_NORMAL)) {
return NS_ERROR_NOT_IMPLEMENTED;
}
// If our thread is shutting down then we can't actually dispatch right now.
// Queue this runnable for later.
@ -395,8 +404,9 @@ LazyIdleThread::Dispatch(nsIRunnable* aEvent,
}
nsresult rv = EnsureThread();
if (NS_WARN_IF(NS_FAILED(rv)))
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
PreDispatch();
@ -437,8 +447,9 @@ LazyIdleThread::Shutdown()
mIdleObserver = nullptr;
if (NS_WARN_IF(NS_FAILED(rv)))
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
}
@ -478,8 +489,9 @@ LazyIdleThread::Notify(nsITimer* aTimer)
}
nsresult rv = ShutdownThread();
if (NS_WARN_IF(NS_FAILED(rv)))
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
}
@ -528,12 +540,14 @@ LazyIdleThread::AfterProcessNextEvent(nsIThreadInternal* /* aThread */,
if (shouldNotifyIdle) {
nsCOMPtr<nsIRunnable> runnable =
NS_NewRunnableMethod(this, &LazyIdleThread::ScheduleTimer);
if (NS_WARN_IF(!runnable))
if (NS_WARN_IF(!runnable)) {
return NS_ERROR_UNEXPECTED;
}
nsresult rv = mOwningThread->Dispatch(runnable, NS_DISPATCH_NORMAL);
if (NS_WARN_IF(NS_FAILED(rv)))
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
}
return NS_OK;

View File

@ -32,10 +32,11 @@ namespace mozilla {
* is created on the main thread then it will automatically join its thread on
* XPCOM shutdown using the Observer Service.
*/
class LazyIdleThread MOZ_FINAL : public nsIThread,
public nsITimerCallback,
public nsIThreadObserver,
public nsIObserver
class LazyIdleThread MOZ_FINAL
: public nsIThread
, public nsITimerCallback
, public nsIThreadObserver
, public nsIObserver
{
public:
NS_DECL_THREADSAFE_ISUPPORTS
@ -45,7 +46,8 @@ public:
NS_DECL_NSITHREADOBSERVER
NS_DECL_NSIOBSERVER
enum ShutdownMethod {
enum ShutdownMethod
{
AutomaticShutdown = 0,
ManualShutdown
};
@ -128,7 +130,8 @@ private:
* Returns true if events should be queued rather than immediately dispatched
* to mThread. Currently only happens when the thread is shutting down.
*/
bool UseRunnableQueue() {
bool UseRunnableQueue()
{
return !!mQueuedRunnables;
}
@ -165,7 +168,7 @@ private:
* Temporary storage for events that happen to be dispatched while we're in
* the process of shutting down our real thread.
*/
nsTArray<nsCOMPtr<nsIRunnable> >* mQueuedRunnables;
nsTArray<nsCOMPtr<nsIRunnable>>* mQueuedRunnables;
/**
* The number of milliseconds a thread should be idle before dying.

View File

@ -30,20 +30,20 @@ namespace mozilla {
class SyncRunnable : public nsRunnable
{
public:
SyncRunnable(nsIRunnable* r)
: mRunnable(r)
SyncRunnable(nsIRunnable* aRunnable)
: mRunnable(aRunnable)
, mMonitor("SyncRunnable")
, mDone(false)
{ }
{
}
void DispatchToThread(nsIEventTarget* thread,
bool forceDispatch = false)
void DispatchToThread(nsIEventTarget* aThread, bool aForceDispatch = false)
{
nsresult rv;
bool on;
if (!forceDispatch) {
rv = thread->IsOnCurrentThread(&on);
if (!aForceDispatch) {
rv = aThread->IsOnCurrentThread(&on);
MOZ_ASSERT(NS_SUCCEEDED(rv));
if (NS_SUCCEEDED(rv) && on) {
mRunnable->Run();
@ -51,7 +51,7 @@ public:
}
}
rv = thread->Dispatch(this, NS_DISPATCH_NORMAL);
rv = aThread->Dispatch(this, NS_DISPATCH_NORMAL);
if (NS_SUCCEEDED(rv)) {
mozilla::MonitorAutoLock lock(mMonitor);
while (!mDone) {
@ -60,12 +60,12 @@ public:
}
}
static void DispatchToThread(nsIEventTarget* thread,
nsIRunnable* r,
bool forceDispatch = false)
static void DispatchToThread(nsIEventTarget* aThread,
nsIRunnable* aRunnable,
bool aForceDispatch = false)
{
nsRefPtr<SyncRunnable> s(new SyncRunnable(r));
s->DispatchToThread(thread, forceDispatch);
nsRefPtr<SyncRunnable> s(new SyncRunnable(aRunnable));
s->DispatchToThread(aThread, aForceDispatch);
}
protected:

View File

@ -184,7 +184,8 @@ ThreadStackHelper::SigAction(int aSignal, siginfo_t* aInfo, void* aContext)
#endif // XP_LINUX
bool
ThreadStackHelper::PrepareStackBuffer(Stack& aStack) {
ThreadStackHelper::PrepareStackBuffer(Stack& aStack)
{
// Return false to skip getting the stack and return an empty stack
aStack.clear();
#ifdef MOZ_ENABLE_PROFILER_SPS
@ -208,7 +209,8 @@ ThreadStackHelper::PrepareStackBuffer(Stack& aStack) {
}
void
ThreadStackHelper::FillStackBuffer() {
ThreadStackHelper::FillStackBuffer()
{
#ifdef MOZ_ENABLE_PROFILER_SPS
size_t reservedSize = mMaxStackSize;

View File

@ -50,9 +50,10 @@ namespace {
class TimerObserverRunnable : public nsRunnable
{
public:
TimerObserverRunnable(nsIObserver* observer)
: mObserver(observer)
{ }
TimerObserverRunnable(nsIObserver* aObserver)
: mObserver(aObserver)
{
}
NS_DECL_NSIRUNNABLE
@ -76,13 +77,16 @@ TimerObserverRunnable::Run()
} // anonymous namespace
nsresult TimerThread::Init()
nsresult
TimerThread::Init()
{
PR_LOG(GetTimerLog(), PR_LOG_DEBUG, ("TimerThread::Init [%d]\n", mInitialized));
PR_LOG(GetTimerLog(), PR_LOG_DEBUG,
("TimerThread::Init [%d]\n", mInitialized));
if (mInitialized) {
if (!mThread)
if (!mThread) {
return NS_ERROR_FAILURE;
}
return NS_OK;
}
@ -92,13 +96,11 @@ nsresult TimerThread::Init()
nsresult rv = NS_NewThread(getter_AddRefs(mThread), this);
if (NS_FAILED(rv)) {
mThread = nullptr;
}
else {
} else {
nsRefPtr<TimerObserverRunnable> r = new TimerObserverRunnable(this);
if (NS_IsMainThread()) {
r->Run();
}
else {
} else {
NS_DispatchToMainThread(r);
}
}
@ -108,29 +110,32 @@ nsresult TimerThread::Init()
mInitialized = true;
mMonitor.NotifyAll();
}
}
else {
} else {
MonitorAutoLock lock(mMonitor);
while (!mInitialized) {
mMonitor.Wait();
}
}
if (!mThread)
if (!mThread) {
return NS_ERROR_FAILURE;
}
return NS_OK;
}
nsresult TimerThread::Shutdown()
nsresult
TimerThread::Shutdown()
{
PR_LOG(GetTimerLog(), PR_LOG_DEBUG, ("TimerThread::Shutdown begin\n"));
if (!mThread)
if (!mThread) {
return NS_ERROR_NOT_INITIALIZED;
}
nsTArray<nsTimerImpl*> timers;
{ // lock scope
{
// lock scope
MonitorAutoLock lock(mMonitor);
mShutdown = true;
@ -153,7 +158,7 @@ nsresult TimerThread::Shutdown()
uint32_t timersCount = timers.Length();
for (uint32_t i = 0; i < timersCount; i++) {
nsTimerImpl *timer = timers[i];
nsTimerImpl* timer = timers[i];
timer->ReleaseCallback();
ReleaseTimerInternal(timer);
}
@ -169,13 +174,14 @@ nsresult TimerThread::Shutdown()
#endif
/* void Run(); */
NS_IMETHODIMP TimerThread::Run()
NS_IMETHODIMP
TimerThread::Run()
{
PR_SetCurrentThreadName("Timer");
#ifdef MOZ_NUWA_PROCESS
if (IsNuwaProcess()) {
NS_ASSERTION(NuwaMarkCurrentThread != nullptr,
NS_ASSERTION(NuwaMarkCurrentThread,
"NuwaMarkCurrentThread is undefined!");
NuwaMarkCurrentThread(nullptr, nullptr);
}
@ -187,18 +193,20 @@ NS_IMETHODIMP TimerThread::Run()
// is platform-dependent, we calculate it at runtime now.
// First we find a value such that PR_MicrosecondsToInterval(high) = 1
int32_t low = 0, high = 1;
while (PR_MicrosecondsToInterval(high) == 0)
while (PR_MicrosecondsToInterval(high) == 0) {
high <<= 1;
}
// We now have
// PR_MicrosecondsToInterval(low) = 0
// PR_MicrosecondsToInterval(high) = 1
// and we can proceed to find the critical value using binary search
while (high-low > 1) {
int32_t mid = (high+low) >> 1;
if (PR_MicrosecondsToInterval(mid) == 0)
while (high - low > 1) {
int32_t mid = (high + low) >> 1;
if (PR_MicrosecondsToInterval(mid) == 0) {
low = mid;
else
} else {
high = mid;
}
}
// Half of the amount of microseconds needed to get positive PRIntervalTime.
@ -222,7 +230,7 @@ NS_IMETHODIMP TimerThread::Run()
} else {
waitFor = PR_INTERVAL_NO_TIMEOUT;
TimeStamp now = TimeStamp::Now();
nsTimerImpl *timer = nullptr;
nsTimerImpl* timer = nullptr;
if (!mTimers.IsEmpty()) {
timer = mTimers[0];
@ -273,8 +281,9 @@ NS_IMETHODIMP TimerThread::Run()
timer = nullptr;
}
if (mShutdown)
if (mShutdown) {
break;
}
// Update now, as PostTimerEvent plus the locking may have taken a
// tick or two, and we may goto next below.
@ -294,7 +303,7 @@ NS_IMETHODIMP TimerThread::Run()
// resolution. We use halfMicrosecondsIntervalResolution, calculated
// before, to do the optimal rounding (i.e., of how to decide what
// interval is so small we should not wait at all).
double microseconds = (timeout - now).ToMilliseconds()*1000;
double microseconds = (timeout - now).ToMilliseconds() * 1000;
if (ChaosMode::isActive()) {
// The mean value of sFractions must be 1 to ensure that
@ -303,7 +312,8 @@ NS_IMETHODIMP TimerThread::Run()
static const float sFractions[] = {
0.0f, 0.25f, 0.5f, 0.75f, 1.0f, 1.75f, 2.75f
};
microseconds *= sFractions[ChaosMode::randomUint32LessThan(ArrayLength(sFractions))];
microseconds *=
sFractions[ChaosMode::randomUint32LessThan(ArrayLength(sFractions))];
forceRunNextTimer = true;
}
@ -311,9 +321,11 @@ NS_IMETHODIMP TimerThread::Run()
forceRunNextTimer = false;
goto next; // round down; execute event now
}
waitFor = PR_MicrosecondsToInterval(static_cast<uint32_t>(microseconds)); // Floor is accurate enough.
if (waitFor == 0)
waitFor = 1; // round up, wait the minimum time we can wait
waitFor = PR_MicrosecondsToInterval(
static_cast<uint32_t>(microseconds)); // Floor is accurate enough.
if (waitFor == 0) {
waitFor = 1; // round up, wait the minimum time we can wait
}
}
#ifdef DEBUG_TIMERS
@ -340,14 +352,16 @@ NS_IMETHODIMP TimerThread::Run()
return NS_OK;
}
nsresult TimerThread::AddTimer(nsTimerImpl *aTimer)
nsresult
TimerThread::AddTimer(nsTimerImpl* aTimer)
{
MonitorAutoLock lock(mMonitor);
// Add the timer to our list.
int32_t i = AddTimerInternal(aTimer);
if (i < 0)
if (i < 0) {
return NS_ERROR_OUT_OF_MEMORY;
}
// Awaken the timer thread.
if (mWaiting && i == 0) {
@ -358,7 +372,8 @@ nsresult TimerThread::AddTimer(nsTimerImpl *aTimer)
return NS_OK;
}
nsresult TimerThread::TimerDelayChanged(nsTimerImpl *aTimer)
nsresult
TimerThread::TimerDelayChanged(nsTimerImpl* aTimer)
{
MonitorAutoLock lock(mMonitor);
@ -367,8 +382,9 @@ nsresult TimerThread::TimerDelayChanged(nsTimerImpl *aTimer)
RemoveTimerInternal(aTimer);
int32_t i = AddTimerInternal(aTimer);
if (i < 0)
if (i < 0) {
return NS_ERROR_OUT_OF_MEMORY;
}
// Awaken the timer thread.
if (mWaiting && i == 0) {
@ -379,7 +395,8 @@ nsresult TimerThread::TimerDelayChanged(nsTimerImpl *aTimer)
return NS_OK;
}
nsresult TimerThread::RemoveTimer(nsTimerImpl *aTimer)
nsresult
TimerThread::RemoveTimer(nsTimerImpl* aTimer)
{
MonitorAutoLock lock(mMonitor);
@ -390,8 +407,9 @@ nsresult TimerThread::RemoveTimer(nsTimerImpl *aTimer)
// TimerThread::Run, must wait for the mMonitor auto-lock here, and during the
// wait Run drops the only remaining ref to aTimer via RemoveTimerInternal.
if (!RemoveTimerInternal(aTimer))
if (!RemoveTimerInternal(aTimer)) {
return NS_ERROR_NOT_AVAILABLE;
}
// Awaken the timer thread.
if (mWaiting) {
@ -403,18 +421,21 @@ nsresult TimerThread::RemoveTimer(nsTimerImpl *aTimer)
}
// This function must be called from within a lock
int32_t TimerThread::AddTimerInternal(nsTimerImpl *aTimer)
int32_t
TimerThread::AddTimerInternal(nsTimerImpl* aTimer)
{
if (mShutdown)
if (mShutdown) {
return -1;
}
TimeStamp now = TimeStamp::Now();
TimerAdditionComparator c(now, aTimer);
nsTimerImpl** insertSlot = mTimers.InsertElementSorted(aTimer, c);
if (!insertSlot)
if (!insertSlot) {
return -1;
}
aTimer->mArmed = true;
NS_ADDREF(aTimer);
@ -426,32 +447,37 @@ int32_t TimerThread::AddTimerInternal(nsTimerImpl *aTimer)
return insertSlot - mTimers.Elements();
}
bool TimerThread::RemoveTimerInternal(nsTimerImpl *aTimer)
bool
TimerThread::RemoveTimerInternal(nsTimerImpl* aTimer)
{
if (!mTimers.RemoveElement(aTimer))
if (!mTimers.RemoveElement(aTimer)) {
return false;
}
ReleaseTimerInternal(aTimer);
return true;
}
void TimerThread::ReleaseTimerInternal(nsTimerImpl *aTimer)
void
TimerThread::ReleaseTimerInternal(nsTimerImpl* aTimer)
{
// Order is crucial here -- see nsTimerImpl::Release.
aTimer->mArmed = false;
NS_RELEASE(aTimer);
}
void TimerThread::DoBeforeSleep()
void
TimerThread::DoBeforeSleep()
{
mSleeping = true;
}
void TimerThread::DoAfterSleep()
void
TimerThread::DoAfterSleep()
{
mSleeping = true; // wake may be notified without preceding sleep notification
for (uint32_t i = 0; i < mTimers.Length(); i ++) {
nsTimerImpl *timer = mTimers[i];
nsTimerImpl* timer = mTimers[i];
// get and set the delay to cause its timeout to be recomputed
uint32_t delay;
timer->GetDelay(&delay);
@ -464,14 +490,16 @@ void TimerThread::DoAfterSleep()
/* void observe (in nsISupports aSubject, in string aTopic, in wstring aData); */
NS_IMETHODIMP
TimerThread::Observe(nsISupports* /* aSubject */, const char *aTopic, const char16_t* /* aData */)
TimerThread::Observe(nsISupports* /* aSubject */, const char* aTopic,
const char16_t* /* aData */)
{
if (strcmp(aTopic, "sleep_notification") == 0 ||
strcmp(aTopic, "suspend_process_notification") == 0)
strcmp(aTopic, "suspend_process_notification") == 0) {
DoBeforeSleep();
else if (strcmp(aTopic, "wake_notification") == 0 ||
strcmp(aTopic, "resume_process_notification") == 0)
} else if (strcmp(aTopic, "wake_notification") == 0 ||
strcmp(aTopic, "resume_process_notification") == 0) {
DoAfterSleep();
}
return NS_OK;
}

View File

@ -23,8 +23,9 @@ namespace mozilla {
class TimeStamp;
}
class TimerThread MOZ_FINAL : public nsIRunnable,
public nsIObserver
class TimerThread MOZ_FINAL
: public nsIRunnable
, public nsIObserver
{
public:
typedef mozilla::Monitor Monitor;
@ -41,9 +42,9 @@ public:
NS_HIDDEN_(nsresult) Init();
NS_HIDDEN_(nsresult) Shutdown();
nsresult AddTimer(nsTimerImpl *aTimer);
nsresult TimerDelayChanged(nsTimerImpl *aTimer);
nsresult RemoveTimer(nsTimerImpl *aTimer);
nsresult AddTimer(nsTimerImpl* aTimer);
nsresult TimerDelayChanged(nsTimerImpl* aTimer);
nsresult RemoveTimer(nsTimerImpl* aTimer);
void DoBeforeSleep();
void DoAfterSleep();
@ -62,9 +63,9 @@ private:
// These two internal helper methods must be called while mLock is held.
// AddTimerInternal returns the position where the timer was added in the
// list, or -1 if it failed.
int32_t AddTimerInternal(nsTimerImpl *aTimer);
bool RemoveTimerInternal(nsTimerImpl *aTimer);
void ReleaseTimerInternal(nsTimerImpl *aTimer);
int32_t AddTimerInternal(nsTimerImpl* aTimer);
bool RemoveTimerInternal(nsTimerImpl* aTimer);
void ReleaseTimerInternal(nsTimerImpl* aTimer);
nsCOMPtr<nsIThread> mThread;
Monitor mMonitor;
@ -77,31 +78,35 @@ private:
nsTArray<nsTimerImpl*> mTimers;
};
struct TimerAdditionComparator {
TimerAdditionComparator(const mozilla::TimeStamp &aNow,
nsTimerImpl *aTimerToInsert) :
struct TimerAdditionComparator
{
TimerAdditionComparator(const mozilla::TimeStamp& aNow,
nsTimerImpl* aTimerToInsert) :
now(aNow)
#ifdef DEBUG
, timerToInsert(aTimerToInsert)
#endif
{}
bool LessThan(nsTimerImpl *fromArray, nsTimerImpl *newTimer) const {
NS_ABORT_IF_FALSE(newTimer == timerToInsert, "Unexpected timer ordering");
// Skip any overdue timers.
return fromArray->mTimeout <= now ||
fromArray->mTimeout <= newTimer->mTimeout;
{
}
bool Equals(nsTimerImpl* fromArray, nsTimerImpl* newTimer) const {
bool LessThan(nsTimerImpl* aFromArray, nsTimerImpl* aNewTimer) const
{
NS_ABORT_IF_FALSE(aNewTimer == timerToInsert, "Unexpected timer ordering");
// Skip any overdue timers.
return aFromArray->mTimeout <= now ||
aFromArray->mTimeout <= aNewTimer->mTimeout;
}
bool Equals(nsTimerImpl* aFromArray, nsTimerImpl* aNewTimer) const
{
return false;
}
private:
const mozilla::TimeStamp &now;
const mozilla::TimeStamp& now;
#ifdef DEBUG
const nsTimerImpl * const timerToInsert;
const nsTimerImpl* const timerToInsert;
#endif
};

View File

@ -1,4 +1,5 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* -*- 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/. */
@ -17,26 +18,25 @@ using namespace mozilla;
NS_IMPL_ISUPPORTS(nsEnvironment, nsIEnvironment)
nsresult
nsEnvironment::Create(nsISupports *aOuter, REFNSIID aIID,
void **aResult)
nsEnvironment::Create(nsISupports* aOuter, REFNSIID aIID, void** aResult)
{
nsresult rv;
*aResult = nullptr;
nsresult rv;
*aResult = nullptr;
if (aOuter != nullptr) {
return NS_ERROR_NO_AGGREGATION;
}
if (aOuter) {
return NS_ERROR_NO_AGGREGATION;
}
nsEnvironment* obj = new nsEnvironment();
if (!obj) {
return NS_ERROR_OUT_OF_MEMORY;
}
nsEnvironment* obj = new nsEnvironment();
if (!obj) {
return NS_ERROR_OUT_OF_MEMORY;
}
rv = obj->QueryInterface(aIID, aResult);
if (NS_FAILED(rv)) {
delete obj;
}
return rv;
rv = obj->QueryInterface(aIID, aResult);
if (NS_FAILED(rv)) {
delete obj;
}
return rv;
}
nsEnvironment::~nsEnvironment()
@ -44,117 +44,123 @@ nsEnvironment::~nsEnvironment()
}
NS_IMETHODIMP
nsEnvironment::Exists(const nsAString& aName, bool *aOutValue)
nsEnvironment::Exists(const nsAString& aName, bool* aOutValue)
{
nsAutoCString nativeName;
nsresult rv = NS_CopyUnicodeToNative(aName, nativeName);
if (NS_WARN_IF(NS_FAILED(rv)))
return rv;
nsAutoCString nativeName;
nsresult rv = NS_CopyUnicodeToNative(aName, nativeName);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
nsAutoCString nativeVal;
nsAutoCString nativeVal;
#if defined(XP_UNIX)
/* For Unix/Linux platforms we follow the Unix definition:
* An environment variable exists when |getenv()| returns a non-nullptr
* value. An environment variable does not exist when |getenv()| returns
* nullptr.
*/
const char *value = PR_GetEnv(nativeName.get());
*aOutValue = value && *value;
/* For Unix/Linux platforms we follow the Unix definition:
* An environment variable exists when |getenv()| returns a non-nullptr
* value. An environment variable does not exist when |getenv()| returns
* nullptr.
*/
const char* value = PR_GetEnv(nativeName.get());
*aOutValue = value && *value;
#else
/* For non-Unix/Linux platforms we have to fall back to a
* "portable" definition (which is incorrect for Unix/Linux!!!!)
* which simply checks whether the string returned by |Get()| is empty
* or not.
*/
nsAutoString value;
Get(aName, value);
*aOutValue = !value.IsEmpty();
/* For non-Unix/Linux platforms we have to fall back to a
* "portable" definition (which is incorrect for Unix/Linux!!!!)
* which simply checks whether the string returned by |Get()| is empty
* or not.
*/
nsAutoString value;
Get(aName, value);
*aOutValue = !value.IsEmpty();
#endif /* XP_UNIX */
return NS_OK;
return NS_OK;
}
NS_IMETHODIMP
nsEnvironment::Get(const nsAString& aName, nsAString& aOutValue)
{
nsAutoCString nativeName;
nsresult rv = NS_CopyUnicodeToNative(aName, nativeName);
if (NS_WARN_IF(NS_FAILED(rv)))
return rv;
nsAutoCString nativeVal;
const char *value = PR_GetEnv(nativeName.get());
if (value && *value) {
rv = NS_CopyNativeToUnicode(nsDependentCString(value), aOutValue);
} else {
aOutValue.Truncate();
rv = NS_OK;
}
nsAutoCString nativeName;
nsresult rv = NS_CopyUnicodeToNative(aName, nativeName);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
nsAutoCString nativeVal;
const char* value = PR_GetEnv(nativeName.get());
if (value && *value) {
rv = NS_CopyNativeToUnicode(nsDependentCString(value), aOutValue);
} else {
aOutValue.Truncate();
rv = NS_OK;
}
return rv;
}
/* Environment strings must have static duration; We're gonna leak all of this
* at shutdown: this is by design, caused how Unix/Linux implement environment
* vars.
* vars.
*/
typedef nsBaseHashtableET<nsCharPtrHashKey,char*> EnvEntryType;
typedef nsBaseHashtableET<nsCharPtrHashKey, char*> EnvEntryType;
typedef nsTHashtable<EnvEntryType> EnvHashType;
static EnvHashType *gEnvHash = nullptr;
static EnvHashType* gEnvHash = nullptr;
static bool
EnsureEnvHash()
{
if (gEnvHash)
return true;
gEnvHash = new EnvHashType;
if (!gEnvHash)
return false;
if (gEnvHash) {
return true;
}
gEnvHash = new EnvHashType;
if (!gEnvHash) {
return false;
}
return true;
}
NS_IMETHODIMP
nsEnvironment::Set(const nsAString& aName, const nsAString& aValue)
{
nsAutoCString nativeName;
nsAutoCString nativeVal;
nsAutoCString nativeName;
nsAutoCString nativeVal;
nsresult rv = NS_CopyUnicodeToNative(aName, nativeName);
if (NS_WARN_IF(NS_FAILED(rv)))
return rv;
nsresult rv = NS_CopyUnicodeToNative(aName, nativeName);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = NS_CopyUnicodeToNative(aValue, nativeVal);
if (NS_WARN_IF(NS_FAILED(rv)))
return rv;
rv = NS_CopyUnicodeToNative(aValue, nativeVal);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
MutexAutoLock lock(mLock);
MutexAutoLock lock(mLock);
if (!EnsureEnvHash()){
return NS_ERROR_UNEXPECTED;
}
if (!EnsureEnvHash()) {
return NS_ERROR_UNEXPECTED;
}
EnvEntryType* entry = gEnvHash->PutEntry(nativeName.get());
if (!entry) {
return NS_ERROR_OUT_OF_MEMORY;
}
char* newData = PR_smprintf("%s=%s",
nativeName.get(),
nativeVal.get());
if (!newData) {
return NS_ERROR_OUT_OF_MEMORY;
}
PR_SetEnv(newData);
if (entry->mData) {
PR_smprintf_free(entry->mData);
}
entry->mData = newData;
return NS_OK;
EnvEntryType* entry = gEnvHash->PutEntry(nativeName.get());
if (!entry) {
return NS_ERROR_OUT_OF_MEMORY;
}
char* newData = PR_smprintf("%s=%s",
nativeName.get(),
nativeVal.get());
if (!newData) {
return NS_ERROR_OUT_OF_MEMORY;
}
PR_SetEnv(newData);
if (entry->mData) {
PR_smprintf_free(entry->mData);
}
entry->mData = newData;
return NS_OK;
}

View File

@ -1,4 +1,5 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* -*- 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/. */
@ -18,17 +19,18 @@
class nsEnvironment MOZ_FINAL : public nsIEnvironment
{
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIENVIRONMENT
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIENVIRONMENT
static nsresult Create(nsISupports *aOuter, REFNSIID aIID,
void **aResult);
static nsresult Create(nsISupports* aOuter, REFNSIID aIID, void** aResult);
private:
nsEnvironment() : mLock("nsEnvironment.mLock") { }
~nsEnvironment();
nsEnvironment() : mLock("nsEnvironment.mLock")
{
}
~nsEnvironment();
mozilla::Mutex mLock;
mozilla::Mutex mLock;
};
#endif /* !nsEnvironment_h__ */

View File

@ -14,12 +14,13 @@
using namespace mozilla;
#ifdef PR_LOGGING
static PRLogModuleInfo *
static PRLogModuleInfo*
GetLog()
{
static PRLogModuleInfo *sLog;
if (!sLog)
static PRLogModuleInfo* sLog;
if (!sLog) {
sLog = PR_NewLogModule("nsEventQueue");
}
return sLog;
}
#endif
@ -41,35 +42,38 @@ nsEventQueue::~nsEventQueue()
{
// It'd be nice to be able to assert that no one else is holding the monitor,
// but NSPR doesn't really expose APIs for it.
NS_ASSERTION(IsEmpty(), "Non-empty event queue being destroyed; events being leaked.");
NS_ASSERTION(IsEmpty(),
"Non-empty event queue being destroyed; events being leaked.");
if (mHead)
if (mHead) {
FreePage(mHead);
}
}
bool
nsEventQueue::GetEvent(bool mayWait, nsIRunnable **result)
nsEventQueue::GetEvent(bool aMayWait, nsIRunnable** aResult)
{
{
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
while (IsEmpty()) {
if (!mayWait) {
if (result)
*result = nullptr;
if (!aMayWait) {
if (aResult) {
*aResult = nullptr;
}
return false;
}
LOG(("EVENTQ(%p): wait begin\n", this));
LOG(("EVENTQ(%p): wait begin\n", this));
mon.Wait();
LOG(("EVENTQ(%p): wait end\n", this));
LOG(("EVENTQ(%p): wait end\n", this));
}
if (result) {
*result = mHead->mEvents[mOffsetHead++];
if (aResult) {
*aResult = mHead->mEvents[mOffsetHead++];
// Check if mHead points to empty Page
if (mOffsetHead == EVENTS_PER_PAGE) {
Page *dead = mHead;
Page* dead = mHead;
mHead = mHead->mNext;
FreePage(dead);
mOffsetHead = 0;

View File

@ -23,7 +23,7 @@ public:
// This method adds a new event to the pending event queue. The queue holds
// a strong reference to the event after this method returns. This method
// cannot fail.
void PutEvent(nsIRunnable *event);
void PutEvent(nsIRunnable* aEvent);
// This method gets an event from the event queue. If mayWait is true, then
// the method will block the calling thread until an event is available. If
@ -31,53 +31,63 @@ public:
// or not an event is pending. When the resulting event is non-null, the
// caller is responsible for releasing the event object. This method does
// not alter the reference count of the resulting event.
bool GetEvent(bool mayWait, nsIRunnable **event);
bool GetEvent(bool aMayWait, nsIRunnable** aEvent);
// This method returns true if there is a pending event.
bool HasPendingEvent() {
bool HasPendingEvent()
{
return GetEvent(false, nullptr);
}
// This method returns the next pending event or null.
bool GetPendingEvent(nsIRunnable **runnable) {
bool GetPendingEvent(nsIRunnable** runnable)
{
return GetEvent(false, runnable);
}
// Expose the event queue's monitor for "power users"
ReentrantMonitor& GetReentrantMonitor() {
ReentrantMonitor& GetReentrantMonitor()
{
return mReentrantMonitor;
}
private:
bool IsEmpty() {
bool IsEmpty()
{
return !mHead || (mHead == mTail && mOffsetHead == mOffsetTail);
}
enum { EVENTS_PER_PAGE = 255 };
enum
{
EVENTS_PER_PAGE = 255
};
// Page objects are linked together to form a simple deque.
struct Page {
struct Page *mNext;
nsIRunnable *mEvents[EVENTS_PER_PAGE];
struct Page
{
struct Page* mNext;
nsIRunnable* mEvents[EVENTS_PER_PAGE];
};
static_assert((sizeof(Page) & (sizeof(Page) - 1)) == 0,
"sizeof(Page) should be a power of two to avoid heap slop.");
static Page *NewPage() {
return static_cast<Page *>(moz_xcalloc(1, sizeof(Page)));
static Page* NewPage()
{
return static_cast<Page*>(moz_xcalloc(1, sizeof(Page)));
}
static void FreePage(Page *p) {
free(p);
static void FreePage(Page* aPage)
{
free(aPage);
}
ReentrantMonitor mReentrantMonitor;
Page *mHead;
Page *mTail;
Page* mHead;
Page* mTail;
uint16_t mOffsetHead; // offset into mHead where next item is removed
uint16_t mOffsetTail; // offset into mTail where next item is added

View File

@ -24,14 +24,14 @@ NS_GetPendingMemoryPressure()
}
void
NS_DispatchEventualMemoryPressure(MemoryPressureState state)
NS_DispatchEventualMemoryPressure(MemoryPressureState aState)
{
/*
* A new memory pressure event erases an ongoing memory pressure, but an
* existing "new" memory pressure event takes precedence over a new "ongoing"
* memory pressure event.
*/
switch (state) {
switch (aState) {
case MemPressure_None:
sMemoryPressurePending = MemPressure_None;
break;
@ -39,15 +39,16 @@ NS_DispatchEventualMemoryPressure(MemoryPressureState state)
sMemoryPressurePending = MemPressure_New;
break;
case MemPressure_Ongoing:
sMemoryPressurePending.compareExchange(MemPressure_None, MemPressure_Ongoing);
sMemoryPressurePending.compareExchange(MemPressure_None,
MemPressure_Ongoing);
break;
}
}
nsresult
NS_DispatchMemoryPressure(MemoryPressureState state)
NS_DispatchMemoryPressure(MemoryPressureState aState)
{
NS_DispatchEventualMemoryPressure(state);
NS_DispatchEventualMemoryPressure(aState);
nsCOMPtr<nsIRunnable> event = new nsRunnable;
return NS_DispatchToMainThread(event);
}

View File

@ -9,7 +9,8 @@
#include "nscore.h"
enum MemoryPressureState {
enum MemoryPressureState
{
/*
* No memory pressure.
*/
@ -59,7 +60,7 @@ NS_GetPendingMemoryPressure();
* You may call this function from any thread.
*/
void
NS_DispatchEventualMemoryPressure(MemoryPressureState state);
NS_DispatchEventualMemoryPressure(MemoryPressureState aState);
/**
* This function causes the main thread to fire a memory pressure event
@ -71,6 +72,6 @@ NS_DispatchEventualMemoryPressure(MemoryPressureState state);
* You may call this function from any thread.
*/
nsresult
NS_DispatchMemoryPressure(MemoryPressureState state);
NS_DispatchMemoryPressure(MemoryPressureState aState);
#endif // nsMemoryPressure_h__

View File

@ -31,8 +31,9 @@
{0x7b4eeb20, 0xd781, 0x11d4, \
{0x8A, 0x83, 0x00, 0x10, 0xa4, 0xe0, 0xc9, 0xca}}
class nsProcess MOZ_FINAL : public nsIProcess,
public nsIObserver
class nsProcess MOZ_FINAL
: public nsIProcess
, public nsIObserver
{
public:
@ -44,17 +45,17 @@ public:
private:
~nsProcess();
static void Monitor(void *arg);
static void Monitor(void* aArg);
void ProcessComplete();
nsresult CopyArgsAndRunProcess(bool blocking, const char** args,
uint32_t count, nsIObserver* observer,
bool holdWeak);
nsresult CopyArgsAndRunProcessw(bool blocking, const char16_t** args,
uint32_t count, nsIObserver* observer,
bool holdWeak);
nsresult CopyArgsAndRunProcess(bool aBlocking, const char** aArgs,
uint32_t aCount, nsIObserver* aObserver,
bool aHoldWeak);
nsresult CopyArgsAndRunProcessw(bool aBlocking, const char16_t** aArgs,
uint32_t aCount, nsIObserver* aObserver,
bool aHoldWeak);
// The 'args' array is null-terminated.
nsresult RunProcess(bool blocking, char **args, nsIObserver* observer,
bool holdWeak, bool argsUTF8);
nsresult RunProcess(bool aBlocking, char** aArgs, nsIObserver* aObserver,
bool aHoldWeak, bool aArgsUTF8);
PRThread* mThread;
mozilla::Mutex mLock;
@ -73,7 +74,7 @@ private:
#if defined(PROCESSMODEL_WINAPI)
HANDLE mProcess;
#elif !defined(XP_MACOSX)
PRProcess *mProcess;
PRProcess* mProcess;
#endif
};

File diff suppressed because it is too large Load Diff

View File

@ -67,12 +67,13 @@ using namespace mozilla::tasktracer;
using namespace mozilla;
#ifdef PR_LOGGING
static PRLogModuleInfo *
static PRLogModuleInfo*
GetThreadLog()
{
static PRLogModuleInfo *sLog;
if (!sLog)
static PRLogModuleInfo* sLog;
if (!sLog) {
sLog = PR_NewLogModule("nsThread");
}
return sLog;
}
#endif
@ -89,68 +90,79 @@ nsIThreadObserver* nsThread::sMainThreadObserver = nullptr;
// Because we do not have our own nsIFactory, we have to implement nsIClassInfo
// somewhat manually.
class nsThreadClassInfo : public nsIClassInfo {
class nsThreadClassInfo : public nsIClassInfo
{
public:
NS_DECL_ISUPPORTS_INHERITED // no mRefCnt
NS_DECL_NSICLASSINFO
nsThreadClassInfo() {}
nsThreadClassInfo()
{
}
};
NS_IMETHODIMP_(MozExternalRefCountType) nsThreadClassInfo::AddRef() { return 2; }
NS_IMETHODIMP_(MozExternalRefCountType) nsThreadClassInfo::Release() { return 1; }
NS_IMETHODIMP_(MozExternalRefCountType)
nsThreadClassInfo::AddRef()
{
return 2;
}
NS_IMETHODIMP_(MozExternalRefCountType)
nsThreadClassInfo::Release()
{
return 1;
}
NS_IMPL_QUERY_INTERFACE(nsThreadClassInfo, nsIClassInfo)
NS_IMETHODIMP
nsThreadClassInfo::GetInterfaces(uint32_t *count, nsIID ***array)
nsThreadClassInfo::GetInterfaces(uint32_t* aCount, nsIID*** aArray)
{
return NS_CI_INTERFACE_GETTER_NAME(nsThread)(count, array);
return NS_CI_INTERFACE_GETTER_NAME(nsThread)(aCount, aArray);
}
NS_IMETHODIMP
nsThreadClassInfo::GetHelperForLanguage(uint32_t lang, nsISupports **result)
nsThreadClassInfo::GetHelperForLanguage(uint32_t aLang, nsISupports** aResult)
{
*result = nullptr;
*aResult = nullptr;
return NS_OK;
}
NS_IMETHODIMP
nsThreadClassInfo::GetContractID(char **result)
nsThreadClassInfo::GetContractID(char** aResult)
{
*result = nullptr;
*aResult = nullptr;
return NS_OK;
}
NS_IMETHODIMP
nsThreadClassInfo::GetClassDescription(char **result)
nsThreadClassInfo::GetClassDescription(char** aResult)
{
*result = nullptr;
*aResult = nullptr;
return NS_OK;
}
NS_IMETHODIMP
nsThreadClassInfo::GetClassID(nsCID **result)
nsThreadClassInfo::GetClassID(nsCID** aResult)
{
*result = nullptr;
*aResult = nullptr;
return NS_OK;
}
NS_IMETHODIMP
nsThreadClassInfo::GetImplementationLanguage(uint32_t *result)
nsThreadClassInfo::GetImplementationLanguage(uint32_t* aResult)
{
*result = nsIProgrammingLanguage::CPLUSPLUS;
*aResult = nsIProgrammingLanguage::CPLUSPLUS;
return NS_OK;
}
NS_IMETHODIMP
nsThreadClassInfo::GetFlags(uint32_t *result)
nsThreadClassInfo::GetFlags(uint32_t* aResult)
{
*result = THREADSAFE;
*aResult = THREADSAFE;
return NS_OK;
}
NS_IMETHODIMP
nsThreadClassInfo::GetClassIDNoAlloc(nsCID *result)
nsThreadClassInfo::GetClassIDNoAlloc(nsCID* aResult)
{
return NS_ERROR_NOT_AVAILABLE;
}
@ -175,30 +187,39 @@ NS_IMPL_CI_INTERFACE_GETTER(nsThread, nsIThread, nsIThreadInternal,
//-----------------------------------------------------------------------------
class nsThreadStartupEvent : public nsRunnable {
class nsThreadStartupEvent : public nsRunnable
{
public:
nsThreadStartupEvent()
: mMon("nsThreadStartupEvent.mMon")
, mInitialized(false) {
, mInitialized(false)
{
}
// This method does not return until the thread startup object is in the
// completion state.
void Wait() {
if (mInitialized) // Maybe avoid locking...
void Wait()
{
if (mInitialized) {
// Maybe avoid locking...
return;
}
ReentrantMonitorAutoEnter mon(mMon);
while (!mInitialized)
while (!mInitialized) {
mon.Wait();
}
}
// This method needs to be public to support older compilers (xlC_r on AIX).
// It should be called directly as this class type is reference counted.
virtual ~nsThreadStartupEvent() {
virtual ~nsThreadStartupEvent()
{
}
private:
NS_IMETHOD Run() {
NS_IMETHOD Run()
{
ReentrantMonitorAutoEnter mon(mMon);
mInitialized = true;
mon.Notify();
@ -206,45 +227,53 @@ private:
}
ReentrantMonitor mMon;
bool mInitialized;
bool mInitialized;
};
//-----------------------------------------------------------------------------
struct nsThreadShutdownContext {
nsThread *joiningThread;
struct nsThreadShutdownContext
{
nsThread* joiningThread;
bool shutdownAck;
};
// This event is responsible for notifying nsThread::Shutdown that it is time
// to call PR_JoinThread.
class nsThreadShutdownAckEvent : public nsRunnable {
class nsThreadShutdownAckEvent : public nsRunnable
{
public:
nsThreadShutdownAckEvent(nsThreadShutdownContext *ctx)
: mShutdownContext(ctx) {
nsThreadShutdownAckEvent(nsThreadShutdownContext* aCtx)
: mShutdownContext(aCtx)
{
}
NS_IMETHOD Run() {
NS_IMETHOD Run()
{
mShutdownContext->shutdownAck = true;
return NS_OK;
}
private:
nsThreadShutdownContext *mShutdownContext;
nsThreadShutdownContext* mShutdownContext;
};
// This event is responsible for setting mShutdownContext
class nsThreadShutdownEvent : public nsRunnable {
class nsThreadShutdownEvent : public nsRunnable
{
public:
nsThreadShutdownEvent(nsThread *thr, nsThreadShutdownContext *ctx)
: mThread(thr), mShutdownContext(ctx) {
}
NS_IMETHOD Run() {
nsThreadShutdownEvent(nsThread* aThr, nsThreadShutdownContext* aCtx)
: mThread(aThr)
, mShutdownContext(aCtx)
{
}
NS_IMETHOD Run()
{
mThread->mShutdownContext = mShutdownContext;
MessageLoop::current()->Quit();
return NS_OK;
}
private:
nsRefPtr<nsThread> mThread;
nsThreadShutdownContext *mShutdownContext;
nsThreadShutdownContext* mShutdownContext;
};
//-----------------------------------------------------------------------------
@ -272,8 +301,8 @@ SetupCurrentThreadForChaosMode()
setpriority(PRIO_PROCESS, 0, ChaosMode::randomUint32LessThan(4));
#else
// We should set the affinity here but NSPR doesn't provide a way to expose it.
PR_SetThreadPriority(PR_GetCurrentThread(),
PRThreadPriority(ChaosMode::randomUint32LessThan(PR_PRIORITY_LAST + 1)));
uint32_t priority = ChaosMode::randomUint32LessThan(PR_PRIORITY_LAST + 1);
PR_SetThreadPriority(PR_GetCurrentThread(), PRThreadPriority(priority));
#endif
#ifdef HAVE_SCHED_SETAFFINITY
@ -288,9 +317,9 @@ SetupCurrentThreadForChaosMode()
}
/*static*/ void
nsThread::ThreadFunc(void *arg)
nsThread::ThreadFunc(void* aArg)
{
nsThread *self = static_cast<nsThread *>(arg); // strong reference
nsThread* self = static_cast<nsThread*>(aArg); // strong reference
self->mThread = PR_GetCurrentThread();
SetupCurrentThreadForChaosMode();
@ -308,7 +337,8 @@ nsThread::ThreadFunc(void *arg)
event->Run(); // unblocks nsThread::Init
event = nullptr;
{ // Scope for MessageLoop.
{
// Scope for MessageLoop.
nsAutoPtr<MessageLoop> loop(
new MessageLoop(MessageLoop::TYPE_MOZILLA_NONMAINTHREAD));
@ -384,13 +414,13 @@ nsThread::Init()
{
// spawn thread and wait until it is fully setup
nsRefPtr<nsThreadStartupEvent> startup = new nsThreadStartupEvent();
NS_ADDREF_THIS();
mShutdownRequired = true;
// ThreadFunc is responsible for setting mThread
PRThread *thr = PR_CreateThread(PR_USER_THREAD, ThreadFunc, this,
PRThread* thr = PR_CreateThread(PR_USER_THREAD, ThreadFunc, this,
PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
PR_JOINABLE_THREAD, mStackSize);
if (!thr) {
@ -423,84 +453,90 @@ nsThread::InitCurrentThread()
}
nsresult
nsThread::PutEvent(nsIRunnable *event, nsNestedEventTarget *target)
nsThread::PutEvent(nsIRunnable* aEvent, nsNestedEventTarget* aTarget)
{
{
MutexAutoLock lock(mLock);
nsChainedEventQueue *queue = target ? target->mQueue : &mEventsRoot;
nsChainedEventQueue* queue = aTarget ? aTarget->mQueue : &mEventsRoot;
if (!queue || (queue == &mEventsRoot && mEventsAreDoomed)) {
NS_WARNING("An event was posted to a thread that will never run it (rejected)");
return NS_ERROR_UNEXPECTED;
}
queue->PutEvent(event);
queue->PutEvent(aEvent);
}
nsCOMPtr<nsIThreadObserver> obs = GetObserver();
if (obs)
if (obs) {
obs->OnDispatchedEvent(this);
}
return NS_OK;
}
nsresult
nsThread::DispatchInternal(nsIRunnable *event, uint32_t flags,
nsNestedEventTarget *target)
nsThread::DispatchInternal(nsIRunnable* aEvent, uint32_t aFlags,
nsNestedEventTarget* aTarget)
{
if (NS_WARN_IF(!event))
if (NS_WARN_IF(!aEvent)) {
return NS_ERROR_INVALID_ARG;
}
if (gXPCOMThreadsShutDown && MAIN_THREAD != mIsMainThread && !target) {
if (gXPCOMThreadsShutDown && MAIN_THREAD != mIsMainThread && !aTarget) {
return NS_ERROR_ILLEGAL_DURING_SHUTDOWN;
}
#ifdef MOZ_TASK_TRACER
nsRefPtr<nsIRunnable> tracedRunnable = CreateTracedRunnable(event);
event = tracedRunnable;
nsRefPtr<nsIRunnable> tracedRunnable = CreateTracedRunnable(aEvent);
aEvent = tracedRunnable;
#endif
if (flags & DISPATCH_SYNC) {
nsThread *thread = nsThreadManager::get()->GetCurrentThread();
if (NS_WARN_IF(!thread))
if (aFlags & DISPATCH_SYNC) {
nsThread* thread = nsThreadManager::get()->GetCurrentThread();
if (NS_WARN_IF(!thread)) {
return NS_ERROR_NOT_AVAILABLE;
}
// XXX we should be able to do something better here... we should
// be able to monitor the slot occupied by this event and use
// that to tell us when the event has been processed.
nsRefPtr<nsThreadSyncDispatch> wrapper =
new nsThreadSyncDispatch(thread, event);
if (!wrapper)
new nsThreadSyncDispatch(thread, aEvent);
if (!wrapper) {
return NS_ERROR_OUT_OF_MEMORY;
nsresult rv = PutEvent(wrapper, target);
}
nsresult rv = PutEvent(wrapper, aTarget);
// Don't wait for the event to finish if we didn't dispatch it...
if (NS_FAILED(rv))
if (NS_FAILED(rv)) {
return rv;
}
// Allows waiting; ensure no locks are held that would deadlock us!
while (wrapper->IsPending())
while (wrapper->IsPending()) {
NS_ProcessNextEvent(thread, true);
}
return wrapper->Result();
}
NS_ASSERTION(flags == NS_DISPATCH_NORMAL, "unexpected dispatch flags");
return PutEvent(event, target);
NS_ASSERTION(aFlags == NS_DISPATCH_NORMAL, "unexpected dispatch flags");
return PutEvent(aEvent, aTarget);
}
//-----------------------------------------------------------------------------
// nsIEventTarget
NS_IMETHODIMP
nsThread::Dispatch(nsIRunnable *event, uint32_t flags)
nsThread::Dispatch(nsIRunnable* aEvent, uint32_t aFlags)
{
LOG(("THRD(%p) Dispatch [%p %x]\n", this, event, flags));
LOG(("THRD(%p) Dispatch [%p %x]\n", this, aEvent, aFlags));
return DispatchInternal(event, flags, nullptr);
return DispatchInternal(aEvent, aFlags, nullptr);
}
NS_IMETHODIMP
nsThread::IsOnCurrentThread(bool *result)
nsThread::IsOnCurrentThread(bool* aResult)
{
*result = (PR_GetCurrentThread() == mThread);
*aResult = (PR_GetCurrentThread() == mThread);
return NS_OK;
}
@ -508,9 +544,9 @@ nsThread::IsOnCurrentThread(bool *result)
// nsIThread
NS_IMETHODIMP
nsThread::GetPRThread(PRThread **result)
nsThread::GetPRThread(PRThread** aResult)
{
*result = mThread;
*aResult = mThread;
return NS_OK;
}
@ -522,17 +558,20 @@ nsThread::Shutdown()
// XXX If we make this warn, then we hit that warning at xpcom shutdown while
// shutting down a thread in a thread pool. That happens b/c the thread
// in the thread pool is already shutdown by the thread manager.
if (!mThread)
if (!mThread) {
return NS_OK;
}
if (NS_WARN_IF(mThread == PR_GetCurrentThread()))
if (NS_WARN_IF(mThread == PR_GetCurrentThread())) {
return NS_ERROR_UNEXPECTED;
}
// Prevent multiple calls to this method
{
MutexAutoLock lock(mLock);
if (!mShutdownRequired)
if (!mShutdownRequired) {
return NS_ERROR_UNEXPECTED;
}
mShutdownRequired = false;
}
@ -543,19 +582,21 @@ nsThread::Shutdown()
// Set mShutdownContext and wake up the thread in case it is waiting for
// events to process.
nsCOMPtr<nsIRunnable> event = new nsThreadShutdownEvent(this, &context);
if (!event)
if (!event) {
return NS_ERROR_OUT_OF_MEMORY;
}
// XXXroc What if posting the event fails due to OOM?
PutEvent(event, nullptr);
// We could still end up with other events being added after the shutdown
// task, but that's okay because we process pending events in ThreadFunc
// after setting mShutdownContext just before exiting.
// Process events on the current thread until we receive a shutdown ACK.
// Allows waiting; ensure no locks are held that would deadlock us!
while (!context.shutdownAck)
while (!context.shutdownAck) {
NS_ProcessNextEvent(context.joiningThread, true);
}
// Now, it should be safe to join without fear of dead-locking.
@ -578,44 +619,50 @@ nsThread::Shutdown()
}
NS_IMETHODIMP
nsThread::HasPendingEvents(bool *result)
nsThread::HasPendingEvents(bool* aResult)
{
if (NS_WARN_IF(PR_GetCurrentThread() != mThread))
if (NS_WARN_IF(PR_GetCurrentThread() != mThread)) {
return NS_ERROR_NOT_SAME_THREAD;
}
*result = mEvents->GetEvent(false, nullptr);
*aResult = mEvents->GetEvent(false, nullptr);
return NS_OK;
}
#ifdef MOZ_CANARY
void canary_alarm_handler (int signum);
void canary_alarm_handler(int signum);
class Canary {
//XXX ToDo: support nested loops
class Canary
{
//XXX ToDo: support nested loops
public:
Canary() {
Canary()
{
if (sCanaryOutputFD > 0 && EventLatencyIsImportant()) {
signal(SIGALRM, canary_alarm_handler);
ualarm(15000, 0);
ualarm(15000, 0);
}
}
~Canary() {
if (sCanaryOutputFD != 0 && EventLatencyIsImportant())
~Canary()
{
if (sCanaryOutputFD != 0 && EventLatencyIsImportant()) {
ualarm(0, 0);
}
}
static bool EventLatencyIsImportant() {
static bool EventLatencyIsImportant()
{
return NS_IsMainThread() && XRE_GetProcessType() == GeckoProcessType_Default;
}
};
void canary_alarm_handler (int signum)
void canary_alarm_handler(int signum)
{
void *array[30];
void* array[30];
const char msg[29] = "event took too long to run:\n";
// use write to be safe in the signal handler
write(sCanaryOutputFD, msg, sizeof(msg));
write(sCanaryOutputFD, msg, sizeof(msg));
backtrace_symbols_fd(array, backtrace(array, 30), sCanaryOutputFD);
}
@ -635,12 +682,13 @@ void canary_alarm_handler (int signum)
PR_END_MACRO
NS_IMETHODIMP
nsThread::ProcessNextEvent(bool mayWait, bool *result)
nsThread::ProcessNextEvent(bool aMayWait, bool* aResult)
{
LOG(("THRD(%p) ProcessNextEvent [%u %u]\n", this, mayWait, mRunningEvent));
LOG(("THRD(%p) ProcessNextEvent [%u %u]\n", this, aMayWait, mRunningEvent));
if (NS_WARN_IF(PR_GetCurrentThread() != mThread))
if (NS_WARN_IF(PR_GetCurrentThread() != mThread)) {
return NS_ERROR_NOT_SAME_THREAD;
}
// The toplevel event loop normally blocks waiting for the next event, but
// if we're trying to shut this thread down, we must exit the event loop when
@ -650,10 +698,11 @@ nsThread::ProcessNextEvent(bool mayWait, bool *result)
// to block even if something has requested shutdown of the thread. Otherwise
// we'll just busywait as we endlessly look for an event, fail to find one,
// and repeat the nested event loop since its state change hasn't happened yet.
bool reallyWait = mayWait && (mRunningEvent > 0 || !ShuttingDown());
bool reallyWait = aMayWait && (mRunningEvent > 0 || !ShuttingDown());
if (MAIN_THREAD == mIsMainThread && reallyWait)
if (MAIN_THREAD == mIsMainThread && reallyWait) {
HangMonitor::Suspend();
}
// Fire a memory pressure notification, if we're the main thread and one is
// pending.
@ -670,7 +719,7 @@ nsThread::ProcessNextEvent(bool mayWait, bool *result)
if (os) {
os->NotifyObservers(nullptr, "memory-pressure",
mpPending == MemPressure_New ? lowMem.get() :
lowMemOngoing.get());
lowMemOngoing.get());
} else {
NS_WARNING("Can't get observer service!");
}
@ -679,12 +728,14 @@ nsThread::ProcessNextEvent(bool mayWait, bool *result)
bool notifyMainThreadObserver =
(MAIN_THREAD == mIsMainThread) && sMainThreadObserver;
if (notifyMainThreadObserver)
sMainThreadObserver->OnProcessNextEvent(this, reallyWait, mRunningEvent);
if (notifyMainThreadObserver) {
sMainThreadObserver->OnProcessNextEvent(this, reallyWait, mRunningEvent);
}
nsCOMPtr<nsIThreadObserver> obs = mObserver;
if (obs)
if (obs) {
obs->OnProcessNextEvent(this, reallyWait, mRunningEvent);
}
NOTIFY_EVENT_OBSERVERS(OnProcessNextEvent,
(this, reallyWait, mRunningEvent));
@ -705,14 +756,15 @@ nsThread::ProcessNextEvent(bool mayWait, bool *result)
nsCOMPtr<nsIRunnable> event;
mEvents->GetEvent(reallyWait, getter_AddRefs(event));
*result = (event.get() != nullptr);
*aResult = (event.get() != nullptr);
if (event) {
LOG(("THRD(%p) running [%p]\n", this, event.get()));
if (MAIN_THREAD == mIsMainThread)
if (MAIN_THREAD == mIsMainThread) {
HangMonitor::NotifyActivity();
}
event->Run();
} else if (mayWait) {
} else if (aMayWait) {
MOZ_ASSERT(ShuttingDown(),
"This should only happen when shutting down");
rv = NS_ERROR_UNEXPECTED;
@ -722,13 +774,15 @@ nsThread::ProcessNextEvent(bool mayWait, bool *result)
--mRunningEvent;
NOTIFY_EVENT_OBSERVERS(AfterProcessNextEvent,
(this, mRunningEvent, *result));
(this, mRunningEvent, *aResult));
if (obs)
obs->AfterProcessNextEvent(this, mRunningEvent, *result);
if (obs) {
obs->AfterProcessNextEvent(this, mRunningEvent, *aResult);
}
if (notifyMainThreadObserver && sMainThreadObserver)
sMainThreadObserver->AfterProcessNextEvent(this, mRunningEvent, *result);
if (notifyMainThreadObserver && sMainThreadObserver) {
sMainThreadObserver->AfterProcessNextEvent(this, mRunningEvent, *aResult);
}
return rv;
}
@ -737,17 +791,18 @@ nsThread::ProcessNextEvent(bool mayWait, bool *result)
// nsISupportsPriority
NS_IMETHODIMP
nsThread::GetPriority(int32_t *priority)
nsThread::GetPriority(int32_t* aPriority)
{
*priority = mPriority;
*aPriority = mPriority;
return NS_OK;
}
NS_IMETHODIMP
nsThread::SetPriority(int32_t priority)
nsThread::SetPriority(int32_t aPriority)
{
if (NS_WARN_IF(!mThread))
if (NS_WARN_IF(!mThread)) {
return NS_ERROR_NOT_INITIALIZED;
}
// NSPR defines the following four thread priorities:
// PR_PRIORITY_LOW
@ -756,7 +811,7 @@ nsThread::SetPriority(int32_t priority)
// PR_PRIORITY_URGENT
// We map the priority values defined on nsISupportsPriority to these values.
mPriority = priority;
mPriority = aPriority;
PRThreadPriority pri;
if (mPriority <= PRIORITY_HIGHEST) {
@ -777,55 +832,59 @@ nsThread::SetPriority(int32_t priority)
}
NS_IMETHODIMP
nsThread::AdjustPriority(int32_t delta)
nsThread::AdjustPriority(int32_t aDelta)
{
return SetPriority(mPriority + delta);
return SetPriority(mPriority + aDelta);
}
//-----------------------------------------------------------------------------
// nsIThreadInternal
NS_IMETHODIMP
nsThread::GetObserver(nsIThreadObserver **obs)
nsThread::GetObserver(nsIThreadObserver** aObs)
{
MutexAutoLock lock(mLock);
NS_IF_ADDREF(*obs = mObserver);
NS_IF_ADDREF(*aObs = mObserver);
return NS_OK;
}
NS_IMETHODIMP
nsThread::SetObserver(nsIThreadObserver *obs)
nsThread::SetObserver(nsIThreadObserver* aObs)
{
if (NS_WARN_IF(PR_GetCurrentThread() != mThread))
if (NS_WARN_IF(PR_GetCurrentThread() != mThread)) {
return NS_ERROR_NOT_SAME_THREAD;
}
MutexAutoLock lock(mLock);
mObserver = obs;
mObserver = aObs;
return NS_OK;
}
NS_IMETHODIMP
nsThread::GetRecursionDepth(uint32_t *depth)
nsThread::GetRecursionDepth(uint32_t* aDepth)
{
if (NS_WARN_IF(PR_GetCurrentThread() != mThread))
if (NS_WARN_IF(PR_GetCurrentThread() != mThread)) {
return NS_ERROR_NOT_SAME_THREAD;
}
*depth = mRunningEvent;
*aDepth = mRunningEvent;
return NS_OK;
}
NS_IMETHODIMP
nsThread::AddObserver(nsIThreadObserver *observer)
nsThread::AddObserver(nsIThreadObserver* aObserver)
{
if (NS_WARN_IF(!observer))
if (NS_WARN_IF(!aObserver)) {
return NS_ERROR_INVALID_ARG;
if (NS_WARN_IF(PR_GetCurrentThread() != mThread))
}
if (NS_WARN_IF(PR_GetCurrentThread() != mThread)) {
return NS_ERROR_NOT_SAME_THREAD;
}
NS_WARN_IF_FALSE(!mEventObservers.Contains(observer),
NS_WARN_IF_FALSE(!mEventObservers.Contains(aObserver),
"Adding an observer twice!");
if (!mEventObservers.AppendElement(observer)) {
if (!mEventObservers.AppendElement(aObserver)) {
NS_WARNING("Out of memory!");
return NS_ERROR_OUT_OF_MEMORY;
}
@ -834,12 +893,13 @@ nsThread::AddObserver(nsIThreadObserver *observer)
}
NS_IMETHODIMP
nsThread::RemoveObserver(nsIThreadObserver *observer)
nsThread::RemoveObserver(nsIThreadObserver* aObserver)
{
if (NS_WARN_IF(PR_GetCurrentThread() != mThread))
if (NS_WARN_IF(PR_GetCurrentThread() != mThread)) {
return NS_ERROR_NOT_SAME_THREAD;
}
if (observer && !mEventObservers.RemoveElement(observer)) {
if (aObserver && !mEventObservers.RemoveElement(aObserver)) {
NS_WARNING("Removing an observer that was never added!");
}
@ -847,12 +907,13 @@ nsThread::RemoveObserver(nsIThreadObserver *observer)
}
NS_IMETHODIMP
nsThread::PushEventQueue(nsIEventTarget **result)
nsThread::PushEventQueue(nsIEventTarget** aResult)
{
if (NS_WARN_IF(PR_GetCurrentThread() != mThread))
if (NS_WARN_IF(PR_GetCurrentThread() != mThread)) {
return NS_ERROR_NOT_SAME_THREAD;
}
nsChainedEventQueue *queue = new nsChainedEventQueue();
nsChainedEventQueue* queue = new nsChainedEventQueue();
queue->mEventTarget = new nsNestedEventTarget(this, queue);
{
@ -861,18 +922,20 @@ nsThread::PushEventQueue(nsIEventTarget **result)
mEvents = queue;
}
NS_ADDREF(*result = queue->mEventTarget);
NS_ADDREF(*aResult = queue->mEventTarget);
return NS_OK;
}
NS_IMETHODIMP
nsThread::PopEventQueue(nsIEventTarget *innermostTarget)
nsThread::PopEventQueue(nsIEventTarget* aInnermostTarget)
{
if (NS_WARN_IF(PR_GetCurrentThread() != mThread))
if (NS_WARN_IF(PR_GetCurrentThread() != mThread)) {
return NS_ERROR_NOT_SAME_THREAD;
}
if (NS_WARN_IF(!innermostTarget))
if (NS_WARN_IF(!aInnermostTarget)) {
return NS_ERROR_NULL_POINTER;
}
// Don't delete or release anything while holding the lock.
nsAutoPtr<nsChainedEventQueue> queue;
@ -882,8 +945,9 @@ nsThread::PopEventQueue(nsIEventTarget *innermostTarget)
MutexAutoLock lock(mLock);
// Make sure we're popping the innermost event target.
if (NS_WARN_IF(mEvents->mEventTarget != innermostTarget))
if (NS_WARN_IF(mEvents->mEventTarget != aInnermostTarget)) {
return NS_ERROR_UNEXPECTED;
}
MOZ_ASSERT(mEvents != &mEventsRoot);
@ -891,8 +955,9 @@ nsThread::PopEventQueue(nsIEventTarget *innermostTarget)
mEvents = mEvents->mNext;
nsCOMPtr<nsIRunnable> event;
while (queue->GetEvent(false, getter_AddRefs(event)))
while (queue->GetEvent(false, getter_AddRefs(event))) {
mEvents->PutEvent(event);
}
// Don't let the event target post any more events.
queue->mEventTarget.swap(target);
@ -936,16 +1001,16 @@ nsThreadSyncDispatch::Run()
NS_IMPL_ISUPPORTS(nsThread::nsNestedEventTarget, nsIEventTarget)
NS_IMETHODIMP
nsThread::nsNestedEventTarget::Dispatch(nsIRunnable *event, uint32_t flags)
nsThread::nsNestedEventTarget::Dispatch(nsIRunnable* aEvent, uint32_t aFlags)
{
LOG(("THRD(%p) Dispatch [%p %x] to nested loop %p\n", mThread.get(), event,
flags, this));
LOG(("THRD(%p) Dispatch [%p %x] to nested loop %p\n", mThread.get(), aEvent,
aFlags, this));
return mThread->DispatchInternal(event, flags, this);
return mThread->DispatchInternal(aEvent, aFlags, this);
}
NS_IMETHODIMP
nsThread::nsNestedEventTarget::IsOnCurrentThread(bool *result)
nsThread::nsNestedEventTarget::IsOnCurrentThread(bool* aResult)
{
return mThread->IsOnCurrentThread(result);
return mThread->IsOnCurrentThread(aResult);
}

View File

@ -18,8 +18,9 @@
#include "nsAutoPtr.h"
// A native thread
class nsThread : public nsIThreadInternal,
public nsISupportsPriority
class nsThread
: public nsIThreadInternal
, public nsISupportsPriority
{
public:
NS_DECL_THREADSAFE_ISUPPORTS
@ -28,7 +29,8 @@ public:
NS_DECL_NSITHREADINTERNAL
NS_DECL_NSISUPPORTSPRIORITY
enum MainThreadFlag {
enum MainThreadFlag
{
MAIN_THREAD,
NOT_MAIN_THREAD
};
@ -42,14 +44,23 @@ public:
nsresult InitCurrentThread();
// The PRThread corresponding to this thread.
PRThread *GetPRThread() { return mThread; }
PRThread* GetPRThread()
{
return mThread;
}
// If this flag is true, then the nsThread was created using
// nsIThreadManager::NewThread.
bool ShutdownRequired() { return mShutdownRequired; }
bool ShutdownRequired()
{
return mShutdownRequired;
}
// Clear the observer list.
void ClearObservers() { mEventObservers.Clear(); }
void ClearObservers()
{
mEventObservers.Clear();
}
static nsresult
SetMainThreadObserver(nsIThreadObserver* aObserver);
@ -66,59 +77,72 @@ protected:
virtual ~nsThread();
bool ShuttingDown() { return mShutdownContext != nullptr; }
bool ShuttingDown()
{
return mShutdownContext != nullptr;
}
static void ThreadFunc(void *arg);
static void ThreadFunc(void* aArg);
// Helper
already_AddRefed<nsIThreadObserver> GetObserver() {
nsIThreadObserver *obs;
already_AddRefed<nsIThreadObserver> GetObserver()
{
nsIThreadObserver* obs;
nsThread::GetObserver(&obs);
return already_AddRefed<nsIThreadObserver>(obs);
}
// Wrappers for event queue methods:
bool GetEvent(bool mayWait, nsIRunnable **event) {
return mEvents->GetEvent(mayWait, event);
bool GetEvent(bool aMayWait, nsIRunnable** aEvent)
{
return mEvents->GetEvent(aMayWait, aEvent);
}
nsresult PutEvent(nsIRunnable *event, nsNestedEventTarget *target);
nsresult PutEvent(nsIRunnable* aEvent, nsNestedEventTarget* aTarget);
nsresult DispatchInternal(nsIRunnable *event, uint32_t flags,
nsNestedEventTarget *target);
nsresult DispatchInternal(nsIRunnable* aEvent, uint32_t aFlags,
nsNestedEventTarget* aTarget);
// Wrapper for nsEventQueue that supports chaining.
class nsChainedEventQueue {
class nsChainedEventQueue
{
public:
nsChainedEventQueue()
: mNext(nullptr) {
: mNext(nullptr)
{
}
bool GetEvent(bool mayWait, nsIRunnable **event) {
return mQueue.GetEvent(mayWait, event);
bool GetEvent(bool aMayWait, nsIRunnable** aEvent)
{
return mQueue.GetEvent(aMayWait, aEvent);
}
void PutEvent(nsIRunnable *event) {
mQueue.PutEvent(event);
void PutEvent(nsIRunnable* aEvent)
{
mQueue.PutEvent(aEvent);
}
bool HasPendingEvent() {
bool HasPendingEvent()
{
return mQueue.HasPendingEvent();
}
nsChainedEventQueue *mNext;
nsChainedEventQueue* mNext;
nsRefPtr<nsNestedEventTarget> mEventTarget;
private:
nsEventQueue mQueue;
};
class nsNestedEventTarget MOZ_FINAL : public nsIEventTarget {
class nsNestedEventTarget MOZ_FINAL : public nsIEventTarget
{
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIEVENTTARGET
nsNestedEventTarget(nsThread *thread, nsChainedEventQueue *queue)
: mThread(thread), mQueue(queue) {
nsNestedEventTarget(nsThread* aThread, nsChainedEventQueue* aQueue)
: mThread(aThread)
, mQueue(aQueue)
{
}
nsRefPtr<nsThread> mThread;
@ -127,7 +151,9 @@ protected:
nsChainedEventQueue* mQueue;
private:
~nsNestedEventTarget() {}
~nsNestedEventTarget()
{
}
};
// This lock protects access to mObserver, mEvents and mEventsAreDoomed.
@ -142,15 +168,15 @@ protected:
// Only accessed on the target thread.
nsAutoTObserverArray<nsCOMPtr<nsIThreadObserver>, 2> mEventObservers;
nsChainedEventQueue *mEvents; // never null
nsChainedEventQueue* mEvents; // never null
nsChainedEventQueue mEventsRoot;
int32_t mPriority;
PRThread *mThread;
PRThread* mThread;
uint32_t mRunningEvent; // counter
uint32_t mStackSize;
struct nsThreadShutdownContext *mShutdownContext;
struct nsThreadShutdownContext* mShutdownContext;
bool mShutdownRequired;
// Set to true when events posted to this thread will never run.
@ -160,17 +186,23 @@ protected:
//-----------------------------------------------------------------------------
class nsThreadSyncDispatch : public nsRunnable {
class nsThreadSyncDispatch : public nsRunnable
{
public:
nsThreadSyncDispatch(nsIThread *origin, nsIRunnable *task)
: mOrigin(origin), mSyncTask(task), mResult(NS_ERROR_NOT_INITIALIZED) {
nsThreadSyncDispatch(nsIThread* aOrigin, nsIRunnable* aTask)
: mOrigin(aOrigin)
, mSyncTask(aTask)
, mResult(NS_ERROR_NOT_INITIALIZED)
{
}
bool IsPending() {
bool IsPending()
{
return mSyncTask != nullptr;
}
nsresult Result() {
nsresult Result()
{
return mResult;
}

View File

@ -24,27 +24,35 @@ DWORD gTLSThreadIDIndex = TlsAlloc();
NS_TLS mozilla::threads::ID gTLSThreadID = mozilla::threads::Generic;
#endif
typedef nsTArray< nsRefPtr<nsThread> > nsThreadArray;
typedef nsTArray<nsRefPtr<nsThread>> nsThreadArray;
//-----------------------------------------------------------------------------
static void
ReleaseObject(void *data)
ReleaseObject(void* aData)
{
static_cast<nsISupports *>(data)->Release();
static_cast<nsISupports*>(aData)->Release();
}
static PLDHashOperator
AppendAndRemoveThread(PRThread *key, nsRefPtr<nsThread> &thread, void *arg)
AppendAndRemoveThread(PRThread* aKey, nsRefPtr<nsThread>& aThread, void* aArg)
{
nsThreadArray *threads = static_cast<nsThreadArray *>(arg);
threads->AppendElement(thread);
nsThreadArray* threads = static_cast<nsThreadArray*>(aArg);
threads->AppendElement(aThread);
return PL_DHASH_REMOVE;
}
// statically allocated instance
NS_IMETHODIMP_(MozExternalRefCountType) nsThreadManager::AddRef() { return 2; }
NS_IMETHODIMP_(MozExternalRefCountType) nsThreadManager::Release() { return 1; }
NS_IMETHODIMP_(MozExternalRefCountType)
nsThreadManager::AddRef()
{
return 2;
}
NS_IMETHODIMP_(MozExternalRefCountType)
nsThreadManager::Release()
{
return 1;
}
NS_IMPL_CLASSINFO(nsThreadManager, nullptr,
nsIClassInfo::THREADSAFE | nsIClassInfo::SINGLETON,
NS_THREADMANAGER_CID)
@ -59,11 +67,13 @@ nsThreadManager::Init()
// Child processes need to initialize the thread manager before they
// initialize XPCOM in order to set up the crash reporter. This leads to
// situations where we get initialized twice.
if (mInitialized)
if (mInitialized) {
return NS_OK;
}
if (PR_NewThreadPrivateIndex(&mCurThreadIndex, ReleaseObject) == PR_FAILURE)
if (PR_NewThreadPrivateIndex(&mCurThreadIndex, ReleaseObject) == PR_FAILURE) {
return NS_ERROR_FAILURE;
}
mLock = new Mutex("nsThreadManager.mLock");
@ -71,9 +81,10 @@ nsThreadManager::Init()
const int flags = O_WRONLY | O_APPEND | O_CREAT | O_NONBLOCK;
const mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
char* env_var_flag = getenv("MOZ_KILL_CANARIES");
sCanaryOutputFD = env_var_flag ? (env_var_flag[0] ?
open(env_var_flag, flags, mode) :
STDERR_FILENO) : 0;
sCanaryOutputFD =
env_var_flag ? (env_var_flag[0] ? open(env_var_flag, flags, mode) :
STDERR_FILENO) :
0;
#endif
// Setup "main" thread
@ -90,7 +101,7 @@ nsThreadManager::Init()
mMainThread->GetPRThread(&mMainPRThread);
#ifdef XP_WIN
TlsSetValue(gTLSThreadIDIndex, (void*) mozilla::threads::Main);
TlsSetValue(gTLSThreadIDIndex, (void*)mozilla::threads::Main);
#elif defined(NS_TLS)
gTLSThreadID = mozilla::threads::Main;
#endif
@ -126,16 +137,17 @@ nsThreadManager::Shutdown()
// accepting new events, but that could lead to badness if one of those
// threads is stuck waiting for a response from another thread. To do it
// right, we'd need some way to interrupt the threads.
//
//
// Instead, we process events on the current thread while waiting for threads
// to shutdown. This means that we have to preserve a mostly functioning
// world until such time as the threads exit.
// Shutdown all threads that require it (join with threads that we created).
for (uint32_t i = 0; i < threads.Length(); ++i) {
nsThread *thread = threads[i];
if (thread->ShutdownRequired())
nsThread* thread = threads[i];
if (thread->ShutdownRequired()) {
thread->Shutdown();
}
}
// In case there are any more events somehow...
@ -164,9 +176,9 @@ nsThreadManager::Shutdown()
}
void
nsThreadManager::RegisterCurrentThread(nsThread *thread)
nsThreadManager::RegisterCurrentThread(nsThread* aThread)
{
MOZ_ASSERT(thread->GetPRThread() == PR_GetCurrentThread(), "bad thread");
MOZ_ASSERT(aThread->GetPRThread() == PR_GetCurrentThread(), "bad aThread");
MutexAutoLock lock(*mLock);
@ -175,33 +187,34 @@ nsThreadManager::RegisterCurrentThread(nsThread *thread)
mHighestNumberOfThreads = mCurrentNumberOfThreads;
}
mThreadsByPRThread.Put(thread->GetPRThread(), thread); // XXX check OOM?
mThreadsByPRThread.Put(aThread->GetPRThread(), aThread); // XXX check OOM?
NS_ADDREF(thread); // for TLS entry
PR_SetThreadPrivate(mCurThreadIndex, thread);
NS_ADDREF(aThread); // for TLS entry
PR_SetThreadPrivate(mCurThreadIndex, aThread);
}
void
nsThreadManager::UnregisterCurrentThread(nsThread *thread)
nsThreadManager::UnregisterCurrentThread(nsThread* aThread)
{
MOZ_ASSERT(thread->GetPRThread() == PR_GetCurrentThread(), "bad thread");
MOZ_ASSERT(aThread->GetPRThread() == PR_GetCurrentThread(), "bad aThread");
MutexAutoLock lock(*mLock);
--mCurrentNumberOfThreads;
mThreadsByPRThread.Remove(thread->GetPRThread());
mThreadsByPRThread.Remove(aThread->GetPRThread());
PR_SetThreadPrivate(mCurThreadIndex, nullptr);
// Ref-count balanced via ReleaseObject
}
nsThread *
nsThread*
nsThreadManager::GetCurrentThread()
{
// read thread local storage
void *data = PR_GetThreadPrivate(mCurThreadIndex);
if (data)
return static_cast<nsThread *>(data);
void* data = PR_GetThreadPrivate(mCurThreadIndex);
if (data) {
return static_cast<nsThread*>(data);
}
if (!mInitialized) {
return nullptr;
@ -209,24 +222,27 @@ nsThreadManager::GetCurrentThread()
// OK, that's fine. We'll dynamically create one :-)
nsRefPtr<nsThread> thread = new nsThread(nsThread::NOT_MAIN_THREAD, 0);
if (!thread || NS_FAILED(thread->InitCurrentThread()))
if (!thread || NS_FAILED(thread->InitCurrentThread())) {
return nullptr;
}
return thread.get(); // reference held in TLS
}
NS_IMETHODIMP
nsThreadManager::NewThread(uint32_t creationFlags,
uint32_t stackSize,
nsIThread **result)
nsThreadManager::NewThread(uint32_t aCreationFlags,
uint32_t aStackSize,
nsIThread** aResult)
{
// No new threads during Shutdown
if (NS_WARN_IF(!mInitialized))
if (NS_WARN_IF(!mInitialized)) {
return NS_ERROR_NOT_INITIALIZED;
}
nsThread *thr = new nsThread(nsThread::NOT_MAIN_THREAD, stackSize);
if (!thr)
nsThread* thr = new nsThread(nsThread::NOT_MAIN_THREAD, aStackSize);
if (!thr) {
return NS_ERROR_OUT_OF_MEMORY;
}
NS_ADDREF(thr);
nsresult rv = thr->Init();
@ -239,58 +255,63 @@ nsThreadManager::NewThread(uint32_t creationFlags,
// however, it is possible that it could have also been replaced by now, so
// we cannot really assert that it was added.
*result = thr;
*aResult = thr;
return NS_OK;
}
NS_IMETHODIMP
nsThreadManager::GetThreadFromPRThread(PRThread *thread, nsIThread **result)
nsThreadManager::GetThreadFromPRThread(PRThread* aThread, nsIThread** aResult)
{
// Keep this functioning during Shutdown
if (NS_WARN_IF(!mMainThread))
if (NS_WARN_IF(!mMainThread)) {
return NS_ERROR_NOT_INITIALIZED;
if (NS_WARN_IF(!thread))
}
if (NS_WARN_IF(!aThread)) {
return NS_ERROR_INVALID_ARG;
}
nsRefPtr<nsThread> temp;
{
MutexAutoLock lock(*mLock);
mThreadsByPRThread.Get(thread, getter_AddRefs(temp));
mThreadsByPRThread.Get(aThread, getter_AddRefs(temp));
}
NS_IF_ADDREF(*result = temp);
NS_IF_ADDREF(*aResult = temp);
return NS_OK;
}
NS_IMETHODIMP
nsThreadManager::GetMainThread(nsIThread **result)
nsThreadManager::GetMainThread(nsIThread** aResult)
{
// Keep this functioning during Shutdown
if (NS_WARN_IF(!mMainThread))
if (NS_WARN_IF(!mMainThread)) {
return NS_ERROR_NOT_INITIALIZED;
NS_ADDREF(*result = mMainThread);
}
NS_ADDREF(*aResult = mMainThread);
return NS_OK;
}
NS_IMETHODIMP
nsThreadManager::GetCurrentThread(nsIThread **result)
nsThreadManager::GetCurrentThread(nsIThread** aResult)
{
// Keep this functioning during Shutdown
if (NS_WARN_IF(!mMainThread))
if (NS_WARN_IF(!mMainThread)) {
return NS_ERROR_NOT_INITIALIZED;
*result = GetCurrentThread();
if (!*result)
}
*aResult = GetCurrentThread();
if (!*aResult) {
return NS_ERROR_OUT_OF_MEMORY;
NS_ADDREF(*result);
}
NS_ADDREF(*aResult);
return NS_OK;
}
NS_IMETHODIMP
nsThreadManager::GetIsMainThread(bool *result)
nsThreadManager::GetIsMainThread(bool* aResult)
{
// This method may be called post-Shutdown
*result = (PR_GetCurrentThread() == mMainPRThread);
*aResult = (PR_GetCurrentThread() == mMainPRThread);
return NS_OK;
}

View File

@ -20,7 +20,8 @@ public:
NS_DECL_ISUPPORTS
NS_DECL_NSITHREADMANAGER
static nsThreadManager *get() {
static nsThreadManager* get()
{
static nsThreadManager sInstance;
return &sInstance;
}
@ -33,15 +34,15 @@ public:
// Called by nsThread to inform the ThreadManager it exists. This method
// must be called when the given thread is the current thread.
void RegisterCurrentThread(nsThread *thread);
void RegisterCurrentThread(nsThread* aThread);
// Called by nsThread to inform the ThreadManager it is going away. This
// method must be called when the given thread is the current thread.
void UnregisterCurrentThread(nsThread *thread);
void UnregisterCurrentThread(nsThread* aThread);
// Returns the current thread. Returns null if OOM or if ThreadManager isn't
// initialized.
nsThread *GetCurrentThread();
nsThread* GetCurrentThread();
// Returns the maximal number of threads that have been in existence
// simultaneously during the execution of the thread manager.
@ -49,7 +50,9 @@ public:
// This needs to be public in order to support static instantiation of this
// class with older compilers (e.g., egcs-2.91.66).
~nsThreadManager() {}
~nsThreadManager()
{
}
private:
nsThreadManager()
@ -58,22 +61,23 @@ private:
, mLock(nullptr)
, mInitialized(false)
, mCurrentNumberOfThreads(1)
, mHighestNumberOfThreads(1) {
, mHighestNumberOfThreads(1)
{
}
nsRefPtrHashtable<nsPtrHashKey<PRThread>, nsThread> mThreadsByPRThread;
unsigned mCurThreadIndex; // thread-local-storage index
nsRefPtr<nsThread> mMainThread;
PRThread *mMainPRThread;
PRThread* mMainPRThread;
// This is a pointer in order to allow creating nsThreadManager from
// the static context in debug builds.
nsAutoPtr<mozilla::Mutex> mLock; // protects tables
bool mInitialized;
// The current number of threads
uint32_t mCurrentNumberOfThreads;
// The highest number of threads encountered so far during the session
uint32_t mHighestNumberOfThreads;
// The current number of threads
uint32_t mCurrentNumberOfThreads;
// The highest number of threads encountered so far during the session
uint32_t mHighestNumberOfThreads;
};
#define NS_THREADMANAGER_CID \

View File

@ -16,12 +16,13 @@
using namespace mozilla;
#ifdef PR_LOGGING
static PRLogModuleInfo *
static PRLogModuleInfo*
GetThreadPoolLog()
{
static PRLogModuleInfo *sLog;
if (!sLog)
static PRLogModuleInfo* sLog;
if (!sLog) {
sLog = PR_NewLogModule("nsThreadPool");
}
return sLog;
}
#endif
@ -66,7 +67,7 @@ nsThreadPool::~nsThreadPool()
}
nsresult
nsThreadPool::PutEvent(nsIRunnable *event)
nsThreadPool::PutEvent(nsIRunnable* aEvent)
{
// Avoid spawning a new thread while holding the event queue lock...
@ -77,31 +78,34 @@ nsThreadPool::PutEvent(nsIRunnable *event)
LOG(("THRD-P(%p) put [%d %d %d]\n", this, mIdleCount, mThreads.Count(),
mThreadLimit));
MOZ_ASSERT(mIdleCount <= (uint32_t) mThreads.Count(), "oops");
MOZ_ASSERT(mIdleCount <= (uint32_t)mThreads.Count(), "oops");
// Make sure we have a thread to service this event.
if (mIdleCount == 0 && mThreads.Count() < (int32_t) mThreadLimit)
if (mIdleCount == 0 && mThreads.Count() < (int32_t)mThreadLimit) {
spawnThread = true;
}
mEvents.PutEvent(event);
mEvents.PutEvent(aEvent);
stackSize = mStackSize;
}
LOG(("THRD-P(%p) put [spawn=%d]\n", this, spawnThread));
if (!spawnThread)
if (!spawnThread) {
return NS_OK;
}
nsCOMPtr<nsIThread> thread;
nsThreadManager::get()->NewThread(0,
stackSize,
getter_AddRefs(thread));
if (NS_WARN_IF(!thread))
if (NS_WARN_IF(!thread)) {
return NS_ERROR_UNEXPECTED;
}
bool killThread = false;
{
ReentrantMonitorAutoEnter mon(mEvents.GetReentrantMonitor());
if (mThreads.Count() < (int32_t) mThreadLimit) {
if (mThreads.Count() < (int32_t)mThreadLimit) {
mThreads.AppendObject(thread);
} else {
killThread = true; // okay, we don't need this thread anymore
@ -126,16 +130,16 @@ nsThreadPool::PutEvent(nsIRunnable *event)
}
void
nsThreadPool::ShutdownThread(nsIThread *thread)
nsThreadPool::ShutdownThread(nsIThread* aThread)
{
LOG(("THRD-P(%p) shutdown async [%p]\n", this, thread));
LOG(("THRD-P(%p) shutdown async [%p]\n", this, aThread));
// This method is responsible for calling Shutdown on |thread|. This must be
// This method is responsible for calling Shutdown on |aThread|. This must be
// done from some other thread, so we use the main thread of the application.
MOZ_ASSERT(!NS_IsMainThread(), "wrong thread");
nsRefPtr<nsIRunnable> r = NS_NewRunnableMethod(thread, &nsIThread::Shutdown);
nsRefPtr<nsIRunnable> r = NS_NewRunnableMethod(aThread, &nsIThread::Shutdown);
NS_DispatchToMainThread(r);
}
@ -178,8 +182,9 @@ nsThreadPool::Run()
} else {
if (wasIdle) {
// if too many idle threads or idle for too long, then bail.
if (mIdleCount > mIdleThreadLimit || (now - idleSince) >= timeout)
if (mIdleCount > mIdleThreadLimit || (now - idleSince) >= timeout) {
exitThread = true;
}
} else {
// if would be too many idle threads...
if (mIdleCount == mIdleThreadLimit) {
@ -193,8 +198,9 @@ nsThreadPool::Run()
}
if (exitThread) {
if (wasIdle)
if (wasIdle) {
--mIdleCount;
}
shutdownThreadOnExit = mThreads.RemoveObject(current);
} else {
PRIntervalTime delta = timeout - (now - idleSince);
@ -225,44 +231,47 @@ nsThreadPool::Run()
}
NS_IMETHODIMP
nsThreadPool::Dispatch(nsIRunnable *event, uint32_t flags)
nsThreadPool::Dispatch(nsIRunnable* aEvent, uint32_t aFlags)
{
LOG(("THRD-P(%p) dispatch [%p %x]\n", this, event, flags));
LOG(("THRD-P(%p) dispatch [%p %x]\n", this, aEvent, aFlags));
if (NS_WARN_IF(mShutdown))
if (NS_WARN_IF(mShutdown)) {
return NS_ERROR_NOT_AVAILABLE;
}
if (flags & DISPATCH_SYNC) {
if (aFlags & DISPATCH_SYNC) {
nsCOMPtr<nsIThread> thread;
nsThreadManager::get()->GetCurrentThread(getter_AddRefs(thread));
if (NS_WARN_IF(!thread))
if (NS_WARN_IF(!thread)) {
return NS_ERROR_NOT_AVAILABLE;
}
nsRefPtr<nsThreadSyncDispatch> wrapper =
new nsThreadSyncDispatch(thread, event);
new nsThreadSyncDispatch(thread, aEvent);
PutEvent(wrapper);
while (wrapper->IsPending())
while (wrapper->IsPending()) {
NS_ProcessNextEvent(thread);
}
} else {
NS_ASSERTION(flags == NS_DISPATCH_NORMAL, "unexpected dispatch flags");
PutEvent(event);
NS_ASSERTION(aFlags == NS_DISPATCH_NORMAL, "unexpected dispatch flags");
PutEvent(aEvent);
}
return NS_OK;
}
NS_IMETHODIMP
nsThreadPool::IsOnCurrentThread(bool *result)
nsThreadPool::IsOnCurrentThread(bool* aResult)
{
ReentrantMonitorAutoEnter mon(mEvents.GetReentrantMonitor());
nsIThread* thread = NS_GetCurrentThread();
for (uint32_t i = 0; i < static_cast<uint32_t>(mThreads.Count()); ++i) {
if (mThreads[i] == thread) {
*result = true;
*aResult = true;
return NS_OK;
}
}
*result = false;
*aResult = false;
return NS_OK;
}
@ -288,26 +297,28 @@ nsThreadPool::Shutdown()
// It's important that we shutdown the threads while outside the event queue
// monitor. Otherwise, we could end up dead-locking.
for (int32_t i = 0; i < threads.Count(); ++i)
for (int32_t i = 0; i < threads.Count(); ++i) {
threads[i]->Shutdown();
}
return NS_OK;
}
NS_IMETHODIMP
nsThreadPool::GetThreadLimit(uint32_t *value)
nsThreadPool::GetThreadLimit(uint32_t* aValue)
{
*value = mThreadLimit;
*aValue = mThreadLimit;
return NS_OK;
}
NS_IMETHODIMP
nsThreadPool::SetThreadLimit(uint32_t value)
nsThreadPool::SetThreadLimit(uint32_t aValue)
{
ReentrantMonitorAutoEnter mon(mEvents.GetReentrantMonitor());
mThreadLimit = value;
if (mIdleThreadLimit > mThreadLimit)
mThreadLimit = aValue;
if (mIdleThreadLimit > mThreadLimit) {
mIdleThreadLimit = mThreadLimit;
}
if (static_cast<uint32_t>(mThreads.Count()) > mThreadLimit) {
mon.NotifyAll(); // wake up threads so they observe this change
@ -316,19 +327,20 @@ nsThreadPool::SetThreadLimit(uint32_t value)
}
NS_IMETHODIMP
nsThreadPool::GetIdleThreadLimit(uint32_t *value)
nsThreadPool::GetIdleThreadLimit(uint32_t* aValue)
{
*value = mIdleThreadLimit;
*aValue = mIdleThreadLimit;
return NS_OK;
}
NS_IMETHODIMP
nsThreadPool::SetIdleThreadLimit(uint32_t value)
nsThreadPool::SetIdleThreadLimit(uint32_t aValue)
{
ReentrantMonitorAutoEnter mon(mEvents.GetReentrantMonitor());
mIdleThreadLimit = value;
if (mIdleThreadLimit > mThreadLimit)
mIdleThreadLimit = aValue;
if (mIdleThreadLimit > mThreadLimit) {
mIdleThreadLimit = mThreadLimit;
}
// Do we need to kill some idle threads?
if (mIdleCount > mIdleThreadLimit) {
@ -338,18 +350,18 @@ nsThreadPool::SetIdleThreadLimit(uint32_t value)
}
NS_IMETHODIMP
nsThreadPool::GetIdleThreadTimeout(uint32_t *value)
nsThreadPool::GetIdleThreadTimeout(uint32_t* aValue)
{
*value = mIdleThreadTimeout;
*aValue = mIdleThreadTimeout;
return NS_OK;
}
NS_IMETHODIMP
nsThreadPool::SetIdleThreadTimeout(uint32_t value)
nsThreadPool::SetIdleThreadTimeout(uint32_t aValue)
{
ReentrantMonitorAutoEnter mon(mEvents.GetReentrantMonitor());
uint32_t oldTimeout = mIdleThreadTimeout;
mIdleThreadTimeout = value;
mIdleThreadTimeout = aValue;
// Do we need to notify any idle threads that their sleep time has shortened?
if (mIdleThreadTimeout < oldTimeout && mIdleCount > 0) {
@ -359,18 +371,18 @@ nsThreadPool::SetIdleThreadTimeout(uint32_t value)
}
NS_IMETHODIMP
nsThreadPool::GetThreadStackSize(uint32_t* value)
nsThreadPool::GetThreadStackSize(uint32_t* aValue)
{
ReentrantMonitorAutoEnter mon(mEvents.GetReentrantMonitor());
*value = mStackSize;
*aValue = mStackSize;
return NS_OK;
}
NS_IMETHODIMP
nsThreadPool::SetThreadStackSize(uint32_t value)
nsThreadPool::SetThreadStackSize(uint32_t aValue)
{
ReentrantMonitorAutoEnter mon(mEvents.GetReentrantMonitor());
mStackSize = value;
mStackSize = aValue;
return NS_OK;
}
@ -398,8 +410,9 @@ nsThreadPool::SetName(const nsACString& aName)
{
{
ReentrantMonitorAutoEnter mon(mEvents.GetReentrantMonitor());
if (mThreads.Count())
if (mThreads.Count()) {
return NS_ERROR_NOT_AVAILABLE;
}
}
mName = aName;

View File

@ -16,8 +16,9 @@
#include "nsThreadUtils.h"
#include "mozilla/Attributes.h"
class nsThreadPool MOZ_FINAL : public nsIThreadPool,
public nsIRunnable
class nsThreadPool MOZ_FINAL
: public nsIThreadPool
, public nsIRunnable
{
public:
NS_DECL_THREADSAFE_ISUPPORTS
@ -30,8 +31,8 @@ public:
private:
~nsThreadPool();
void ShutdownThread(nsIThread *thread);
nsresult PutEvent(nsIRunnable *event);
void ShutdownThread(nsIThread* aThread);
nsresult PutEvent(nsIRunnable* aEvent);
nsCOMArray<nsIThread> mThreads;
nsEventQueue mEvents;

View File

@ -26,9 +26,10 @@ static TimerThread* gThread = nullptr;
PRLogModuleInfo*
GetTimerLog()
{
static PRLogModuleInfo *sLog;
if (!sLog)
static PRLogModuleInfo* sLog;
if (!sLog) {
sLog = PR_NewLogModule("nsTimerImpl");
}
return sLog;
}
@ -40,16 +41,17 @@ double nsTimerImpl::sDeltaNum = 0;
static void
myNS_MeanAndStdDev(double n, double sumOfValues, double sumOfSquaredValues,
double *meanResult, double *stdDevResult)
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)
if (temp < 0.0 || n <= 1) {
var = 0.0;
else
} 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;
}
@ -77,7 +79,8 @@ namespace {
class TimerEventAllocator
{
private:
struct FreeEntry {
struct FreeEntry
{
FreeEntry* mNext;
};
@ -87,8 +90,8 @@ private:
public:
TimerEventAllocator()
: mFirstFree(nullptr),
mMonitor("TimerEventAllocator")
: mFirstFree(nullptr)
, mMonitor("TimerEventAllocator")
{
PL_InitArenaPool(&mPool, "TimerEventPool", 4096, /* align = */ 0);
}
@ -104,13 +107,16 @@ public:
} // anonymous namespace
class nsTimerEvent : public nsRunnable {
class nsTimerEvent : public nsRunnable
{
public:
NS_IMETHOD Run();
nsTimerEvent(nsTimerImpl *timer, int32_t generation)
: mTimer(dont_AddRef(timer)), mGeneration(generation) {
// timer is already addref'd for us
nsTimerEvent(nsTimerImpl* aTimer, int32_t aGeneration)
: mTimer(dont_AddRef(aTimer))
, mGeneration(aGeneration)
{
// aTimer is already addref'd for us
MOZ_COUNT_CTOR(nsTimerEvent);
MOZ_ASSERT(gThread->IsOnTimerThread(),
@ -127,17 +133,20 @@ public:
static void Shutdown();
static void DeleteAllocatorIfNeeded();
static void* operator new(size_t size) CPP_THROW_NEW {
return sAllocator->Alloc(size);
static void* operator new(size_t aSize) CPP_THROW_NEW
{
return sAllocator->Alloc(aSize);
}
void operator delete(void* p) {
sAllocator->Free(p);
void operator delete(void* aPtr)
{
sAllocator->Free(aPtr);
DeleteAllocatorIfNeeded();
}
private:
nsTimerEvent(); // Not implemented
~nsTimerEvent() {
~nsTimerEvent()
{
MOZ_COUNT_DTOR(nsTimerEvent);
MOZ_ASSERT(!sCanDeleteAllocator || sAllocatorUsers > 0,
@ -159,7 +168,8 @@ bool nsTimerEvent::sCanDeleteAllocator = false;
namespace {
void* TimerEventAllocator::Alloc(size_t aSize)
void*
TimerEventAllocator::Alloc(size_t aSize)
{
MOZ_ASSERT(aSize == sizeof(nsTimerEvent));
@ -169,17 +179,18 @@ void* TimerEventAllocator::Alloc(size_t aSize)
if (mFirstFree) {
p = mFirstFree;
mFirstFree = mFirstFree->mNext;
}
else {
} else {
PL_ARENA_ALLOCATE(p, &mPool, aSize);
if (!p)
if (!p) {
return nullptr;
}
}
return p;
}
void TimerEventAllocator::Free(void* aPtr)
void
TimerEventAllocator::Free(void* aPtr)
{
mozilla::MonitorAutoLock lock(mMonitor);
@ -194,7 +205,8 @@ void TimerEventAllocator::Free(void* aPtr)
NS_IMPL_QUERY_INTERFACE(nsTimerImpl, nsITimer)
NS_IMPL_ADDREF(nsTimerImpl)
NS_IMETHODIMP_(MozExternalRefCountType) nsTimerImpl::Release(void)
NS_IMETHODIMP_(MozExternalRefCountType)
nsTimerImpl::Release(void)
{
nsrefcnt count;
@ -243,8 +255,9 @@ NS_IMETHODIMP_(MozExternalRefCountType) nsTimerImpl::Release(void)
mCanceled = true;
MOZ_ASSERT(gThread, "Armed timer exists after the thread timer stopped.");
if (NS_SUCCEEDED(gThread->RemoveTimer(this)))
if (NS_SUCCEEDED(gThread->RemoveTimer(this))) {
return 0;
}
}
return count;
@ -279,7 +292,9 @@ nsTimerImpl::Startup()
nsTimerEvent::Init();
gThread = new TimerThread();
if (!gThread) return NS_ERROR_OUT_OF_MEMORY;
if (!gThread) {
return NS_ERROR_OUT_OF_MEMORY;
}
NS_ADDREF(gThread);
rv = gThread->InitLocks();
@ -291,20 +306,25 @@ nsTimerImpl::Startup()
return rv;
}
void nsTimerImpl::Shutdown()
void
nsTimerImpl::Shutdown()
{
#ifdef DEBUG_TIMERS
if (PR_LOG_TEST(GetTimerLog(), PR_LOG_DEBUG)) {
double mean = 0, stddev = 0;
myNS_MeanAndStdDev(sDeltaNum, sDeltaSum, sDeltaSumSquared, &mean, &stddev);
PR_LOG(GetTimerLog(), PR_LOG_DEBUG, ("sDeltaNum = %f, sDeltaSum = %f, sDeltaSumSquared = %f\n", sDeltaNum, sDeltaSum, sDeltaSumSquared));
PR_LOG(GetTimerLog(), PR_LOG_DEBUG, ("mean: %fms, stddev: %fms\n", mean, stddev));
PR_LOG(GetTimerLog(), PR_LOG_DEBUG,
("sDeltaNum = %f, sDeltaSum = %f, sDeltaSumSquared = %f\n",
sDeltaNum, sDeltaSum, sDeltaSumSquared));
PR_LOG(GetTimerLog(), PR_LOG_DEBUG,
("mean: %fms, stddev: %fms\n", mean, stddev));
}
#endif
if (!gThread)
if (!gThread) {
return;
}
gThread->Shutdown();
NS_RELEASE(gThread);
@ -313,20 +333,23 @@ void nsTimerImpl::Shutdown()
}
nsresult nsTimerImpl::InitCommon(uint32_t aType, uint32_t aDelay)
nsresult
nsTimerImpl::InitCommon(uint32_t aType, uint32_t aDelay)
{
nsresult rv;
if (NS_WARN_IF(!gThread))
if (NS_WARN_IF(!gThread)) {
return NS_ERROR_NOT_INITIALIZED;
}
if (!mEventTarget) {
NS_ERROR("mEventTarget is NULL");
return NS_ERROR_NOT_INITIALIZED;
}
rv = gThread->Init();
if (NS_WARN_IF(NS_FAILED(rv)))
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
/**
* In case of re-Init, both with and without a preceding Cancel, clear the
@ -342,8 +365,9 @@ nsresult nsTimerImpl::InitCommon(uint32_t aType, uint32_t aDelay)
* be cleared by another CPU whose store hasn't reached our CPU's cache),
* because RemoveTimer is idempotent.
*/
if (mArmed)
if (mArmed) {
gThread->RemoveTimer(this);
}
mCanceled = false;
mTimeout = TimeStamp();
mGeneration = gGenerator++;
@ -354,14 +378,16 @@ nsresult nsTimerImpl::InitCommon(uint32_t aType, uint32_t aDelay)
return gThread->AddTimer(this);
}
NS_IMETHODIMP nsTimerImpl::InitWithFuncCallback(nsTimerCallbackFunc aFunc,
void *aClosure,
uint32_t aDelay,
uint32_t aType)
NS_IMETHODIMP
nsTimerImpl::InitWithFuncCallback(nsTimerCallbackFunc aFunc,
void* aClosure,
uint32_t aDelay,
uint32_t aType)
{
if (NS_WARN_IF(!aFunc))
if (NS_WARN_IF(!aFunc)) {
return NS_ERROR_INVALID_ARG;
}
ReleaseCallback();
mCallbackType = CALLBACK_TYPE_FUNC;
mCallback.c = aFunc;
@ -370,12 +396,14 @@ NS_IMETHODIMP nsTimerImpl::InitWithFuncCallback(nsTimerCallbackFunc aFunc,
return InitCommon(aType, aDelay);
}
NS_IMETHODIMP nsTimerImpl::InitWithCallback(nsITimerCallback *aCallback,
uint32_t aDelay,
uint32_t aType)
NS_IMETHODIMP
nsTimerImpl::InitWithCallback(nsITimerCallback* aCallback,
uint32_t aDelay,
uint32_t aType)
{
if (NS_WARN_IF(!aCallback))
if (NS_WARN_IF(!aCallback)) {
return NS_ERROR_INVALID_ARG;
}
ReleaseCallback();
mCallbackType = CALLBACK_TYPE_INTERFACE;
@ -385,12 +413,12 @@ NS_IMETHODIMP nsTimerImpl::InitWithCallback(nsITimerCallback *aCallback,
return InitCommon(aType, aDelay);
}
NS_IMETHODIMP nsTimerImpl::Init(nsIObserver *aObserver,
uint32_t aDelay,
uint32_t aType)
NS_IMETHODIMP
nsTimerImpl::Init(nsIObserver* aObserver, uint32_t aDelay, uint32_t aType)
{
if (NS_WARN_IF(!aObserver))
if (NS_WARN_IF(!aObserver)) {
return NS_ERROR_INVALID_ARG;
}
ReleaseCallback();
mCallbackType = CALLBACK_TYPE_OBSERVER;
@ -400,19 +428,22 @@ NS_IMETHODIMP nsTimerImpl::Init(nsIObserver *aObserver,
return InitCommon(aType, aDelay);
}
NS_IMETHODIMP nsTimerImpl::Cancel()
NS_IMETHODIMP
nsTimerImpl::Cancel()
{
mCanceled = true;
if (gThread)
if (gThread) {
gThread->RemoveTimer(this);
}
ReleaseCallback();
return NS_OK;
}
NS_IMETHODIMP nsTimerImpl::SetDelay(uint32_t aDelay)
NS_IMETHODIMP
nsTimerImpl::SetDelay(uint32_t aDelay)
{
if (mCallbackType == CALLBACK_TYPE_UNKNOWN && mType == TYPE_ONE_SHOT) {
// This may happen if someone tries to re-use a one-shot timer
@ -424,24 +455,28 @@ NS_IMETHODIMP nsTimerImpl::SetDelay(uint32_t aDelay)
// If we're already repeating precisely, update mTimeout now so that the
// new delay takes effect in the future.
if (!mTimeout.IsNull() && mType == TYPE_REPEATING_PRECISE)
if (!mTimeout.IsNull() && mType == TYPE_REPEATING_PRECISE) {
mTimeout = TimeStamp::Now();
}
SetDelayInternal(aDelay);
if (!mFiring && gThread)
if (!mFiring && gThread) {
gThread->TimerDelayChanged(this);
}
return NS_OK;
}
NS_IMETHODIMP nsTimerImpl::GetDelay(uint32_t* aDelay)
NS_IMETHODIMP
nsTimerImpl::GetDelay(uint32_t* aDelay)
{
*aDelay = mDelay;
return NS_OK;
}
NS_IMETHODIMP nsTimerImpl::SetType(uint32_t aType)
NS_IMETHODIMP
nsTimerImpl::SetType(uint32_t aType)
{
mType = (uint8_t)aType;
// XXX if this is called, we should change the actual type.. this could effect
@ -450,57 +485,67 @@ NS_IMETHODIMP nsTimerImpl::SetType(uint32_t aType)
return NS_OK;
}
NS_IMETHODIMP nsTimerImpl::GetType(uint32_t* aType)
NS_IMETHODIMP
nsTimerImpl::GetType(uint32_t* aType)
{
*aType = mType;
return NS_OK;
}
NS_IMETHODIMP nsTimerImpl::GetClosure(void** aClosure)
NS_IMETHODIMP
nsTimerImpl::GetClosure(void** aClosure)
{
*aClosure = mClosure;
return NS_OK;
}
NS_IMETHODIMP nsTimerImpl::GetCallback(nsITimerCallback **aCallback)
NS_IMETHODIMP
nsTimerImpl::GetCallback(nsITimerCallback** aCallback)
{
if (mCallbackType == CALLBACK_TYPE_INTERFACE)
if (mCallbackType == CALLBACK_TYPE_INTERFACE) {
NS_IF_ADDREF(*aCallback = mCallback.i);
else if (mTimerCallbackWhileFiring)
} else if (mTimerCallbackWhileFiring) {
NS_ADDREF(*aCallback = mTimerCallbackWhileFiring);
else
} else {
*aCallback = nullptr;
}
return NS_OK;
}
NS_IMETHODIMP nsTimerImpl::GetTarget(nsIEventTarget** aTarget)
NS_IMETHODIMP
nsTimerImpl::GetTarget(nsIEventTarget** aTarget)
{
NS_IF_ADDREF(*aTarget = mEventTarget);
return NS_OK;
}
NS_IMETHODIMP nsTimerImpl::SetTarget(nsIEventTarget* aTarget)
NS_IMETHODIMP
nsTimerImpl::SetTarget(nsIEventTarget* aTarget)
{
if (NS_WARN_IF(mCallbackType != CALLBACK_TYPE_UNKNOWN))
if (NS_WARN_IF(mCallbackType != CALLBACK_TYPE_UNKNOWN)) {
return NS_ERROR_ALREADY_INITIALIZED;
}
if (aTarget)
if (aTarget) {
mEventTarget = aTarget;
else
} else {
mEventTarget = static_cast<nsIEventTarget*>(NS_GetCurrentThread());
}
return NS_OK;
}
void nsTimerImpl::Fire()
void
nsTimerImpl::Fire()
{
if (mCanceled)
if (mCanceled) {
return;
}
PROFILER_LABEL("Timer", "Fire");
@ -519,10 +564,16 @@ void nsTimerImpl::Fire()
sDeltaSumSquared += double(d) * double(d);
sDeltaNum++;
PR_LOG(GetTimerLog(), PR_LOG_DEBUG, ("[this=%p] expected delay time %4ums\n", this, mDelay));
PR_LOG(GetTimerLog(), PR_LOG_DEBUG, ("[this=%p] actual delay time %fms\n", this, a.ToMilliseconds()));
PR_LOG(GetTimerLog(), PR_LOG_DEBUG, ("[this=%p] (mType is %d) -------\n", this, mType));
PR_LOG(GetTimerLog(), PR_LOG_DEBUG, ("[this=%p] delta %4dms\n", this, (a > b) ? (int32_t)d : -(int32_t)d));
PR_LOG(GetTimerLog(), PR_LOG_DEBUG,
("[this=%p] expected delay time %4ums\n", this, mDelay));
PR_LOG(GetTimerLog(), PR_LOG_DEBUG,
("[this=%p] actual delay time %fms\n", this,
a.ToMilliseconds()));
PR_LOG(GetTimerLog(), PR_LOG_DEBUG,
("[this=%p] (mType is %d) -------\n", this, mType));
PR_LOG(GetTimerLog(), PR_LOG_DEBUG,
("[this=%p] delta %4dms\n",
this, (a > b) ? (int32_t)d : -(int32_t)d));
mStart = mStart2;
mStart2 = TimeStamp();
@ -536,18 +587,20 @@ void nsTimerImpl::Fire()
timeout -= TimeDuration::FromMilliseconds(mDelay);
}
if (mCallbackType == CALLBACK_TYPE_INTERFACE)
if (mCallbackType == CALLBACK_TYPE_INTERFACE) {
mTimerCallbackWhileFiring = mCallback.i;
}
mFiring = true;
// Handle callbacks that re-init the timer, but avoid leaking.
// See bug 330128.
CallbackUnion callback = mCallback;
unsigned callbackType = mCallbackType;
if (callbackType == CALLBACK_TYPE_INTERFACE)
if (callbackType == CALLBACK_TYPE_INTERFACE) {
NS_ADDREF(callback.i);
else if (callbackType == CALLBACK_TYPE_OBSERVER)
} else if (callbackType == CALLBACK_TYPE_OBSERVER) {
NS_ADDREF(callback.o);
}
ReleaseCallback();
switch (callbackType) {
@ -562,7 +615,8 @@ void nsTimerImpl::Fire()
NS_TIMER_CALLBACK_TOPIC,
nullptr);
break;
default:;
default:
;
}
// If the callback didn't re-init the timer, and it's not a one-shot timer,
@ -573,10 +627,11 @@ void nsTimerImpl::Fire()
mCallbackType = callbackType;
} else {
// The timer was a one-shot, or the callback was reinitialized.
if (callbackType == CALLBACK_TYPE_INTERFACE)
if (callbackType == CALLBACK_TYPE_INTERFACE) {
NS_RELEASE(callback.i);
else if (callbackType == CALLBACK_TYPE_OBSERVER)
} else if (callbackType == CALLBACK_TYPE_OBSERVER) {
NS_RELEASE(callback.o);
}
}
mFiring = false;
@ -594,27 +649,32 @@ void nsTimerImpl::Fire()
// that in PostTimerEvent, but make sure that we aren't armed already (which
// can happen if the callback reinitialized the timer).
if (IsRepeating() && mType != TYPE_REPEATING_PRECISE && !mArmed) {
if (mType == TYPE_REPEATING_SLACK)
SetDelayInternal(mDelay); // force mTimeout to be recomputed. For
// REPEATING_PRECISE_CAN_SKIP timers this has
// already happened.
if (gThread)
if (mType == TYPE_REPEATING_SLACK) {
SetDelayInternal(mDelay); // force mTimeout to be recomputed. For
}
// REPEATING_PRECISE_CAN_SKIP timers this has
// already happened.
if (gThread) {
gThread->AddTimer(this);
}
}
}
void nsTimerEvent::Init()
void
nsTimerEvent::Init()
{
sAllocator = new TimerEventAllocator();
}
void nsTimerEvent::Shutdown()
void
nsTimerEvent::Shutdown()
{
sCanDeleteAllocator = true;
DeleteAllocatorIfNeeded();
}
void nsTimerEvent::DeleteAllocatorIfNeeded()
void
nsTimerEvent::DeleteAllocatorIfNeeded()
{
if (sCanDeleteAllocator && sAllocatorUsers == 0) {
delete sAllocator;
@ -622,10 +682,12 @@ void nsTimerEvent::DeleteAllocatorIfNeeded()
}
}
NS_IMETHODIMP nsTimerEvent::Run()
NS_IMETHODIMP
nsTimerEvent::Run()
{
if (mGeneration != mTimer->GetGeneration())
if (mGeneration != mTimer->GetGeneration()) {
return NS_OK;
}
#ifdef DEBUG_TIMERS
if (PR_LOG_TEST(GetTimerLog(), PR_LOG_DEBUG)) {
@ -641,7 +703,8 @@ NS_IMETHODIMP nsTimerEvent::Run()
return NS_OK;
}
nsresult nsTimerImpl::PostTimerEvent()
nsresult
nsTimerImpl::PostTimerEvent()
{
if (!mEventTarget) {
NS_ERROR("Attempt to post timer event to NULL event target");
@ -656,8 +719,9 @@ nsresult nsTimerImpl::PostTimerEvent()
// re-initialized after being canceled.
nsRefPtr<nsTimerEvent> event = new nsTimerEvent(this, mGeneration);
if (!event)
if (!event) {
return NS_ERROR_OUT_OF_MEMORY;
}
#ifdef DEBUG_TIMERS
if (PR_LOG_TEST(GetTimerLog(), PR_LOG_DEBUG)) {
@ -673,56 +737,62 @@ nsresult nsTimerImpl::PostTimerEvent()
// But only re-arm REPEATING_PRECISE timers.
if (gThread && mType == TYPE_REPEATING_PRECISE) {
nsresult rv = gThread->AddTimer(this);
if (NS_FAILED(rv))
if (NS_FAILED(rv)) {
return rv;
}
}
}
nsresult rv = mEventTarget->Dispatch(event, NS_DISPATCH_NORMAL);
if (NS_FAILED(rv) && gThread)
if (NS_FAILED(rv) && gThread) {
gThread->RemoveTimer(this);
}
return rv;
}
void nsTimerImpl::SetDelayInternal(uint32_t aDelay)
void
nsTimerImpl::SetDelayInternal(uint32_t aDelay)
{
TimeDuration delayInterval = TimeDuration::FromMilliseconds(aDelay);
mDelay = aDelay;
TimeStamp now = TimeStamp::Now();
if (mTimeout.IsNull() || mType != TYPE_REPEATING_PRECISE)
if (mTimeout.IsNull() || mType != TYPE_REPEATING_PRECISE) {
mTimeout = now;
}
mTimeout += delayInterval;
#ifdef DEBUG_TIMERS
if (PR_LOG_TEST(GetTimerLog(), PR_LOG_DEBUG)) {
if (mStart.IsNull())
if (mStart.IsNull()) {
mStart = now;
else
} else {
mStart2 = now;
}
}
#endif
}
// NOT FOR PUBLIC CONSUMPTION!
nsresult
NS_NewTimer(nsITimer* *aResult, nsTimerCallbackFunc aCallback, void *aClosure,
NS_NewTimer(nsITimer** aResult, nsTimerCallbackFunc aCallback, void* aClosure,
uint32_t aDelay, uint32_t aType)
{
nsTimerImpl* timer = new nsTimerImpl();
if (timer == nullptr)
return NS_ERROR_OUT_OF_MEMORY;
NS_ADDREF(timer);
nsTimerImpl* timer = new nsTimerImpl();
if (!timer) {
return NS_ERROR_OUT_OF_MEMORY;
}
NS_ADDREF(timer);
nsresult rv = timer->InitWithFuncCallback(aCallback, aClosure,
aDelay, aType);
if (NS_FAILED(rv)) {
NS_RELEASE(timer);
return rv;
}
nsresult rv = timer->InitWithFuncCallback(aCallback, aClosure,
aDelay, aType);
if (NS_FAILED(rv)) {
NS_RELEASE(timer);
return rv;
}
*aResult = timer;
return NS_OK;
*aResult = timer;
return NS_OK;
}

View File

@ -23,7 +23,7 @@
#endif
#if defined(PR_LOGGING)
extern PRLogModuleInfo *GetTimerLog();
extern PRLogModuleInfo* GetTimerLog();
#define DEBUG_TIMERS 1
#else
#undef DEBUG_TIMERS
@ -37,7 +37,8 @@ extern PRLogModuleInfo *GetTimerLog();
{0x84, 0x27, 0xfb, 0xab, 0x44, 0xf2, 0x9b, 0xc8} \
}
enum {
enum
{
CALLBACK_TYPE_UNKNOWN = 0,
CALLBACK_TYPE_INTERFACE = 1,
CALLBACK_TYPE_FUNC = 2,
@ -64,7 +65,10 @@ public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSITIMER
int32_t GetGeneration() { return mGeneration; }
int32_t GetGeneration()
{
return mGeneration;
}
#ifdef MOZ_TASK_TRACER
void DispatchTracedTask()
@ -83,33 +87,37 @@ private:
// sure that we don't recurse into ReleaseCallback in case
// the callback's destructor calls Cancel() or similar.
uint8_t cbType = mCallbackType;
mCallbackType = CALLBACK_TYPE_UNKNOWN;
mCallbackType = CALLBACK_TYPE_UNKNOWN;
if (cbType == CALLBACK_TYPE_INTERFACE)
if (cbType == CALLBACK_TYPE_INTERFACE) {
NS_RELEASE(mCallback.i);
else if (cbType == CALLBACK_TYPE_OBSERVER)
} else if (cbType == CALLBACK_TYPE_OBSERVER) {
NS_RELEASE(mCallback.o);
}
}
bool IsRepeating() const {
bool IsRepeating() const
{
PR_STATIC_ASSERT(TYPE_ONE_SHOT < TYPE_REPEATING_SLACK);
PR_STATIC_ASSERT(TYPE_REPEATING_SLACK < TYPE_REPEATING_PRECISE);
PR_STATIC_ASSERT(TYPE_REPEATING_PRECISE < TYPE_REPEATING_PRECISE_CAN_SKIP);
return mType >= TYPE_REPEATING_SLACK;
}
bool IsRepeatingPrecisely() const {
bool IsRepeatingPrecisely() const
{
return mType >= TYPE_REPEATING_PRECISE;
}
nsCOMPtr<nsIEventTarget> mEventTarget;
void * mClosure;
void* mClosure;
union CallbackUnion {
union CallbackUnion
{
nsTimerCallbackFunc c;
nsITimerCallback * i;
nsIObserver * o;
nsITimerCallback* i;
nsIObserver* o;
} mCallback;
// Some callers expect to be able to access the callback while the