Bug 1404997 - P18. Add Await convenience methods. r?gerald draft
authorJean-Yves Avenard <jyavenard@mozilla.com>
Fri, 08 Dec 2017 13:35:29 +0100
changeset 712538 f2edc4d9f86a6d87adc4a2a81de7f146681b13fa
parent 712537 d89cbf39c2a4afc642567c8bba4684e302e1bcf8
child 712539 c600de66b1bc9de8a48b95204d2ba2219b4b1804
push id93357
push userbmo:jyavenard@mozilla.com
push dateSun, 17 Dec 2017 09:29:04 +0000
reviewersgerald
bugs1404997
milestone59.0a1
Bug 1404997 - P18. Add Await convenience methods. r?gerald Takes either a MozPromise or an AllPromiseType and will execute the resolve/reject functions synchronously once the promise has resolved/rejected. MozReview-Commit-ID: EyfMTPtA1Lu
dom/media/systemservices/MediaUtils.h
--- a/dom/media/systemservices/MediaUtils.h
+++ b/dom/media/systemservices/MediaUtils.h
@@ -2,17 +2,22 @@
 /* vim: set sw=2 ts=8 et ft=cpp : */
 /* 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_MediaUtils_h
 #define mozilla_MediaUtils_h
 
+#include "AutoTaskQueue.h"
 #include "mozilla/Assertions.h"
+#include "mozilla/Monitor.h"
+#include "mozilla/MozPromise.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/SharedThreadPool.h"
 #include "mozilla/UniquePtr.h"
 #include "nsCOMPtr.h"
 #include "nsIAsyncShutdown.h"
 #include "nsISupportsImpl.h"
 #include "nsThreadUtils.h"
 
 namespace mozilla {
 namespace media {
@@ -405,12 +410,128 @@ private:
   {
     nsCOMPtr<nsIAsyncShutdownClient> barrier = GetShutdownBarrier();
     barrier->RemoveBlocker(mBlocker);
   }
 
   nsCOMPtr<nsIAsyncShutdownBlocker> mBlocker;
 };
 
+/**
+ * Await convenience methods to block until the promise has been resolved or
+ * rejected. The Resolve/Reject functions, while called on a different thread,
+ * would be running just as on the current thread thanks to the memory barrier
+ * provided by the monitor.
+ * For now Await can only be used with an exclusive MozPromise if passed a
+ * Resolve/Reject function.
+ */
+template<typename ResolveValueType,
+         typename RejectValueType,
+         typename ResolveFunction,
+         typename RejectFunction>
+void
+Await(
+  RefPtr<MozPromise<ResolveValueType, RejectValueType, true>> aPromise,
+  ResolveFunction&& aResolveFunction,
+  RejectFunction&& aRejectFunction)
+{
+  Monitor mon(__func__);
+  RefPtr<AutoTaskQueue> taskQueue = new AutoTaskQueue(
+    SharedThreadPool::Get(NS_LITERAL_CSTRING("AwaitMozPromise")));
+  bool done = false;
+
+  aPromise->Then(taskQueue,
+                 __func__,
+                 [&](ResolveValueType&& aResolveValue) {
+                   MonitorAutoLock lock(mon);
+                   aResolveFunction(Forward<ResolveValueType>(aResolveValue));
+                   done = true;
+                   mon.Notify();
+                 },
+                 [&](RejectValueType&& aRejectValue) {
+                   MonitorAutoLock lock(mon);
+                   aRejectFunction(Forward<RejectValueType>(aRejectValue));
+                   done = true;
+                   mon.Notify();
+                 });
+
+  MonitorAutoLock lock(mon);
+  while (!done) {
+    mon.Wait();
+  }
+}
+
+template<typename ResolveValueType, typename RejectValueType, bool Excl>
+typename MozPromise<ResolveValueType, RejectValueType, Excl>::
+  ResolveOrRejectValue
+Await(RefPtr<MozPromise<ResolveValueType, RejectValueType, Excl>> aPromise)
+{
+  Monitor mon(__func__);
+  RefPtr<AutoTaskQueue> taskQueue = new AutoTaskQueue(
+    SharedThreadPool::Get(NS_LITERAL_CSTRING("AwaitMozPromise")));
+  bool done = false;
+
+  typename MozPromise<ResolveValueType, RejectValueType, Excl>::ResolveOrRejectValue val;
+  aPromise->Then(taskQueue,
+                 __func__,
+                 [&](ResolveValueType aResolveValue) {
+                   val.SetResolve(Move(aResolveValue));
+                   MonitorAutoLock lock(mon);
+                   done = true;
+                   mon.Notify();
+                 },
+                 [&](RejectValueType aRejectValue) {
+                   val.SetReject(Move(aRejectValue));
+                   MonitorAutoLock lock(mon);
+                   done = true;
+                   mon.Notify();
+                 });
+
+  MonitorAutoLock lock(mon);
+  while (!done) {
+    mon.Wait();
+  }
+
+  return val;
+}
+
+/**
+ * Similar to Await, takes an array of promises of the same type.
+ * MozPromise::All is used to handle the resolution/rejection of the promises.
+ */
+template<typename ResolveValueType,
+         typename RejectValueType,
+         typename ResolveFunction,
+         typename RejectFunction>
+void
+AwaitAll(nsTArray<RefPtr<MozPromise<ResolveValueType, RejectValueType, true>>>&
+           aPromises,
+         ResolveFunction&& aResolveFunction,
+         RejectFunction&& aRejectFunction)
+{
+  typedef MozPromise<ResolveValueType, RejectValueType, true> Promise;
+  RefPtr<AutoTaskQueue> taskQueue = new AutoTaskQueue(
+    SharedThreadPool::Get(NS_LITERAL_CSTRING("AwaitMozPromise")));
+  RefPtr<typename Promise::AllPromiseType> p = Promise::All(taskQueue, aPromises);
+  Await(p, Move(aResolveFunction), Move(aRejectFunction));
+}
+
+// Note: only works with exclusive MozPromise, as Promise::All would attempt
+// to perform copy of nsTArrays which are disallowed.
+template<typename ResolveValueType, typename RejectValueType>
+typename MozPromise<ResolveValueType,
+                    RejectValueType,
+                    true>::AllPromiseType::ResolveOrRejectValue
+AwaitAll(nsTArray<RefPtr<MozPromise<ResolveValueType, RejectValueType, true>>>&
+           aPromises)
+{
+  typedef MozPromise<ResolveValueType, RejectValueType, true> Promise;
+  RefPtr<AutoTaskQueue> taskQueue = new AutoTaskQueue(
+    SharedThreadPool::Get(NS_LITERAL_CSTRING("AwaitMozPromise")));
+  RefPtr<typename Promise::AllPromiseType> p =
+    Promise::All(taskQueue, aPromises);
+  return Await(p);
+}
+
 } // namespace media
 } // namespace mozilla
 
 #endif // mozilla_MediaUtils_h