Revert "Compiles and does not crash."

This reverts commit 48ba811bd6bed8b75d223eddf74c0785e7e81efd.
This commit is contained in:
win7-7 2026-01-28 18:02:18 +02:00 committed by wuggy
commit 3bbee255fd
6 changed files with 598 additions and 0 deletions

View file

@ -1102,6 +1102,20 @@ public:
nsCycleCollector_doDeferredDeletion();
}
nsresult ScheduleTimerForThread(nsITimer* aTimer,
nsICancelableRunnable* aRunnable,
uint32_t aDelay) override
{
NS_ENSURE_STATE(mWorkerPrivate);
RefPtr<ExternalRunnableWrapper> wrapper =
new ExternalRunnableWrapper(mWorkerPrivate, aRunnable);
RefPtr<TimerThreadEventTarget> target =
new TimerThreadEventTarget(mWorkerPrivate, wrapper);
aTimer->SetTarget(target);
return aTimer->InitWithFuncCallback(DummyCallback, nullptr, aDelay,
nsITimer::TYPE_ONE_SHOT);
}
virtual void CustomGCCallback(JSGCStatus aStatus) override
{
if (!mWorkerPrivate) {

View file

@ -682,6 +682,38 @@ XPCJSContext::DispatchDeferredDeletion(bool aContinuation, bool aPurge)
mAsyncSnowWhiteFreer->Dispatch(aContinuation, aPurge);
}
class TimerCallbackForRunnable : public nsITimerCallback
{
public:
TimerCallbackForRunnable(nsIRunnable* aRunnable)
: mRunnable(aRunnable)
{}
NS_DECL_ISUPPORTS
NS_IMETHOD Notify(nsITimer* aTimer) final
{
mRunnable->Run();
return NS_OK;
}
nsCOMPtr<nsIRunnable> mRunnable;
private:
virtual ~TimerCallbackForRunnable() {}
};
NS_IMPL_ISUPPORTS(TimerCallbackForRunnable, nsITimerCallback)
nsresult
XPCJSContext::ScheduleTimerForThread(nsITimer* aTimer,
nsICancelableRunnable* aRunnable,
uint32_t aDelay)
{
nsCOMPtr<nsITimerCallback> callback =
new TimerCallbackForRunnable(aRunnable);
aTimer->InitWithCallback(callback, aDelay, nsITimer::TYPE_ONE_SHOT);
return NS_OK;
}
void
xpc_UnmarkSkippableJSHolders()
{

View file

@ -528,6 +528,9 @@ public:
void BeginCycleCollectionCallback() override;
void EndCycleCollectionCallback(mozilla::CycleCollectorResults& aResults) override;
void DispatchDeferredDeletion(bool aContinuation, bool aPurge = false) override;
nsresult ScheduleTimerForThread(nsITimer* aTimer,
nsICancelableRunnable* aRunnable,
uint32_t aDelay) override;
void CustomGCCallback(JSGCStatus status) override;
void CustomOutOfMemoryCallback() override;

View file

@ -368,6 +368,27 @@ public:
// isn't one.
static CycleCollectedJSContext* Get();
static SlowAsynchronousTaskScheduler* GetScheduler()
{
CycleCollectedJSContext* context = Get();
return context ? &context->mSATS : nullptr;
}
// Because of our Web Worker setup being broken, we need to specially initiate
// timers for Worker threads.
static nsresult ScheduleTimer(nsITimer* aTimer,
nsICancelableRunnable* aRunnable,
uint32_t aDelay)
{
CycleCollectedJSContext* context = Get();
NS_ENSURE_STATE(context);
return context->ScheduleTimerForThread(aTimer, aRunnable, aDelay);
}
virtual nsresult ScheduleTimerForThread(nsITimer* aTimer,
nsICancelableRunnable* aRunnable,
uint32_t aDelay) = 0;
// Add aZone to the set of zones waiting for a GC.
void AddZoneWaitingForGC(JS::Zone* aZone)
{

View file

@ -0,0 +1,371 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "SlowAsynchronousTaskScheduler.h"
#include "mozilla/TimeStamp.h"
#include "nsComponentManagerUtils.h"
//#define DEBUG_SATS 1
using namespace mozilla;
// If we're expecting a tick to happen soon, don't use timer based tick.
static const uint32_t kExpectedTimeOverlapLimit = 10;
SlowAsynchronousTaskScheduler::SlowAsynchronousTaskScheduler()
: mShuttingDown(false)
{
}
SlowAsynchronousTaskScheduler::~SlowAsynchronousTaskScheduler()
{
MOZ_ASSERT(!mTimer);
while (ScheduledSlowTask* task = mScheduledSlowTasks.popFirst()) {
delete task;
}
}
void
SlowAsynchronousTaskScheduler::ExpectedTick(uint32_t aMillisecondsFromNow)
{
TimeStamp newExpectedTick = FromNow(aMillisecondsFromNow);
if (mExpectedNonTimerTick.IsNull() ||
newExpectedTick < mExpectedNonTimerTick) {
mExpectedNonTimerTick = newExpectedTick;
}
}
void
SlowAsynchronousTaskScheduler::Tick(bool aFromTimer)
{
CancelTimer();
if (mScheduledSlowTasks.isEmpty()) {
if (!aFromTimer) {
mExpectedNonTimerTick = TimeStamp();
}
return;
}
TimeStamp now = TimeStamp::Now();
TimeDuration limit =
TimeDuration::FromMilliseconds(
static_cast<double>(kExpectedTimeOverlapLimit));
// We have a tick from timer, but we're possibly waiting for a non-timer
// tick happening real soon. If so, don't handle this tick.
if (aFromTimer && !mExpectedNonTimerTick.IsNull() &&
now < mExpectedNonTimerTick &&
now + limit > mExpectedNonTimerTick) {
// Try to run the timer right after the expected tick, since
// the expected tick might get lost.
uint32_t newTimerValue =
static_cast<uint32_t>((mExpectedNonTimerTick - now).ToMilliseconds()) + 1;
EnsureTimer(newTimerValue);
return;
}
mExpectedNonTimerTick = TimeStamp();
ScheduledSlowTask* current = mScheduledSlowTasks.popFirst();
if (!current) {
return;
}
if (current->mRunnable) {
#ifdef DEBUG_SATS
printf("SlowAsynchronousTaskScheduler::Tick[timer=%s], runnable \n",
aFromTimer ? "true" : "false");
#endif
nsCOMPtr<nsIRunnable> runnable;
runnable.swap(current->mRunnable);
delete current;
current = nullptr;
runnable->Run();
} else {
#ifdef DEBUG_SATS
printf("SlowAsynchronousTaskScheduler::Tick[timer=%s] task=%p ",
aFromTimer ? "true" : "false", current->mSlowTasks);
for (uint32_t i = 0; current->mSlowTasks[i].mCallback; ++i) {
printf("[%u]", current->mSlowTasks[i].mID);
}
#endif
DependentSlowTask& task =
current->mSlowTasks[current->mIndexOfNextTask];
#ifdef DEBUG_SATS
printf(", will run id=%u\n", task.mID);
#endif
// Run the currently scheduled task, and store state of the next task.
void* data = current->mDataForNextTask;
current->mDataForNextTask = nullptr;
SATSState nextState = task.mCallback(task.mID, data);
uint32_t nextID = nextState.mState;
uint32_t i = 0;
for (; current->mSlowTasks[i].mCallback; ++i) {
if (current->mSlowTasks[i].mID == nextID) {
break;
}
}
// The nextID did point to another task which has a callback to run.
// Schedule that.
if (current->mSlowTasks[i].mCallback) {
current->mIndexOfNextTask = i;
current->mDataForNextTask = nextState.mData;
// Reschedule, since it now has new mIndexOfNextTask.
AddScheduledSlowTask(current, current->mSlowTasks[i].mDelayMillis);
} else {
// Couldn't find anything to schedule.
delete current;
}
}
// Make sure we have a timer running if we have something scheduled.
ScheduledSlowTask* first = mScheduledSlowTasks.getFirst();
if (first) {
#ifdef DEBUG_SATS
printf("Ensuring timer for the next task\n");
#endif
uint32_t millis = first->mExpectedTimeToRun > now ?
static_cast<uint32_t>(
(first->mExpectedTimeToRun - now).ToMilliseconds()) :
0;
EnsureTimer(millis);
}
}
void
SlowAsynchronousTaskScheduler::Schedule(nsIRunnable* aRunnable,
uint32_t aMillis)
{
#ifdef DEBUG_SATS
printf("SlowAsynchronousTaskScheduler::Schedule aRunnable\n");
#endif
AddScheduledSlowTask(new ScheduledSlowTask(aRunnable), aMillis);
EnsureTimer(aMillis);
}
void
SlowAsynchronousTaskScheduler::Schedule(DependentSlowTask* aTasks,
void* aData)
{
#ifdef DEBUG_SATS
printf("SlowAsynchronousTaskScheduler::Schedule aTasks ");
for (uint32_t i = 0; aTasks[i].mCallback; ++i) {
printf("[%u]", aTasks[i].mID);
}
printf("\n");
#endif
MOZ_ASSERT(aTasks && aTasks[0].mCallback);
AddScheduledSlowTask(new ScheduledSlowTask(aTasks, 0, aData),
aTasks[0].mDelayMillis);
EnsureTimer(aTasks[0].mDelayMillis);
}
void
SlowAsynchronousTaskScheduler::Schedule(DependentSlowTask* aTasks,
uint32_t aID, void* aData)
{
#ifdef DEBUG_SATS
printf("SlowAsynchronousTaskScheduler::Schedule aTask ");
for (uint32_t i = 0; aTasks[i].mCallback; ++i) {
printf("[%u]", aTasks[i].mID);
}
printf(", scheduling=%u\n", aID);
#endif
for (uint32_t i = 0; aTasks[i].mCallback; ++i) {
if (aTasks[i].mID == aID) {
MOZ_ASSERT(aTasks[i].mCallback);
AddScheduledSlowTask(new ScheduledSlowTask(aTasks, i, aData),
aTasks[i].mDelayMillis);
EnsureTimer(aTasks[i].mDelayMillis);
return;
}
}
MOZ_ASSERT(false, "Unknown ID?");
}
void
SlowAsynchronousTaskScheduler::CancelScheduledTask(nsIRunnable* aRunnable)
{
if (aRunnable) {
ScheduledSlowTask* task = mScheduledSlowTasks.getFirst();
while(task) {
ScheduledSlowTask* next = task->getNext();
if (task->mRunnable == aRunnable) {
task->remove();
delete task;
}
task = next;
}
}
}
void
SlowAsynchronousTaskScheduler::CancelScheduledTask(DependentSlowTask* aTasks)
{
if (aTasks) {
ScheduledSlowTask* task = mScheduledSlowTasks.getFirst();
while(task) {
ScheduledSlowTask* next = task->getNext();
if (task->mSlowTasks == aTasks) {
task->remove();
delete task;
}
task = next;
}
}
}
void
SlowAsynchronousTaskScheduler::CancelScheduledTask(DependentSlowTask* aTasks,
uint32_t aID)
{
if (aTasks) {
ScheduledSlowTask* task = mScheduledSlowTasks.getFirst();
while(task) {
ScheduledSlowTask* next = task->getNext();
if (task->mSlowTasks == aTasks &&
task->mSlowTasks[task->mIndexOfNextTask].mID == aID) {
task->remove();
delete task;
}
task = next;
}
}
}
bool
SlowAsynchronousTaskScheduler::IsScheduled(nsIRunnable* aRunnable)
{
if (aRunnable) {
ScheduledSlowTask* task = mScheduledSlowTasks.getFirst();
while(task) {
if (task->mRunnable == aRunnable) {
return true;
}
task = task->getNext();
}
}
return false;
}
bool
SlowAsynchronousTaskScheduler::IsScheduled(DependentSlowTask* aTasks)
{
if (aTasks) {
ScheduledSlowTask* task = mScheduledSlowTasks.getFirst();
while(task) {
if (task->mSlowTasks == aTasks) {
return true;
}
task = task->getNext();
}
}
return false;
}
bool
SlowAsynchronousTaskScheduler::IsScheduled(DependentSlowTask* aTasks,
uint32_t aID)
{
if (aTasks) {
ScheduledSlowTask* task = mScheduledSlowTasks.getFirst();
while(task) {
if (task->mSlowTasks == aTasks &&
task->mSlowTasks[task->mIndexOfNextTask].mID == aID) {
return true;
}
task = task->getNext();
}
}
return false;
}
class SATSTimerTick : public CancelableRunnable
{
public:
NS_IMETHOD Run()
{
if (!mCancelled) {
SlowAsynchronousTaskScheduler* sats =
CycleCollectedJSContext::GetScheduler();
if (sats) {
sats->TickFromTimer();
}
}
return NS_OK;
}
NS_IMETHOD Cancel()
{
mCancelled = true;
return NS_OK;
}
bool mCancelled = false;
};
void
SlowAsynchronousTaskScheduler::EnsureTimer(uint32_t aMillis)
{
#ifdef DEBUG_SATS
printf("SlowAsynchronousTaskScheduler::EnsureTimer %ums\n", aMillis);
#endif
TimeStamp fromNow = FromNow(aMillis);
if (mTimer) {
if (!mTimerTime.IsNull() && mTimerTime < fromNow) {
// We will run the timer sooner than aMillis from now.
return;
}
mTimer->Cancel();
} else {
if (mShuttingDown) {
return;
}
mTimer = do_CreateInstance("@mozilla.org/timer;1");
if (!mTimer) {
NS_WARNING("No timer!");
return;
}
mTimerCallback = new SATSTimerTick();
}
mTimerTime = fromNow;
CycleCollectedJSContext::ScheduleTimer(mTimer, mTimerCallback, aMillis);
}
void
SlowAsynchronousTaskScheduler::AddScheduledSlowTask(ScheduledSlowTask* aTask,
uint32_t aMillis)
{
TimeStamp expectedTime = FromNow(aMillis);
ScheduledSlowTask* task = mScheduledSlowTasks.getFirst();
while(task) {
if (task->mExpectedTimeToRun > expectedTime) {
task->setPrevious(aTask);
break;
}
task = task->getNext();
}
if (!task) {
mScheduledSlowTasks.insertBack(aTask);
}
aTask->mExpectedTimeToRun = expectedTime;
}
void
SlowAsynchronousTaskScheduler::CancelTimer()
{
if (mTimer) {
mTimer->Cancel();
mTimerTime = TimeStamp();
}
}

View file

@ -0,0 +1,157 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
#ifndef mozilla_SlowAsynchronousTaskScheduler_h__
#define mozilla_SlowAsynchronousTaskScheduler_h__
#include "nsThreadUtils.h"
#include "nsITimer.h"
#include "mozilla/LinkedList.h"
#include "mozilla/TimeStamp.h"
namespace mozilla {
struct SATSState
{
MOZ_IMPLICIT SATSState(uint32_t aState)
: mState(aState), mData(nullptr)
{}
MOZ_IMPLICIT SATSState(uint32_t aState, void* aData)
: mState(aState), mData(aData)
{}
MOZ_IMPLICIT SATSState(const SATSState& aOther)
: mState(aOther.mState), mData(aOther.mData)
{}
uint32_t mState;
void* mData;
private:
SATSState() = delete;
};
// Returns the ID of the next DependentSlowTask.
typedef SATSState (*SlowTaskCallback)(uint32_t aCurrentID, void* aData);
struct DependentSlowTask
{
uint32_t mID;
uint32_t mDelayMillis;
SlowTaskCallback mCallback;
};
// If one has several tasks which need to run occasionally and those tasks
// depend on the each others, one can pass pointer to an array of
// DependentSlowTasks objects. Each callback returns the ID of the next state.
//
// enum {
// eIDOfState1,
// eIdOfState2,
// eIdOfFinishState
// };
//
// static DependentSlowTask sMySlowTasks[]
// {
// { eIDOfState1, 10000, TriggerState1 },
// { eIdOfState2, 5000, TriggerState2 },
// { eIdOfFinishState, 0, nullptr }
// };
//
// scheduler->Schedule(sMySlowTasks);
class SlowAsynchronousTaskScheduler
{
public:
SlowAsynchronousTaskScheduler();
~SlowAsynchronousTaskScheduler();
// Call ExpectedTick() to give SATS hint when the next non-timer based Tick
// might be called. For example refresh driver could call this.
void ExpectedTick(uint32_t aMillisecondsFromNow);
// Call Tick() to run possible pending scheduled tasks.
void Tick()
{
Tick(false);
}
void TickFromTimer()
{
Tick(true);
}
void Schedule(nsIRunnable* aRunnable, uint32_t aMillis);
void Schedule(DependentSlowTask* aTasks, void* aData = nullptr);
void Schedule(DependentSlowTask* aTasks, uint32_t aID, void* aData = nullptr);
void CancelScheduledTask(nsIRunnable* aRunnable);
void CancelScheduledTask(DependentSlowTask* aTasks);
void CancelScheduledTask(DependentSlowTask* aTasks, uint32_t aID);
bool IsScheduled(nsIRunnable* aRunnable);
bool IsScheduled(DependentSlowTask* aTasks);
bool IsScheduled(DependentSlowTask* aTasks, uint32_t aID);
void Shutdown()
{
CancelTimer();
mTimer = nullptr;
mShuttingDown = true;
}
private:
class ScheduledSlowTask : public LinkedListElement<ScheduledSlowTask>
{
public:
ScheduledSlowTask(DependentSlowTask* aSlowTasks, uint32_t aIndex = 0,
void* aData = nullptr)
: mSlowTasks(aSlowTasks)
, mIndexOfNextTask(aIndex)
, mDataForNextTask(aData)
{
MOZ_COUNT_CTOR(ScheduledSlowTask);
}
ScheduledSlowTask(nsIRunnable* aSlowTask)
: mRunnable(aSlowTask)
, mSlowTasks(nullptr)
, mIndexOfNextTask(0)
, mDataForNextTask(nullptr)
{
MOZ_COUNT_CTOR(ScheduledSlowTask);
}
~ScheduledSlowTask()
{
MOZ_COUNT_DTOR(ScheduledSlowTask);
}
mozilla::TimeStamp mExpectedTimeToRun;
nsCOMPtr<nsIRunnable> mRunnable;
DependentSlowTask* mSlowTasks;
uint32_t mIndexOfNextTask;
void* mDataForNextTask;
};
void Tick(bool aFromTimer);
void AddScheduledSlowTask(ScheduledSlowTask* aTask, uint32_t aMillis);
void EnsureTimer(uint32_t aMillis);
void CancelTimer();
static TimeStamp FromNow(uint32_t aMillis)
{
return TimeStamp::Now() +
TimeDuration::FromMilliseconds(static_cast<double>(aMillis));
}
LinkedList<ScheduledSlowTask> mScheduledSlowTasks;
nsCOMPtr<nsITimer> mTimer;
mozilla::TimeStamp mTimerTime;
nsCOMPtr<nsICancelableRunnable> mTimerCallback;
mozilla::TimeStamp mExpectedNonTimerTick;
bool mShuttingDown;
};
}
#endif