Bug 856410 - Implement futures - Part 3: resolver.resolve(new Future(). r=mounir
authorAndrea Marchesini <amarchesini@mozilla.com>
Tue, 11 Jun 2013 21:41:22 -0400
changeset 146219 7c3bc1a19c7ab6bdb7d7fe0e81f9dea5612d8a45
parent 146218 90f1f4535f03b753de71593bffec524a19d042da
child 146220 9bcc6c50cd266000bceb38c454450bc57a6292cb
push id2697
push userbbajaj@mozilla.com
push dateMon, 05 Aug 2013 18:49:53 +0000
treeherdermozilla-beta@dfec938c7b63 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmounir
bugs856410
milestone24.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 856410 - Implement futures - Part 3: resolver.resolve(new Future(). r=mounir
dom/future/FutureCallback.cpp
dom/future/FutureResolver.cpp
dom/future/FutureResolver.h
dom/future/tests/test_future.html
--- a/dom/future/FutureCallback.cpp
+++ b/dom/future/FutureCallback.cpp
@@ -65,17 +65,17 @@ ResolveFutureCallback::Call(const Option
   AutoJSContext cx;
   // FIXME Bug 878849
   Maybe<JSAutoCompartment> ac;
   if (aValue.WasPassed() && aValue.Value().isObject()) {
     JS::Rooted<JSObject*> rooted(cx, &aValue.Value().toObject());
     ac.construct(cx, rooted);
   }
 
-  mResolver->Resolve(cx, aValue, FutureResolver::SyncTask);
+  mResolver->ResolveInternal(cx, aValue, FutureResolver::SyncTask);
 }
 
 // RejectFutureCallback
 
 NS_IMPL_CYCLE_COLLECTION_INHERITED_1(RejectFutureCallback,
                                      FutureCallback,
                                      mResolver)
 
@@ -104,17 +104,17 @@ RejectFutureCallback::Call(const Optiona
   AutoJSContext cx;
   // FIXME Bug 878849
   Maybe<JSAutoCompartment> ac;
   if (aValue.WasPassed() && aValue.Value().isObject()) {
     JS::Rooted<JSObject*> rooted(cx, &aValue.Value().toObject());
     ac.construct(cx, rooted);
   }
 
-  mResolver->Reject(cx, aValue, FutureResolver::SyncTask);
+  mResolver->RejectInternal(cx, aValue, FutureResolver::SyncTask);
 }
 
 // WrapperFutureCallback
 
 NS_IMPL_CYCLE_COLLECTION_INHERITED_2(WrapperFutureCallback,
                                      FutureCallback,
                                      mNextResolver, mCallback)
 
@@ -157,23 +157,23 @@ WrapperFutureCallback::Call(const Option
     mCallback->Call(mNextResolver->GetParentObject(), aValue, rv,
                     CallbackObject::eRethrowExceptions));
 
   rv.WouldReportJSException();
 
   if (rv.Failed() && rv.IsJSException()) {
     Optional<JS::Handle<JS::Value> > value(cx);
     rv.StealJSException(cx, &value.Value());
-    mNextResolver->Reject(cx, value, FutureResolver::SyncTask);
+    mNextResolver->RejectInternal(cx, value, FutureResolver::SyncTask);
     return;
   }
 
   // Otherwise, run resolver's resolve with value and the synchronous flag
   // set.
-  mNextResolver->Resolve(cx, value, FutureResolver::SyncTask);
+  mNextResolver->ResolveInternal(cx, value, FutureResolver::SyncTask);
 }
 
 // SimpleWrapperFutureCallback
 
 NS_IMPL_CYCLE_COLLECTION_INHERITED_2(SimpleWrapperFutureCallback,
                                      FutureCallback,
                                      mFuture, mCallback)
 
--- a/dom/future/FutureResolver.cpp
+++ b/dom/future/FutureResolver.cpp
@@ -2,16 +2,17 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* 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 "mozilla/dom/FutureResolver.h"
 #include "mozilla/dom/FutureBinding.h"
 #include "mozilla/dom/Future.h"
+#include "FutureCallback.h"
 
 namespace mozilla {
 namespace dom {
 
 // FutureResolverTask
 
 // This class processes the future's callbacks with future's result.
 class FutureResolverTask MOZ_FINAL : public nsRunnable
@@ -83,19 +84,39 @@ void
 FutureResolver::Resolve(JSContext* aCx,
                         const Optional<JS::Handle<JS::Value> >& aValue,
                         FutureTaskSync aAsynchronous)
 {
   if (mResolvePending) {
     return;
   }
 
-  // TODO: if the arg is a future?
+  ResolveInternal(aCx, aValue, aAsynchronous);
+}
+
+void
+FutureResolver::ResolveInternal(JSContext* aCx,
+                                const Optional<JS::Handle<JS::Value> >& aValue,
+                                FutureTaskSync aAsynchronous)
+{
+  mResolvePending = true;
 
-  mResolvePending = true;
+  // TODO: Bug 879245 - Then-able objects
+  if (aValue.WasPassed() && aValue.Value().isObject()) {
+    JS::Rooted<JSObject*> valueObj(aCx, &aValue.Value().toObject());
+    Future* nextFuture;
+    nsresult rv = UnwrapObject<Future>(aCx, valueObj, nextFuture);
+
+    if (NS_SUCCEEDED(rv)) {
+      nsRefPtr<FutureCallback> resolveCb = new ResolveFutureCallback(this);
+      nsRefPtr<FutureCallback> rejectCb = new RejectFutureCallback(this);
+      nextFuture->AppendCallbacks(resolveCb, rejectCb);
+      return;
+    }
+  }
 
   // If the synchronous flag is set, process future's resolve callbacks with
   // value. Otherwise, the synchronous flag is unset, queue a task to process
   // future's resolve callbacks with value. Otherwise, the synchronous flag is
   // unset, queue a task to process future's resolve callbacks with value.
   RunTask(aValue.WasPassed() ? aValue.Value() : JS::UndefinedHandleValue,
           Future::Resolved, aAsynchronous);
 }
@@ -104,16 +125,24 @@ void
 FutureResolver::Reject(JSContext* aCx,
                        const Optional<JS::Handle<JS::Value> >& aValue,
                        FutureTaskSync aAsynchronous)
 {
   if (mResolvePending) {
     return;
   }
 
+  RejectInternal(aCx, aValue, aAsynchronous);
+}
+
+void
+FutureResolver::RejectInternal(JSContext* aCx,
+                               const Optional<JS::Handle<JS::Value> >& aValue,
+                               FutureTaskSync aAsynchronous)
+{
   mResolvePending = true;
 
   // If the synchronous flag is set, process future's reject callbacks with
   // value. Otherwise, the synchronous flag is unset, queue a task to process
   // future's reject callbacks with value.
   RunTask(aValue.WasPassed() ? aValue.Value() : JS::UndefinedHandleValue,
           Future::Rejected, aAsynchronous);
 }
--- a/dom/future/FutureResolver.h
+++ b/dom/future/FutureResolver.h
@@ -48,16 +48,24 @@ public:
 
   void Resolve(JSContext* aCx, const Optional<JS::Handle<JS::Value> >& aValue,
                FutureTaskSync aSync = AsyncTask);
 
   void Reject(JSContext* aCx, const Optional<JS::Handle<JS::Value> >& aValue,
               FutureTaskSync aSync = AsyncTask);
 
 private:
+  void ResolveInternal(JSContext* aCx,
+                       const Optional<JS::Handle<JS::Value> >& aValue,
+                       FutureTaskSync aSync = AsyncTask);
+
+  void RejectInternal(JSContext* aCx,
+                      const Optional<JS::Handle<JS::Value> >& aValue,
+                      FutureTaskSync aSync = AsyncTask);
+
   void RunTask(JS::Handle<JS::Value> aValue,
                Future::FutureState aState, FutureTaskSync aSync);
 
   nsRefPtr<Future> mFuture;
 
   bool mResolvePending;
 };
 
--- a/dom/future/tests/test_future.html
+++ b/dom/future/tests/test_future.html
@@ -282,24 +282,82 @@ function futureThenCatchOrderingReject()
     });
     setTimeout(function() {
       is(global, 2, "Many steps... should return 2");
       runTest();
     }, 0);
   });
 }
 
+function futureNestedFuture() {
+  new Future(function(resolver) {
+    resolver.resolve(new Future(function(r) {
+      ok(true, "Nested future is executed");
+      r.resolve(42);
+    }));
+  }).then(function(value) {
+    is(value, 42, "Nested future is executed and then == 42");
+    runTest();
+  });
+}
+
+function futureNestedNestedFuture() {
+  new Future(function(resolver) {
+    resolver.resolve(new Future(function(r) {
+      ok(true, "Nested future is executed");
+      r.resolve(42);
+    }).then(function(what) { return what+1; }));
+  }).then(function(value) {
+    is(value, 43, "Nested future is executed and then == 43");
+    runTest();
+  });
+}
+
+function futureWrongNestedFuture() {
+  new Future(function(resolver) {
+    resolver.resolve(new Future(function(r) {
+      ok(true, "Nested future is executed");
+      r.resolve(42);
+    }));
+    resolver.reject(42);
+  }).then(function(value) {
+    is(value, 42, "Nested future is executed and then == 42");
+    runTest();
+  }, function(value) {
+     ok(false, "This is wrong");
+  });
+}
+
+function futureLoop() {
+  new Future(function(resolver) {
+    resolver.resolve(new Future(function(r) {
+      ok(true, "Nested future is executed");
+      r.resolve(new Future(function(r) {
+        ok(true, "Nested nested future is executed");
+        r.resolve(42);
+      }));
+    }));
+  }).then(function(value) {
+    is(value, 42, "Nested nested future is executed and then == 42");
+    runTest();
+  }, function(value) {
+     ok(false, "This is wrong");
+  });
+}
+
 var tests = [ futureResolve, futureReject,
               futureException, futureGC, futureAsync,
               futureDoubleDone, futureDoneException,
               futureThenCatchDone, futureRejectThenCatchDone,
               futureRejectThenCatchDone2,
               futureRejectThenCatchExceptionDone,
               futureThenCatchOrderingResolve,
-              futureThenCatchOrderingReject
+              futureThenCatchOrderingReject,
+              futureNestedFuture, futureNestedNestedFuture,
+              futureWrongNestedFuture, futureLoop
             ];
 
 function runTest() {
   if (!tests.length) {
     SimpleTest.finish();
     return;
   }