Bug 1055925 - Refactor promise to merge main thread and worker thread tasks into a single task. r=nsm

This commit is contained in:
Yuan Xulei 2014-08-19 18:39:56 +08:00
parent e0bcb0e461
commit 3baf570af3
2 changed files with 93 additions and 189 deletions

View File

@ -51,12 +51,15 @@ public:
protected:
~PromiseTask()
{
NS_ASSERT_OWNINGTHREAD(PromiseTask);
MOZ_COUNT_DTOR(PromiseTask);
}
public:
NS_IMETHOD Run()
NS_IMETHOD
Run() MOZ_OVERRIDE
{
NS_ASSERT_OWNINGTHREAD(PromiseTask);
mPromise->mTaskPending = false;
mPromise->RunTask();
return NS_OK;
@ -64,67 +67,40 @@ public:
private:
nsRefPtr<Promise> mPromise;
NS_DECL_OWNINGTHREAD
};
class WorkerPromiseTask MOZ_FINAL : public WorkerSameThreadRunnable
// This class processes the promise's callbacks with promise's result.
class PromiseResolverTask MOZ_FINAL : public nsRunnable
{
public:
WorkerPromiseTask(WorkerPrivate* aWorkerPrivate, Promise* aPromise)
: WorkerSameThreadRunnable(aWorkerPrivate)
, mPromise(aPromise)
{
MOZ_ASSERT(aPromise);
MOZ_COUNT_CTOR(WorkerPromiseTask);
}
protected:
~WorkerPromiseTask()
{
MOZ_COUNT_DTOR(WorkerPromiseTask);
}
public:
bool
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
{
mPromise->mTaskPending = false;
mPromise->RunTask();
return true;
}
private:
nsRefPtr<Promise> mPromise;
};
class PromiseResolverMixin
{
public:
PromiseResolverMixin(Promise* aPromise,
JS::Handle<JS::Value> aValue,
Promise::PromiseState aState)
PromiseResolverTask(Promise* aPromise,
JS::Handle<JS::Value> aValue,
Promise::PromiseState aState)
: mPromise(aPromise)
, mValue(CycleCollectedJSRuntime::Get()->Runtime(), aValue)
, mState(aState)
{
MOZ_ASSERT(aPromise);
MOZ_ASSERT(mState != Promise::Pending);
MOZ_COUNT_CTOR(PromiseResolverMixin);
MOZ_COUNT_CTOR(PromiseResolverTask);
}
virtual ~PromiseResolverMixin()
virtual
~PromiseResolverTask()
{
NS_ASSERT_OWNINGTHREAD(PromiseResolverMixin);
MOZ_COUNT_DTOR(PromiseResolverMixin);
NS_ASSERT_OWNINGTHREAD(PromiseResolverTask);
MOZ_COUNT_DTOR(PromiseResolverTask);
}
protected:
void
RunInternal()
NS_IMETHOD
Run() MOZ_OVERRIDE
{
NS_ASSERT_OWNINGTHREAD(PromiseResolverMixin);
NS_ASSERT_OWNINGTHREAD(PromiseResolverTask);
mPromise->RunResolveTask(
JS::Handle<JS::Value>::fromMarkedLocation(mValue.address()),
mState, Promise::SyncTask);
return NS_OK;
}
private:
@ -134,50 +110,6 @@ private:
NS_DECL_OWNINGTHREAD;
};
// This class processes the promise's callbacks with promise's result.
class PromiseResolverTask MOZ_FINAL : public nsRunnable,
public PromiseResolverMixin
{
public:
PromiseResolverTask(Promise* aPromise,
JS::Handle<JS::Value> aValue,
Promise::PromiseState aState)
: PromiseResolverMixin(aPromise, aValue, aState)
{}
~PromiseResolverTask()
{}
NS_IMETHOD Run()
{
RunInternal();
return NS_OK;
}
};
class WorkerPromiseResolverTask MOZ_FINAL : public WorkerSameThreadRunnable,
public PromiseResolverMixin
{
public:
WorkerPromiseResolverTask(WorkerPrivate* aWorkerPrivate,
Promise* aPromise,
JS::Handle<JS::Value> aValue,
Promise::PromiseState aState)
: WorkerSameThreadRunnable(aWorkerPrivate),
PromiseResolverMixin(aPromise, aValue, aState)
{}
~WorkerPromiseResolverTask()
{}
bool
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
{
RunInternal();
return true;
}
};
enum {
SLOT_PROMISE = 0,
SLOT_DATA
@ -239,11 +171,12 @@ GetPromise(JSContext* aCx, JS::Handle<JSObject*> aFunc)
}
};
// Main thread runnable to resolve thenables.
// Equivalent to the specification's ResolvePromiseViaThenableTask.
class ThenableResolverMixin
class ThenableResolverTask MOZ_FINAL : public nsRunnable
{
public:
ThenableResolverMixin(Promise* aPromise,
ThenableResolverTask(Promise* aPromise,
JS::Handle<JSObject*> aThenable,
PromiseInit* aThen)
: mPromise(aPromise)
@ -251,25 +184,26 @@ public:
, mThen(aThen)
{
MOZ_ASSERT(aPromise);
MOZ_COUNT_CTOR(ThenableResolverMixin);
MOZ_COUNT_CTOR(ThenableResolverTask);
}
virtual ~ThenableResolverMixin()
virtual
~ThenableResolverTask()
{
NS_ASSERT_OWNINGTHREAD(ThenableResolverMixin);
MOZ_COUNT_DTOR(ThenableResolverMixin);
NS_ASSERT_OWNINGTHREAD(ThenableResolverTask);
MOZ_COUNT_DTOR(ThenableResolverTask);
}
protected:
void
RunInternal()
NS_IMETHOD
Run() MOZ_OVERRIDE
{
NS_ASSERT_OWNINGTHREAD(ThenableResolverMixin);
NS_ASSERT_OWNINGTHREAD(ThenableResolverTask);
ThreadsafeAutoJSContext cx;
JS::Rooted<JSObject*> wrapper(cx, mPromise->GetWrapper());
MOZ_ASSERT(wrapper); // It was preserved!
if (!wrapper) {
return;
return NS_OK;
}
JSAutoCompartment ac(cx, wrapper);
@ -278,14 +212,14 @@ protected:
if (!resolveFunc) {
mPromise->HandleException(cx);
return;
return NS_OK;
}
JS::Rooted<JSObject*> rejectFunc(cx,
mPromise->CreateThenableFunction(cx, mPromise, PromiseCallback::Reject));
if (!rejectFunc) {
mPromise->HandleException(cx);
return;
return NS_OK;
}
LinkThenableCallables(cx, resolveFunc, rejectFunc);
@ -319,6 +253,8 @@ protected:
// the exception. FIXME(nsm): This should be reported to the error
// console though, for debugging.
}
return NS_OK;
}
private:
@ -328,60 +264,6 @@ private:
NS_DECL_OWNINGTHREAD;
};
// Main thread runnable to resolve thenables.
class ThenableResolverTask MOZ_FINAL : public nsRunnable,
public ThenableResolverMixin
{
public:
ThenableResolverTask(Promise* aPromise,
JS::Handle<JSObject*> aThenable,
PromiseInit* aThen)
: ThenableResolverMixin(aPromise, aThenable, aThen)
{
MOZ_ASSERT(NS_IsMainThread());
}
~ThenableResolverTask()
{
MOZ_ASSERT(NS_IsMainThread());
}
NS_IMETHOD Run()
{
MOZ_ASSERT(NS_IsMainThread());
RunInternal();
return NS_OK;
}
};
// Worker thread runnable to resolve thenables.
class WorkerThenableResolverTask MOZ_FINAL : public WorkerSameThreadRunnable,
public ThenableResolverMixin
{
public:
WorkerThenableResolverTask(WorkerPrivate* aWorkerPrivate,
Promise* aPromise,
JS::Handle<JSObject*> aThenable,
PromiseInit* aThen)
: WorkerSameThreadRunnable(aWorkerPrivate),
ThenableResolverMixin(aPromise, aThenable, aThen)
{
MOZ_ASSERT(aWorkerPrivate);
aWorkerPrivate->AssertIsOnWorkerThread();
}
~WorkerThenableResolverTask()
{}
bool
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
{
aWorkerPrivate->AssertIsOnWorkerThread();
RunInternal();
return true;
}
};
// Promise
NS_IMPL_CYCLE_COLLECTION_CLASS(Promise)
@ -1017,19 +899,57 @@ Promise::AppendCallbacks(PromiseCallback* aResolveCallback,
// callbacks with promise's result. If promise's state is rejected, queue a
// task to process our reject callbacks with promise's result.
if (mState != Pending && !mTaskPending) {
if (MOZ_LIKELY(NS_IsMainThread())) {
nsRefPtr<PromiseTask> task = new PromiseTask(this);
NS_DispatchToCurrentThread(task);
} else {
WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
MOZ_ASSERT(worker);
nsRefPtr<WorkerPromiseTask> task = new WorkerPromiseTask(worker, this);
task->Dispatch(worker->GetJSContext());
}
nsRefPtr<PromiseTask> task = new PromiseTask(this);
DispatchToMainOrWorkerThread(task);
mTaskPending = true;
}
}
class WrappedWorkerRunnable MOZ_FINAL : public WorkerSameThreadRunnable
{
public:
WrappedWorkerRunnable(WorkerPrivate* aWorkerPrivate, nsIRunnable* aRunnable)
: WorkerSameThreadRunnable(aWorkerPrivate)
, mRunnable(aRunnable)
{
MOZ_ASSERT(aRunnable);
MOZ_COUNT_CTOR(WrappedWorkerRunnable);
}
bool
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE
{
NS_ASSERT_OWNINGTHREAD(WrappedWorkerRunnable);
mRunnable->Run();
return true;
}
private:
virtual
~WrappedWorkerRunnable()
{
MOZ_COUNT_DTOR(WrappedWorkerRunnable);
NS_ASSERT_OWNINGTHREAD(WrappedWorkerRunnable);
}
nsCOMPtr<nsIRunnable> mRunnable;
NS_DECL_OWNINGTHREAD
};
/* static */ void
Promise::DispatchToMainOrWorkerThread(nsIRunnable* aRunnable)
{
MOZ_ASSERT(aRunnable);
if (NS_IsMainThread()) {
NS_DispatchToCurrentThread(aRunnable);
return;
}
WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
MOZ_ASSERT(worker);
nsRefPtr<WrappedWorkerRunnable> task = new WrappedWorkerRunnable(worker, aRunnable);
task->Dispatch(worker->GetJSContext());
}
void
Promise::RunTask()
{
@ -1170,18 +1090,9 @@ Promise::ResolveInternal(JSContext* aCx,
JS::Rooted<JSObject*> thenObj(aCx, &then.toObject());
nsRefPtr<PromiseInit> thenCallback =
new PromiseInit(thenObj, mozilla::dom::GetIncumbentGlobal());
if (NS_IsMainThread()) {
nsRefPtr<ThenableResolverTask> task =
new ThenableResolverTask(this, valueObj, thenCallback);
NS_DispatchToCurrentThread(task);
} else {
WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
MOZ_ASSERT(worker);
nsRefPtr<WorkerThenableResolverTask> task =
new WorkerThenableResolverTask(worker, this, valueObj, thenCallback);
task->Dispatch(worker->GetJSContext());
}
nsRefPtr<ThenableResolverTask> task =
new ThenableResolverTask(this, valueObj, thenCallback);
DispatchToMainOrWorkerThread(task);
return;
}
}
@ -1214,17 +1125,9 @@ Promise::RunResolveTask(JS::Handle<JS::Value> aValue,
// If the synchronous flag is unset, queue a task to process our
// accept callbacks with value.
if (aAsynchronous == AsyncTask) {
if (MOZ_LIKELY(NS_IsMainThread())) {
nsRefPtr<PromiseResolverTask> task =
new PromiseResolverTask(this, aValue, aState);
NS_DispatchToCurrentThread(task);
} else {
WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
MOZ_ASSERT(worker);
nsRefPtr<WorkerPromiseResolverTask> task =
new WorkerPromiseResolverTask(worker, this, aValue, aState);
task->Dispatch(worker->GetJSContext());
}
nsRefPtr<PromiseResolverTask> task =
new PromiseResolverTask(this, aValue, aState);
DispatchToMainOrWorkerThread(task);
return;
}

View File

@ -55,7 +55,6 @@ class Promise MOZ_FINAL : public nsISupports,
public SupportsWeakPtr<Promise>
{
friend class NativePromiseCallback;
friend class PromiseResolverMixin;
friend class PromiseResolverTask;
friend class PromiseTask;
friend class PromiseReportRejectFeature;
@ -63,9 +62,7 @@ class Promise MOZ_FINAL : public nsISupports,
friend class PromiseWorkerProxyRunnable;
friend class RejectPromiseCallback;
friend class ResolvePromiseCallback;
friend class ThenableResolverMixin;
friend class WorkerPromiseResolverTask;
friend class WorkerPromiseTask;
friend class ThenableResolverTask;
friend class WrapperPromiseCallback;
~Promise();
@ -192,6 +189,10 @@ private:
mResult = aValue;
}
// Queue an async task to current main or worker thread.
static void
DispatchToMainOrWorkerThread(nsIRunnable* aRunnable);
// This method processes promise's resolve/reject callbacks with promise's
// result. It's executed when the resolver.resolve() or resolver.reject() is
// called or when the promise already has a result and new callbacks are