mirror of
https://github.com/roytam1/palemoon27.git
synced 2026-05-26 14:30:27 +00:00
import changes from `dev' branch of rmottola/Arctic-Fox:
- Bug 1180189 - Fix crash in mozilla::a11y::HTMLTableRowAccessible::GroupPosition, r=MarcoZ (a2ee5f2df8) - Bug 1194859 - crash in mozilla::a11y::ARIAGridCellAccessible::GroupPosition, r=marcoz (879bad005f) - Bug 1194859 - crash in mozilla::a11y::ARIAGridCellAccessible::GroupPosition(), part2, r=marcoz (38193050c0) - comment and style (a3ae06e5e6) - var/let (7b3552e2ba) - var-let and some misspatch fixes (a6d3b0c9cb) - Bug 1141978 - Support rowgroup and colgroup HTML scope, r=marcoz (11eedb88f0) - Bug 1146257 - spanned headers don't work well in our table code, r=marcoz (d9b11584c2) - remove unused MultiCompartmentMatcher (480d895553) - Bug 1168170 - Mark reference counted members of nsTimerImpl::mCallback as MOZ_OWNING_REF. r=froydnj (b84c55460a) - Bug 1178363 - make nsTimerImpl::SetDelayInternal a private method; r=poiru (5f70ed869e) - Bug 1178363 - make nsTimerImpl::Fire a private method; r=poiru (4b6007c376) - Bug 1178363 - make MOZ_TASK_TRACER-dependent bits of nsTimerImpl private; r=poiru (e1f54cde97) - Bug 1178363 - make nsTimerImpl::PostTimerEvent a private method; r=poiru (0a0b71eae3) - Bug 1178363 - make nsTimerImpl::GetGeneration a private method; r=poiru (593d3a3f2d) - Bug 1059572 - Part 0: Fuzz test for timers. r=nfroyd (7b03728aa4) - Bug 1059572 - Part 0.5: Fixes for pre-existing problems in TestTimers. r=nfroyd (e463afc995) - Bug 1059572 - Part 1: Move PostTimerEvent to TimerThread to allow TimerThread's monitor to protect it. r=nfroyd (5e56ce272a) - Bug 1059572 - Part 2: Make absolutely sure a timer is removed before reinitting it. r=nfroyd (7d916a9aa9) - Bug 1190735 - Remove nsITimer.TYPE_REPEATING_PRECISE. r=froydnj. (34f7e4c02e) - Bug 1203427 (part 5) - Add logging of timer firings. r=froydnj. (664954eef7)
This commit is contained in:
@@ -8,6 +8,7 @@
|
||||
#include "TimerThread.h"
|
||||
|
||||
#include "nsThreadUtils.h"
|
||||
#include "plarena.h"
|
||||
#include "pratom.h"
|
||||
|
||||
#include "nsIObserverService.h"
|
||||
@@ -79,6 +80,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()
|
||||
{
|
||||
@@ -93,6 +288,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);
|
||||
@@ -167,6 +364,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;
|
||||
}
|
||||
@@ -262,15 +461,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.
|
||||
@@ -484,6 +678,68 @@ 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. But don't re-arm.
|
||||
if (timer->IsRepeatingPrecisely()) {
|
||||
timer->SetDelayInternal(timer->mDelay);
|
||||
}
|
||||
|
||||
#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()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user