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

View File

@ -55,7 +55,6 @@ class Promise MOZ_FINAL : public nsISupports,
public SupportsWeakPtr<Promise> public SupportsWeakPtr<Promise>
{ {
friend class NativePromiseCallback; friend class NativePromiseCallback;
friend class PromiseResolverMixin;
friend class PromiseResolverTask; friend class PromiseResolverTask;
friend class PromiseTask; friend class PromiseTask;
friend class PromiseReportRejectFeature; friend class PromiseReportRejectFeature;
@ -63,9 +62,7 @@ class Promise MOZ_FINAL : public nsISupports,
friend class PromiseWorkerProxyRunnable; friend class PromiseWorkerProxyRunnable;
friend class RejectPromiseCallback; friend class RejectPromiseCallback;
friend class ResolvePromiseCallback; friend class ResolvePromiseCallback;
friend class ThenableResolverMixin; friend class ThenableResolverTask;
friend class WorkerPromiseResolverTask;
friend class WorkerPromiseTask;
friend class WrapperPromiseCallback; friend class WrapperPromiseCallback;
~Promise(); ~Promise();
@ -192,6 +189,10 @@ private:
mResult = aValue; 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 // This method processes promise's resolve/reject callbacks with promise's
// result. It's executed when the resolver.resolve() or resolver.reject() is // 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 // called or when the promise already has a result and new callbacks are