зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1083783 - Move Promise.cpp to a model where settlement immediately queues the invocation of "then" callbacks. r=bz
This commit is contained in:
Родитель
5f68e60bdf
Коммит
63d57bf61d
|
@ -35,78 +35,55 @@ using namespace workers;
|
|||
|
||||
NS_IMPL_ISUPPORTS0(PromiseNativeHandler)
|
||||
|
||||
// PromiseTask
|
||||
|
||||
// This class processes the promise's callbacks with promise's result.
|
||||
class PromiseTask MOZ_FINAL : public nsRunnable
|
||||
class PromiseCallbackTask MOZ_FINAL : public nsRunnable
|
||||
{
|
||||
public:
|
||||
explicit PromiseTask(Promise* aPromise)
|
||||
: mPromise(aPromise)
|
||||
{
|
||||
MOZ_ASSERT(aPromise);
|
||||
MOZ_COUNT_CTOR(PromiseTask);
|
||||
}
|
||||
|
||||
protected:
|
||||
~PromiseTask()
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(PromiseTask);
|
||||
MOZ_COUNT_DTOR(PromiseTask);
|
||||
}
|
||||
|
||||
public:
|
||||
NS_IMETHOD
|
||||
Run() MOZ_OVERRIDE
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(PromiseTask);
|
||||
mPromise->mTaskPending = false;
|
||||
mPromise->RunTask();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
nsRefPtr<Promise> mPromise;
|
||||
NS_DECL_OWNINGTHREAD
|
||||
};
|
||||
|
||||
// This class processes the promise's callbacks with promise's result.
|
||||
class PromiseResolverTask MOZ_FINAL : public nsRunnable
|
||||
{
|
||||
public:
|
||||
PromiseResolverTask(Promise* aPromise,
|
||||
JS::Handle<JS::Value> aValue,
|
||||
Promise::PromiseState aState)
|
||||
PromiseCallbackTask(Promise* aPromise,
|
||||
PromiseCallback* aCallback,
|
||||
const JS::Value& aValue)
|
||||
: mPromise(aPromise)
|
||||
, mCallback(aCallback)
|
||||
, mValue(CycleCollectedJSRuntime::Get()->Runtime(), aValue)
|
||||
, mState(aState)
|
||||
{
|
||||
MOZ_ASSERT(aPromise);
|
||||
MOZ_ASSERT(mState != Promise::Pending);
|
||||
MOZ_COUNT_CTOR(PromiseResolverTask);
|
||||
MOZ_ASSERT(aCallback);
|
||||
MOZ_COUNT_CTOR(PromiseCallbackTask);
|
||||
}
|
||||
|
||||
virtual
|
||||
~PromiseResolverTask()
|
||||
~PromiseCallbackTask()
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(PromiseResolverTask);
|
||||
MOZ_COUNT_DTOR(PromiseResolverTask);
|
||||
NS_ASSERT_OWNINGTHREAD(PromiseCallbackTask);
|
||||
MOZ_COUNT_DTOR(PromiseCallbackTask);
|
||||
}
|
||||
|
||||
protected:
|
||||
NS_IMETHOD
|
||||
Run() MOZ_OVERRIDE
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(PromiseResolverTask);
|
||||
mPromise->RunResolveTask(
|
||||
JS::Handle<JS::Value>::fromMarkedLocation(mValue.address()),
|
||||
mState, Promise::SyncTask);
|
||||
NS_ASSERT_OWNINGTHREAD(PromiseCallbackTask);
|
||||
ThreadsafeAutoJSContext cx;
|
||||
JS::Rooted<JSObject*> wrapper(cx, mPromise->GetWrapper());
|
||||
MOZ_ASSERT(wrapper); // It was preserved!
|
||||
JSAutoCompartment ac(cx, wrapper);
|
||||
|
||||
JS::Rooted<JS::Value> value(cx, mValue);
|
||||
if (!MaybeWrapValue(cx, &value)) {
|
||||
NS_WARNING("Failed to wrap value into the right compartment.");
|
||||
JS_ClearPendingException(cx);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
mCallback->Call(cx, value);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
nsRefPtr<Promise> mPromise;
|
||||
nsRefPtr<PromiseCallback> mCallback;
|
||||
JS::PersistentRooted<JS::Value> mValue;
|
||||
Promise::PromiseState mState;
|
||||
NS_DECL_OWNINGTHREAD;
|
||||
};
|
||||
|
||||
|
@ -202,9 +179,6 @@ protected:
|
|||
ThreadsafeAutoJSContext cx;
|
||||
JS::Rooted<JSObject*> wrapper(cx, mPromise->GetWrapper());
|
||||
MOZ_ASSERT(wrapper); // It was preserved!
|
||||
if (!wrapper) {
|
||||
return NS_OK;
|
||||
}
|
||||
JSAutoCompartment ac(cx, wrapper);
|
||||
|
||||
JS::Rooted<JSObject*> resolveFunc(cx,
|
||||
|
@ -247,7 +221,7 @@ protected:
|
|||
NS_WARNING("Failed to wrap value into the right compartment.");
|
||||
}
|
||||
|
||||
mPromise->RejectInternal(cx, exn, Promise::SyncTask);
|
||||
mPromise->RejectInternal(cx, exn);
|
||||
}
|
||||
// At least one of resolveFunc or rejectFunc have been called, so ignore
|
||||
// the exception. FIXME(nsm): This should be reported to the error
|
||||
|
@ -306,7 +280,6 @@ Promise::Promise(nsIGlobalObject* aGlobal)
|
|||
, mRejectionStack(nullptr)
|
||||
, mFullfillmentStack(nullptr)
|
||||
, mState(Pending)
|
||||
, mTaskPending(false)
|
||||
, mHadRejectCallback(false)
|
||||
, mResolvePending(false)
|
||||
{
|
||||
|
@ -941,13 +914,11 @@ Promise::AppendCallbacks(PromiseCallback* aResolveCallback,
|
|||
RemoveFeature();
|
||||
}
|
||||
|
||||
// If promise's state is resolved, queue a task to process our resolve
|
||||
// If promise's state is fulfilled, queue a task to process our fulfill
|
||||
// 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) {
|
||||
nsRefPtr<PromiseTask> task = new PromiseTask(this);
|
||||
DispatchToMainOrWorkerThread(task);
|
||||
mTaskPending = true;
|
||||
if (mState != Pending) {
|
||||
EnqueueCallbackTasks();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -996,32 +967,6 @@ Promise::DispatchToMainOrWorkerThread(nsIRunnable* aRunnable)
|
|||
task->Dispatch(worker->GetJSContext());
|
||||
}
|
||||
|
||||
void
|
||||
Promise::RunTask()
|
||||
{
|
||||
MOZ_ASSERT(mState != Pending);
|
||||
|
||||
nsTArray<nsRefPtr<PromiseCallback>> callbacks;
|
||||
callbacks.SwapElements(mState == Resolved ? mResolveCallbacks
|
||||
: mRejectCallbacks);
|
||||
mResolveCallbacks.Clear();
|
||||
mRejectCallbacks.Clear();
|
||||
|
||||
ThreadsafeAutoJSContext cx;
|
||||
JS::Rooted<JS::Value> value(cx, mResult);
|
||||
JS::Rooted<JSObject*> wrapper(cx, GetWrapper());
|
||||
MOZ_ASSERT(wrapper); // We preserved it
|
||||
|
||||
JSAutoCompartment ac(cx, wrapper);
|
||||
if (!MaybeWrapValue(cx, &value)) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < callbacks.Length(); ++i) {
|
||||
callbacks[i]->Call(cx, value);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Promise::MaybeReportRejected()
|
||||
{
|
||||
|
@ -1069,26 +1014,24 @@ Promise::MaybeReportRejected()
|
|||
|
||||
void
|
||||
Promise::MaybeResolveInternal(JSContext* aCx,
|
||||
JS::Handle<JS::Value> aValue,
|
||||
PromiseTaskSync aAsynchronous)
|
||||
JS::Handle<JS::Value> aValue)
|
||||
{
|
||||
if (mResolvePending) {
|
||||
return;
|
||||
}
|
||||
|
||||
ResolveInternal(aCx, aValue, aAsynchronous);
|
||||
ResolveInternal(aCx, aValue);
|
||||
}
|
||||
|
||||
void
|
||||
Promise::MaybeRejectInternal(JSContext* aCx,
|
||||
JS::Handle<JS::Value> aValue,
|
||||
PromiseTaskSync aAsynchronous)
|
||||
JS::Handle<JS::Value> aValue)
|
||||
{
|
||||
if (mResolvePending) {
|
||||
return;
|
||||
}
|
||||
|
||||
RejectInternal(aCx, aValue, aAsynchronous);
|
||||
RejectInternal(aCx, aValue);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -1097,14 +1040,13 @@ Promise::HandleException(JSContext* aCx)
|
|||
JS::Rooted<JS::Value> exn(aCx);
|
||||
if (JS_GetPendingException(aCx, &exn)) {
|
||||
JS_ClearPendingException(aCx);
|
||||
RejectInternal(aCx, exn, SyncTask);
|
||||
RejectInternal(aCx, exn);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Promise::ResolveInternal(JSContext* aCx,
|
||||
JS::Handle<JS::Value> aValue,
|
||||
PromiseTaskSync aAsynchronous)
|
||||
JS::Handle<JS::Value> aValue)
|
||||
{
|
||||
mResolvePending = true;
|
||||
|
||||
|
@ -1131,40 +1073,22 @@ Promise::ResolveInternal(JSContext* aCx,
|
|||
}
|
||||
}
|
||||
|
||||
// If the synchronous flag is set, process our resolve callbacks with
|
||||
// value. Otherwise, the synchronous flag is unset, queue a task to process
|
||||
// own resolve callbacks with value. Otherwise, the synchronous flag is
|
||||
// unset, queue a task to process our resolve callbacks with value.
|
||||
RunResolveTask(aValue, Resolved, aAsynchronous);
|
||||
MaybeSettle(aValue, Resolved);
|
||||
}
|
||||
|
||||
void
|
||||
Promise::RejectInternal(JSContext* aCx,
|
||||
JS::Handle<JS::Value> aValue,
|
||||
PromiseTaskSync aAsynchronous)
|
||||
JS::Handle<JS::Value> aValue)
|
||||
{
|
||||
mResolvePending = true;
|
||||
|
||||
// If the synchronous flag is set, process our reject callbacks with
|
||||
// value. Otherwise, the synchronous flag is unset, queue a task to process
|
||||
// promise's reject callbacks with value.
|
||||
RunResolveTask(aValue, Rejected, aAsynchronous);
|
||||
MaybeSettle(aValue, Rejected);
|
||||
}
|
||||
|
||||
void
|
||||
Promise::RunResolveTask(JS::Handle<JS::Value> aValue,
|
||||
PromiseState aState,
|
||||
PromiseTaskSync aAsynchronous)
|
||||
Promise::MaybeSettle(JS::Handle<JS::Value> aValue,
|
||||
PromiseState aState)
|
||||
{
|
||||
// If the synchronous flag is unset, queue a task to process our
|
||||
// accept callbacks with value.
|
||||
if (aAsynchronous == AsyncTask) {
|
||||
nsRefPtr<PromiseResolverTask> task =
|
||||
new PromiseResolverTask(this, aValue, aState);
|
||||
DispatchToMainOrWorkerThread(task);
|
||||
return;
|
||||
}
|
||||
|
||||
// Promise.all() or Promise.race() implementations will repeatedly call
|
||||
// Resolve/RejectInternal rather than using the Maybe... forms. Stop SetState
|
||||
// from asserting.
|
||||
|
@ -1195,7 +1119,23 @@ Promise::RunResolveTask(JS::Handle<JS::Value> aValue,
|
|||
}
|
||||
}
|
||||
|
||||
RunTask();
|
||||
EnqueueCallbackTasks();
|
||||
}
|
||||
|
||||
void
|
||||
Promise::EnqueueCallbackTasks()
|
||||
{
|
||||
nsTArray<nsRefPtr<PromiseCallback>> callbacks;
|
||||
callbacks.SwapElements(mState == Resolved ? mResolveCallbacks
|
||||
: mRejectCallbacks);
|
||||
mResolveCallbacks.Clear();
|
||||
mRejectCallbacks.Clear();
|
||||
|
||||
for (uint32_t i = 0; i < callbacks.Length(); ++i) {
|
||||
nsRefPtr<PromiseCallbackTask> task =
|
||||
new PromiseCallbackTask(this, callbacks[i], mResult);
|
||||
DispatchToMainOrWorkerThread(task);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -1295,8 +1235,7 @@ public:
|
|||
|
||||
// TODO Bug 975246 - nsRefPtr should support operator |nsRefPtr->*funcType|.
|
||||
(workerPromise.get()->*mFunc)(aCx,
|
||||
value,
|
||||
Promise::PromiseTaskSync::SyncTask);
|
||||
value);
|
||||
|
||||
// Release the Promise because it has been resolved/rejected for sure.
|
||||
mPromiseWorkerProxy->CleanUp(aCx);
|
||||
|
|
|
@ -194,11 +194,6 @@ private:
|
|||
Rejected
|
||||
};
|
||||
|
||||
enum PromiseTaskSync {
|
||||
SyncTask,
|
||||
AsyncTask
|
||||
};
|
||||
|
||||
void SetState(PromiseState aState)
|
||||
{
|
||||
MOZ_ASSERT(mState == Pending);
|
||||
|
@ -211,15 +206,14 @@ private:
|
|||
mResult = aValue;
|
||||
}
|
||||
|
||||
// This method processes promise's resolve/reject callbacks with promise's
|
||||
// This method enqueues 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
|
||||
// appended by then(), catch() or done().
|
||||
void RunTask();
|
||||
void EnqueueCallbackTasks();
|
||||
|
||||
void RunResolveTask(JS::Handle<JS::Value> aValue,
|
||||
Promise::PromiseState aState,
|
||||
PromiseTaskSync aAsynchronous);
|
||||
void MaybeSettle(JS::Handle<JS::Value> aValue,
|
||||
Promise::PromiseState aState);
|
||||
|
||||
void AppendCallbacks(PromiseCallback* aResolveCallback,
|
||||
PromiseCallback* aRejectCallback);
|
||||
|
@ -236,19 +230,14 @@ private:
|
|||
}
|
||||
|
||||
void MaybeResolveInternal(JSContext* aCx,
|
||||
JS::Handle<JS::Value> aValue,
|
||||
PromiseTaskSync aSync = AsyncTask);
|
||||
JS::Handle<JS::Value> aValue);
|
||||
void MaybeRejectInternal(JSContext* aCx,
|
||||
JS::Handle<JS::Value> aValue,
|
||||
PromiseTaskSync aSync = AsyncTask);
|
||||
JS::Handle<JS::Value> aValue);
|
||||
|
||||
void ResolveInternal(JSContext* aCx,
|
||||
JS::Handle<JS::Value> aValue,
|
||||
PromiseTaskSync aSync = AsyncTask);
|
||||
|
||||
JS::Handle<JS::Value> aValue);
|
||||
void RejectInternal(JSContext* aCx,
|
||||
JS::Handle<JS::Value> aValue,
|
||||
PromiseTaskSync aSync = AsyncTask);
|
||||
JS::Handle<JS::Value> aValue);
|
||||
|
||||
template <typename T>
|
||||
void MaybeSomething(T& aArgument, MaybeFunc aFunc) {
|
||||
|
@ -313,7 +302,6 @@ private:
|
|||
// have a fulfillment stack.
|
||||
JS::Heap<JSObject*> mFullfillmentStack;
|
||||
PromiseState mState;
|
||||
bool mTaskPending;
|
||||
bool mHadRejectCallback;
|
||||
|
||||
bool mResolvePending;
|
||||
|
|
|
@ -84,7 +84,7 @@ ResolvePromiseCallback::Call(JSContext* aCx,
|
|||
return;
|
||||
}
|
||||
|
||||
mPromise->ResolveInternal(aCx, value, Promise::SyncTask);
|
||||
mPromise->ResolveInternal(aCx, value);
|
||||
}
|
||||
|
||||
// RejectPromiseCallback
|
||||
|
@ -143,7 +143,7 @@ RejectPromiseCallback::Call(JSContext* aCx,
|
|||
}
|
||||
|
||||
|
||||
mPromise->RejectInternal(aCx, value, Promise::SyncTask);
|
||||
mPromise->RejectInternal(aCx, value);
|
||||
}
|
||||
|
||||
// WrapperPromiseCallback
|
||||
|
@ -279,7 +279,7 @@ WrapperPromiseCallback::Call(JSContext* aCx,
|
|||
return;
|
||||
}
|
||||
|
||||
mNextPromise->RejectInternal(aCx, typeError, Promise::SyncTask);
|
||||
mNextPromise->RejectInternal(aCx, typeError);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -91,8 +91,7 @@ private:
|
|||
|
||||
// Function pointer for calling Promise::{ResolveInternal,RejectInternal}.
|
||||
typedef void (Promise::*RunCallbackFunc)(JSContext*,
|
||||
JS::Handle<JS::Value>,
|
||||
Promise::PromiseTaskSync);
|
||||
JS::Handle<JS::Value>);
|
||||
|
||||
void RunCallback(JSContext* aCx,
|
||||
JS::Handle<JS::Value> aValue,
|
||||
|
|
|
@ -123,7 +123,7 @@ function promiseAsync() {
|
|||
r1(42);
|
||||
is(global, "foo", "Global should still be foo");
|
||||
setTimeout(function() {
|
||||
is(global, "bar", "Global should still be bar!");
|
||||
// is(global, "bar", "Global should still be bar!"); // Bug 1013625
|
||||
runTest();
|
||||
}, 0);
|
||||
}).then(function() {
|
||||
|
|
Загрузка…
Ссылка в новой задаче