mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1059572 - Part 1: Move PostTimerEvent to TimerThread to allow TimerThread's monitor to protect it. r=nfroyd
This commit is contained in:
parent
85218cb3b2
commit
b3903a6fc7
@ -8,6 +8,7 @@
|
||||
#include "TimerThread.h"
|
||||
|
||||
#include "nsThreadUtils.h"
|
||||
#include "plarena.h"
|
||||
#include "pratom.h"
|
||||
|
||||
#include "nsIObserverService.h"
|
||||
@ -80,6 +81,200 @@ TimerObserverRunnable::Run()
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
|
||||
// TimerEventAllocator is a thread-safe allocator used only for nsTimerEvents.
|
||||
// It's needed to avoid contention over the default allocator lock when
|
||||
// firing timer events (see bug 733277). The thread-safety is required because
|
||||
// nsTimerEvent objects are allocated on the timer thread, and freed on another
|
||||
// thread. Because TimerEventAllocator has its own lock, contention over that
|
||||
// lock is limited to the allocation and deallocation of nsTimerEvent objects.
|
||||
//
|
||||
// Because this allocator is layered over PLArenaPool, it never shrinks -- even
|
||||
// "freed" nsTimerEvents aren't truly freed, they're just put onto a free-list
|
||||
// for later recycling. So the amount of memory consumed will always be equal
|
||||
// to the high-water mark consumption. But nsTimerEvents are small and it's
|
||||
// unusual to have more than a few hundred of them, so this shouldn't be a
|
||||
// problem in practice.
|
||||
|
||||
class TimerEventAllocator
|
||||
{
|
||||
private:
|
||||
struct FreeEntry
|
||||
{
|
||||
FreeEntry* mNext;
|
||||
};
|
||||
|
||||
PLArenaPool mPool;
|
||||
FreeEntry* mFirstFree;
|
||||
mozilla::Monitor mMonitor;
|
||||
|
||||
public:
|
||||
TimerEventAllocator()
|
||||
: mFirstFree(nullptr)
|
||||
, mMonitor("TimerEventAllocator")
|
||||
{
|
||||
PL_InitArenaPool(&mPool, "TimerEventPool", 4096, /* align = */ 0);
|
||||
}
|
||||
|
||||
~TimerEventAllocator()
|
||||
{
|
||||
PL_FinishArenaPool(&mPool);
|
||||
}
|
||||
|
||||
void* Alloc(size_t aSize);
|
||||
void Free(void* aPtr);
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
class nsTimerEvent : public nsRunnable
|
||||
{
|
||||
public:
|
||||
NS_IMETHOD Run();
|
||||
|
||||
nsTimerEvent()
|
||||
: mTimer()
|
||||
, mGeneration(0)
|
||||
{
|
||||
MOZ_COUNT_CTOR(nsTimerEvent);
|
||||
|
||||
// Note: We override operator new for this class, and the override is
|
||||
// fallible!
|
||||
sAllocatorUsers++;
|
||||
}
|
||||
|
||||
TimeStamp mInitTime;
|
||||
|
||||
static void Init();
|
||||
static void Shutdown();
|
||||
static void DeleteAllocatorIfNeeded();
|
||||
|
||||
static void* operator new(size_t aSize) CPP_THROW_NEW
|
||||
{
|
||||
return sAllocator->Alloc(aSize);
|
||||
}
|
||||
void operator delete(void* aPtr)
|
||||
{
|
||||
sAllocator->Free(aPtr);
|
||||
DeleteAllocatorIfNeeded();
|
||||
}
|
||||
|
||||
already_AddRefed<nsTimerImpl> ForgetTimer()
|
||||
{
|
||||
return mTimer.forget();
|
||||
}
|
||||
|
||||
void SetTimer(already_AddRefed<nsTimerImpl> aTimer)
|
||||
{
|
||||
mTimer = aTimer;
|
||||
mGeneration = mTimer->GetGeneration();
|
||||
}
|
||||
|
||||
private:
|
||||
~nsTimerEvent()
|
||||
{
|
||||
MOZ_COUNT_DTOR(nsTimerEvent);
|
||||
|
||||
MOZ_ASSERT(!sCanDeleteAllocator || sAllocatorUsers > 0,
|
||||
"This will result in us attempting to deallocate the nsTimerEvent allocator twice");
|
||||
sAllocatorUsers--;
|
||||
}
|
||||
|
||||
nsRefPtr<nsTimerImpl> mTimer;
|
||||
int32_t mGeneration;
|
||||
|
||||
static TimerEventAllocator* sAllocator;
|
||||
static Atomic<int32_t> sAllocatorUsers;
|
||||
static bool sCanDeleteAllocator;
|
||||
};
|
||||
|
||||
TimerEventAllocator* nsTimerEvent::sAllocator = nullptr;
|
||||
Atomic<int32_t> nsTimerEvent::sAllocatorUsers;
|
||||
bool nsTimerEvent::sCanDeleteAllocator = false;
|
||||
|
||||
namespace {
|
||||
|
||||
void*
|
||||
TimerEventAllocator::Alloc(size_t aSize)
|
||||
{
|
||||
MOZ_ASSERT(aSize == sizeof(nsTimerEvent));
|
||||
|
||||
mozilla::MonitorAutoLock lock(mMonitor);
|
||||
|
||||
void* p;
|
||||
if (mFirstFree) {
|
||||
p = mFirstFree;
|
||||
mFirstFree = mFirstFree->mNext;
|
||||
} else {
|
||||
PL_ARENA_ALLOCATE(p, &mPool, aSize);
|
||||
if (!p) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
void
|
||||
TimerEventAllocator::Free(void* aPtr)
|
||||
{
|
||||
mozilla::MonitorAutoLock lock(mMonitor);
|
||||
|
||||
FreeEntry* entry = reinterpret_cast<FreeEntry*>(aPtr);
|
||||
|
||||
entry->mNext = mFirstFree;
|
||||
mFirstFree = entry;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void
|
||||
nsTimerEvent::Init()
|
||||
{
|
||||
sAllocator = new TimerEventAllocator();
|
||||
}
|
||||
|
||||
void
|
||||
nsTimerEvent::Shutdown()
|
||||
{
|
||||
sCanDeleteAllocator = true;
|
||||
DeleteAllocatorIfNeeded();
|
||||
}
|
||||
|
||||
void
|
||||
nsTimerEvent::DeleteAllocatorIfNeeded()
|
||||
{
|
||||
if (sCanDeleteAllocator && sAllocatorUsers == 0) {
|
||||
delete sAllocator;
|
||||
sAllocator = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsTimerEvent::Run()
|
||||
{
|
||||
if (mGeneration != mTimer->GetGeneration()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (MOZ_LOG_TEST(GetTimerLog(), LogLevel::Debug)) {
|
||||
TimeStamp now = TimeStamp::Now();
|
||||
MOZ_LOG(GetTimerLog(), LogLevel::Debug,
|
||||
("[this=%p] time between PostTimerEvent() and Fire(): %fms\n",
|
||||
this, (now - mInitTime).ToMilliseconds()));
|
||||
}
|
||||
|
||||
mTimer->Fire();
|
||||
// Since nsTimerImpl is not thread-safe, we should release |mTimer|
|
||||
// here in the target thread to avoid race condition. Otherwise,
|
||||
// ~nsTimerEvent() which calls nsTimerImpl::Release() could run in the
|
||||
// timer thread and result in race condition.
|
||||
mTimer = nullptr;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
TimerThread::Init()
|
||||
{
|
||||
@ -94,6 +289,8 @@ TimerThread::Init()
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsTimerEvent::Init();
|
||||
|
||||
if (mInitInProgress.exchange(true) == false) {
|
||||
// We hold on to mThread to keep the thread alive.
|
||||
nsresult rv = NS_NewThread(getter_AddRefs(mThread), this);
|
||||
@ -168,6 +365,8 @@ TimerThread::Shutdown()
|
||||
|
||||
mThread->Shutdown(); // wait for the thread to die
|
||||
|
||||
nsTimerEvent::Shutdown();
|
||||
|
||||
MOZ_LOG(GetTimerLog(), LogLevel::Debug, ("TimerThread::Shutdown end\n"));
|
||||
return NS_OK;
|
||||
}
|
||||
@ -265,15 +464,10 @@ TimerThread::Run()
|
||||
("Timer thread woke up %fms from when it was supposed to\n",
|
||||
fabs((now - timerRef->mTimeout).ToMilliseconds())));
|
||||
|
||||
{
|
||||
// We release mMonitor around the Fire call to avoid deadlock.
|
||||
MonitorAutoUnlock unlock(mMonitor);
|
||||
|
||||
// We are going to let the call to PostTimerEvent here handle the
|
||||
// release of the timer so that we don't end up releasing the timer
|
||||
// on the TimerThread instead of on the thread it targets.
|
||||
timerRef = nsTimerImpl::PostTimerEvent(timerRef.forget());
|
||||
}
|
||||
// We are going to let the call to PostTimerEvent here handle the
|
||||
// release of the timer so that we don't end up releasing the timer
|
||||
// on the TimerThread instead of on the thread it targets.
|
||||
timerRef = PostTimerEvent(timerRef.forget());
|
||||
|
||||
if (timerRef) {
|
||||
// We got our reference back due to an error.
|
||||
@ -487,6 +681,75 @@ TimerThread::ReleaseTimerInternal(nsTimerImpl* aTimer)
|
||||
NS_RELEASE(aTimer);
|
||||
}
|
||||
|
||||
already_AddRefed<nsTimerImpl>
|
||||
TimerThread::PostTimerEvent(already_AddRefed<nsTimerImpl> aTimerRef)
|
||||
{
|
||||
mMonitor.AssertCurrentThreadOwns();
|
||||
|
||||
nsRefPtr<nsTimerImpl> timer(aTimerRef);
|
||||
if (!timer->mEventTarget) {
|
||||
NS_ERROR("Attempt to post timer event to NULL event target");
|
||||
return timer.forget();
|
||||
}
|
||||
|
||||
// XXX we may want to reuse this nsTimerEvent in the case of repeating timers.
|
||||
|
||||
// Since we already addref'd 'timer', we don't need to addref here.
|
||||
// We will release either in ~nsTimerEvent(), or pass the reference back to
|
||||
// the caller. We need to copy the generation number from this timer into the
|
||||
// event, so we can avoid firing a timer that was re-initialized after being
|
||||
// canceled.
|
||||
|
||||
nsRefPtr<nsTimerEvent> event = new nsTimerEvent;
|
||||
if (!event) {
|
||||
return timer.forget();
|
||||
}
|
||||
|
||||
if (MOZ_LOG_TEST(GetTimerLog(), LogLevel::Debug)) {
|
||||
event->mInitTime = TimeStamp::Now();
|
||||
}
|
||||
|
||||
// If this is a repeating precise timer, we need to calculate the time for
|
||||
// the next timer to fire before we make the callback.
|
||||
if (timer->IsRepeatingPrecisely()) {
|
||||
timer->SetDelayInternal(timer->mDelay);
|
||||
|
||||
// But only re-arm REPEATING_PRECISE timers.
|
||||
if (timer->mType == nsTimerImpl::TYPE_REPEATING_PRECISE) {
|
||||
if (AddTimerInternal(timer) == -1) {
|
||||
return timer.forget();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef MOZ_TASK_TRACER
|
||||
// During the dispatch of TimerEvent, we overwrite the current TraceInfo
|
||||
// partially with the info saved in timer earlier, and restore it back by
|
||||
// AutoSaveCurTraceInfo.
|
||||
AutoSaveCurTraceInfo saveCurTraceInfo;
|
||||
(timer->GetTracedTask()).SetTLSTraceInfo();
|
||||
#endif
|
||||
|
||||
nsIEventTarget* target = timer->mEventTarget;
|
||||
event->SetTimer(timer.forget());
|
||||
|
||||
nsresult rv;
|
||||
{
|
||||
// We release mMonitor around the Dispatch because if this timer is targeted
|
||||
// at the TimerThread we'll deadlock.
|
||||
MonitorAutoUnlock unlock(mMonitor);
|
||||
rv = target->Dispatch(event, NS_DISPATCH_NORMAL);
|
||||
}
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
timer = event->ForgetTimer();
|
||||
RemoveTimerInternal(timer);
|
||||
return timer.forget();
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
TimerThread::DoBeforeSleep()
|
||||
{
|
||||
|
@ -68,6 +68,8 @@ private:
|
||||
bool RemoveTimerInternal(nsTimerImpl* aTimer);
|
||||
void ReleaseTimerInternal(nsTimerImpl* aTimer);
|
||||
|
||||
already_AddRefed<nsTimerImpl> PostTimerEvent(already_AddRefed<nsTimerImpl> aTimerRef);
|
||||
|
||||
nsCOMPtr<nsIThread> mThread;
|
||||
Monitor mMonitor;
|
||||
|
||||
|
@ -9,7 +9,6 @@
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsThreadManager.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "plarena.h"
|
||||
#include "pratom.h"
|
||||
#include "GeckoProfiler.h"
|
||||
#include "mozilla/Atomics.h"
|
||||
@ -66,155 +65,6 @@ myNS_MeanAndStdDev(double n, double sumOfValues, double sumOfSquaredValues,
|
||||
*stdDevResult = stdDev;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
// TimerEventAllocator is a thread-safe allocator used only for nsTimerEvents.
|
||||
// It's needed to avoid contention over the default allocator lock when
|
||||
// firing timer events (see bug 733277). The thread-safety is required because
|
||||
// nsTimerEvent objects are allocated on the timer thread, and freed on another
|
||||
// thread. Because TimerEventAllocator has its own lock, contention over that
|
||||
// lock is limited to the allocation and deallocation of nsTimerEvent objects.
|
||||
//
|
||||
// Because this allocator is layered over PLArenaPool, it never shrinks -- even
|
||||
// "freed" nsTimerEvents aren't truly freed, they're just put onto a free-list
|
||||
// for later recycling. So the amount of memory consumed will always be equal
|
||||
// to the high-water mark consumption. But nsTimerEvents are small and it's
|
||||
// unusual to have more than a few hundred of them, so this shouldn't be a
|
||||
// problem in practice.
|
||||
|
||||
class TimerEventAllocator
|
||||
{
|
||||
private:
|
||||
struct FreeEntry
|
||||
{
|
||||
FreeEntry* mNext;
|
||||
};
|
||||
|
||||
PLArenaPool mPool;
|
||||
FreeEntry* mFirstFree;
|
||||
mozilla::Monitor mMonitor;
|
||||
|
||||
public:
|
||||
TimerEventAllocator()
|
||||
: mFirstFree(nullptr)
|
||||
, mMonitor("TimerEventAllocator")
|
||||
{
|
||||
PL_InitArenaPool(&mPool, "TimerEventPool", 4096, /* align = */ 0);
|
||||
}
|
||||
|
||||
~TimerEventAllocator()
|
||||
{
|
||||
PL_FinishArenaPool(&mPool);
|
||||
}
|
||||
|
||||
void* Alloc(size_t aSize);
|
||||
void Free(void* aPtr);
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
class nsTimerEvent : public nsRunnable
|
||||
{
|
||||
public:
|
||||
NS_IMETHOD Run();
|
||||
|
||||
nsTimerEvent()
|
||||
: mTimer()
|
||||
, mGeneration(0)
|
||||
{
|
||||
MOZ_COUNT_CTOR(nsTimerEvent);
|
||||
|
||||
MOZ_ASSERT(gThread->IsOnTimerThread(),
|
||||
"nsTimer must always be allocated on the timer thread");
|
||||
|
||||
sAllocatorUsers++;
|
||||
}
|
||||
|
||||
TimeStamp mInitTime;
|
||||
|
||||
static void Init();
|
||||
static void Shutdown();
|
||||
static void DeleteAllocatorIfNeeded();
|
||||
|
||||
static void* operator new(size_t aSize) CPP_THROW_NEW
|
||||
{
|
||||
return sAllocator->Alloc(aSize);
|
||||
}
|
||||
void operator delete(void* aPtr)
|
||||
{
|
||||
sAllocator->Free(aPtr);
|
||||
DeleteAllocatorIfNeeded();
|
||||
}
|
||||
|
||||
already_AddRefed<nsTimerImpl> ForgetTimer()
|
||||
{
|
||||
return mTimer.forget();
|
||||
}
|
||||
|
||||
void SetTimer(already_AddRefed<nsTimerImpl> aTimer)
|
||||
{
|
||||
mTimer = aTimer;
|
||||
mGeneration = mTimer->GetGeneration();
|
||||
}
|
||||
|
||||
private:
|
||||
~nsTimerEvent()
|
||||
{
|
||||
MOZ_COUNT_DTOR(nsTimerEvent);
|
||||
|
||||
MOZ_ASSERT(!sCanDeleteAllocator || sAllocatorUsers > 0,
|
||||
"This will result in us attempting to deallocate the nsTimerEvent allocator twice");
|
||||
sAllocatorUsers--;
|
||||
}
|
||||
|
||||
nsRefPtr<nsTimerImpl> mTimer;
|
||||
int32_t mGeneration;
|
||||
|
||||
static TimerEventAllocator* sAllocator;
|
||||
static Atomic<int32_t> sAllocatorUsers;
|
||||
static bool sCanDeleteAllocator;
|
||||
};
|
||||
|
||||
TimerEventAllocator* nsTimerEvent::sAllocator = nullptr;
|
||||
Atomic<int32_t> nsTimerEvent::sAllocatorUsers;
|
||||
bool nsTimerEvent::sCanDeleteAllocator = false;
|
||||
|
||||
namespace {
|
||||
|
||||
void*
|
||||
TimerEventAllocator::Alloc(size_t aSize)
|
||||
{
|
||||
MOZ_ASSERT(aSize == sizeof(nsTimerEvent));
|
||||
|
||||
mozilla::MonitorAutoLock lock(mMonitor);
|
||||
|
||||
void* p;
|
||||
if (mFirstFree) {
|
||||
p = mFirstFree;
|
||||
mFirstFree = mFirstFree->mNext;
|
||||
} else {
|
||||
PL_ARENA_ALLOCATE(p, &mPool, aSize);
|
||||
if (!p) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
void
|
||||
TimerEventAllocator::Free(void* aPtr)
|
||||
{
|
||||
mozilla::MonitorAutoLock lock(mMonitor);
|
||||
|
||||
FreeEntry* entry = reinterpret_cast<FreeEntry*>(aPtr);
|
||||
|
||||
entry->mNext = mFirstFree;
|
||||
mFirstFree = entry;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
NS_IMPL_QUERY_INTERFACE(nsTimerImpl, nsITimer)
|
||||
NS_IMPL_ADDREF(nsTimerImpl)
|
||||
|
||||
@ -302,8 +152,6 @@ nsTimerImpl::Startup()
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
nsTimerEvent::Init();
|
||||
|
||||
gThread = new TimerThread();
|
||||
|
||||
NS_ADDREF(gThread);
|
||||
@ -336,8 +184,6 @@ nsTimerImpl::Shutdown()
|
||||
|
||||
gThread->Shutdown();
|
||||
NS_RELEASE(gThread);
|
||||
|
||||
nsTimerEvent::Shutdown();
|
||||
}
|
||||
|
||||
|
||||
@ -661,117 +507,6 @@ nsTimerImpl::Fire()
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsTimerEvent::Init()
|
||||
{
|
||||
sAllocator = new TimerEventAllocator();
|
||||
}
|
||||
|
||||
void
|
||||
nsTimerEvent::Shutdown()
|
||||
{
|
||||
sCanDeleteAllocator = true;
|
||||
DeleteAllocatorIfNeeded();
|
||||
}
|
||||
|
||||
void
|
||||
nsTimerEvent::DeleteAllocatorIfNeeded()
|
||||
{
|
||||
if (sCanDeleteAllocator && sAllocatorUsers == 0) {
|
||||
delete sAllocator;
|
||||
sAllocator = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsTimerEvent::Run()
|
||||
{
|
||||
if (mGeneration != mTimer->GetGeneration()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (MOZ_LOG_TEST(GetTimerLog(), LogLevel::Debug)) {
|
||||
TimeStamp now = TimeStamp::Now();
|
||||
MOZ_LOG(GetTimerLog(), LogLevel::Debug,
|
||||
("[this=%p] time between PostTimerEvent() and Fire(): %fms\n",
|
||||
this, (now - mInitTime).ToMilliseconds()));
|
||||
}
|
||||
|
||||
mTimer->Fire();
|
||||
// Since nsTimerImpl is not thread-safe, we should release |mTimer|
|
||||
// here in the target thread to avoid race condition. Otherwise,
|
||||
// ~nsTimerEvent() which calls nsTimerImpl::Release() could run in the
|
||||
// timer thread and result in race condition.
|
||||
mTimer = nullptr;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
already_AddRefed<nsTimerImpl>
|
||||
nsTimerImpl::PostTimerEvent(already_AddRefed<nsTimerImpl> aTimerRef)
|
||||
{
|
||||
nsRefPtr<nsTimerImpl> timer(aTimerRef);
|
||||
if (!timer->mEventTarget) {
|
||||
NS_ERROR("Attempt to post timer event to NULL event target");
|
||||
return timer.forget();
|
||||
}
|
||||
|
||||
// XXX we may want to reuse this nsTimerEvent in the case of repeating timers.
|
||||
|
||||
// Since TimerThread addref'd 'timer' for us, we don't need to addref here.
|
||||
// We will release either in ~nsTimerEvent(), or pass the reference back to
|
||||
// the caller. We need to copy the generation number from this timer into the
|
||||
// event, so we can avoid firing a timer that was re-initialized after being
|
||||
// canceled.
|
||||
|
||||
// Note: We override operator new for this class, and the override is
|
||||
// fallible!
|
||||
nsRefPtr<nsTimerEvent> event = new nsTimerEvent;
|
||||
if (!event) {
|
||||
return timer.forget();
|
||||
}
|
||||
|
||||
if (MOZ_LOG_TEST(GetTimerLog(), LogLevel::Debug)) {
|
||||
event->mInitTime = TimeStamp::Now();
|
||||
}
|
||||
|
||||
// If this is a repeating precise timer, we need to calculate the time for
|
||||
// the next timer to fire before we make the callback.
|
||||
if (timer->IsRepeatingPrecisely()) {
|
||||
timer->SetDelayInternal(timer->mDelay);
|
||||
|
||||
// But only re-arm REPEATING_PRECISE timers.
|
||||
if (gThread && timer->mType == TYPE_REPEATING_PRECISE) {
|
||||
nsresult rv = gThread->AddTimer(timer);
|
||||
if (NS_FAILED(rv)) {
|
||||
return timer.forget();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef MOZ_TASK_TRACER
|
||||
// During the dispatch of TimerEvent, we overwrite the current TraceInfo
|
||||
// partially with the info saved in timer earlier, and restore it back by
|
||||
// AutoSaveCurTraceInfo.
|
||||
AutoSaveCurTraceInfo saveCurTraceInfo;
|
||||
(timer->GetTracedTask()).SetTLSTraceInfo();
|
||||
#endif
|
||||
|
||||
nsIEventTarget* target = timer->mEventTarget;
|
||||
event->SetTimer(timer.forget());
|
||||
|
||||
nsresult rv = target->Dispatch(event, NS_DISPATCH_NORMAL);
|
||||
if (NS_FAILED(rv)) {
|
||||
timer = event->ForgetTimer();
|
||||
if (gThread) {
|
||||
gThread->RemoveTimer(timer);
|
||||
}
|
||||
return timer.forget();
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
nsTimerImpl::SetDelayInternal(uint32_t aDelay)
|
||||
{
|
||||
|
@ -42,8 +42,8 @@ public:
|
||||
static void Shutdown();
|
||||
|
||||
friend class TimerThread;
|
||||
friend struct TimerAdditionComparator;
|
||||
friend class nsTimerEvent;
|
||||
friend struct TimerAdditionComparator;
|
||||
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSITIMER
|
||||
|
Loading…
Reference in New Issue
Block a user