Bug 1281090. Part 2 - add synchronous notification to MediaEventSourceImpl. r=gerald. draft
authorJW Wang <jwwang@mozilla.com>
Mon, 20 Jun 2016 18:31:01 +0800
changeset 380740 045d7e18650aa4a17c05f35f33fc4fa80e9a030c
parent 380739 8d438b3c768f2d78b8b62e83e0ab55000d8a7fb7
child 380741 d4ef0e44106f939ed0695e5eef6df181e9bd0ec9
push id21310
push userjwwang@mozilla.com
push dateThu, 23 Jun 2016 02:27:34 +0000
reviewersgerald
bugs1281090
milestone50.0a1
Bug 1281090. Part 2 - add synchronous notification to MediaEventSourceImpl. r=gerald. MozReview-Commit-ID: 6uWl4IP5iEP
dom/media/MediaEventSource.h
--- a/dom/media/MediaEventSource.h
+++ b/dom/media/MediaEventSource.h
@@ -53,16 +53,21 @@ enum class ListenerPolicy : int8_t {
   // Allow at most one listener. Move will be used when possible
   // to pass the event data to save copy.
   Exclusive,
   // Allow multiple listeners. Event data will always be copied when passed
   // to the listeners.
   NonExclusive
 };
 
+enum class DispatchPolicy : int8_t {
+  Sync, // Events are passed synchronously to the listeners.
+  Async // Events are passed asynchronously to the listeners.
+};
+
 namespace detail {
 
 /**
  * Define how an event type is passed internally in MediaEventSource and to the
  * listeners. Specialized for the void type to pass a dummy bool instead.
  */
 template <typename T>
 struct EventTypeTraits {
@@ -87,34 +92,59 @@ class TakeArgsHelper {
   static TrueType test(...);
 public:
   typedef decltype(test(DeclVal<T>(), 0)) Type;
 };
 
 template <typename T>
 struct TakeArgs : public TakeArgsHelper<T>::Type {};
 
-template <typename T> struct EventTarget;
+template <DispatchPolicy Dp, typename T> struct EventTarget;
 
 template <>
-struct EventTarget<nsIEventTarget> {
+struct EventTarget<DispatchPolicy::Async, nsIEventTarget> {
   static void
   Dispatch(nsIEventTarget* aTarget, already_AddRefed<nsIRunnable> aTask) {
     aTarget->Dispatch(Move(aTask), NS_DISPATCH_NORMAL);
   }
 };
 
 template <>
-struct EventTarget<AbstractThread> {
+struct EventTarget<DispatchPolicy::Async, AbstractThread> {
   static void
   Dispatch(AbstractThread* aTarget, already_AddRefed<nsIRunnable> aTask) {
     aTarget->Dispatch(Move(aTask));
   }
 };
 
+template <>
+struct EventTarget<DispatchPolicy::Sync, nsIEventTarget> {
+  static bool IsOnCurrentThread(nsIEventTarget* aTarget) {
+    bool current = false;
+    auto rv = aTarget->IsOnCurrentThread(&current);
+    return NS_SUCCEEDED(rv) && current;
+  }
+  static void
+  Dispatch(nsIEventTarget* aTarget, already_AddRefed<nsIRunnable> aTask) {
+    MOZ_ASSERT(IsOnCurrentThread(aTarget));
+    nsCOMPtr<nsIRunnable> r = aTask;
+    r->Run();
+  }
+};
+
+template <>
+struct EventTarget<DispatchPolicy::Sync, AbstractThread> {
+  static void
+  Dispatch(AbstractThread* aTarget, already_AddRefed<nsIRunnable> aTask) {
+    MOZ_ASSERT(aTarget->IsCurrentThreadIn());
+    nsCOMPtr<nsIRunnable> r = aTask;
+    r->Run();
+  }
+};
+
 /**
  * Encapsulate a raw pointer to be captured by a lambda without causing
  * static-analysis errors.
  */
 template <typename T>
 class RawPtr {
 public:
   explicit RawPtr(T* aPtr) : mPtr(aPtr) {}
@@ -122,17 +152,17 @@ public:
 private:
   T* const mPtr;
 };
 
 /**
  * A helper class to pass event data to the listeners. Optimized to save
  * copy when Move is possible or |Function| takes no arguments.
  */
-template<typename Target, typename Function>
+template<DispatchPolicy Dp, typename Target, typename Function>
 class ListenerHelper {
   // Define our custom runnable to minimize copy of the event data.
   // NS_NewRunnableFunction will result in 2 copies of the event data.
   // One is captured by the lambda and the other is the copy of the lambda.
   template <typename... Ts>
   class R : public Runnable {
   public:
     template <typename... Us>
@@ -169,31 +199,31 @@ public:
     : mToken(aToken), mTarget(aTarget), mFunction(aFunc) {}
 
   // |F| takes one or more arguments.
   template <typename F, typename... Ts>
   typename EnableIf<TakeArgs<F>::value, void>::Type
   DispatchHelper(const F& aFunc, Ts&&... aEvents) {
     nsCOMPtr<nsIRunnable> r =
       new R<Ts...>(mToken, aFunc, Forward<Ts>(aEvents)...);
-    EventTarget<Target>::Dispatch(mTarget.get(), r.forget());
+    EventTarget<Dp, Target>::Dispatch(mTarget.get(), r.forget());
   }
 
   // |F| takes no arguments. Don't bother passing aEvent.
   template <typename F, typename... Ts>
   typename EnableIf<!TakeArgs<F>::value, void>::Type
   DispatchHelper(const F& aFunc, Ts&&...) {
     const RefPtr<RevocableToken>& token = mToken;
     nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([=] () {
       // Don't call the listener if it is disconnected.
       if (!token->IsRevoked()) {
         aFunc();
       }
     });
-    EventTarget<Target>::Dispatch(mTarget.get(), r.forget());
+    EventTarget<Dp, Target>::Dispatch(mTarget.get(), r.forget());
   }
 
   template <typename... Ts>
   void Dispatch(Ts&&... aEvents) {
     DispatchHelper(mFunction, Forward<Ts>(aEvents)...);
   }
 
 private:
@@ -244,39 +274,40 @@ public:
   virtual ~Listener() {}
   virtual void Dispatch(As... aEvents) = 0;
 };
 
 /**
  * Store the registered target thread and function so it knows where and to
  * whom to send the event data.
  */
-template <typename Target, typename Function, EventPassMode, typename... As>
+template <DispatchPolicy Dp, typename Target,
+          typename Function, EventPassMode, typename... As>
 class ListenerImpl : public Listener<EventPassMode::Copy, As...> {
 public:
   ListenerImpl(Target* aTarget, const Function& aFunction)
     : mHelper(ListenerBase::Token(), aTarget, aFunction) {}
   void Dispatch(const As&... aEvents) override {
     mHelper.Dispatch(aEvents...);
   }
 private:
-  ListenerHelper<Target, Function> mHelper;
+  ListenerHelper<Dp, Target, Function> mHelper;
 };
 
-template <typename Target, typename Function, typename... As>
-class ListenerImpl<Target, Function, EventPassMode::Move, As...>
+template <DispatchPolicy Dp, typename Target, typename Function, typename... As>
+class ListenerImpl<Dp, Target, Function, EventPassMode::Move, As...>
   : public Listener<EventPassMode::Move, As...> {
 public:
   ListenerImpl(Target* aTarget, const Function& aFunction)
     : mHelper(ListenerBase::Token(), aTarget, aFunction) {}
   void Dispatch(As... aEvents) override {
     mHelper.Dispatch(Move(aEvents)...);
   }
 private:
-  ListenerHelper<Target, Function> mHelper;
+  ListenerHelper<Dp, Target, Function> mHelper;
 };
 
 /**
  * Select EventPassMode based on ListenerPolicy.
  *
  * @Copy Selected when ListenerPolicy is NonExclusive because each listener
  * must get a copy.
  *
@@ -301,26 +332,27 @@ struct IsAnyReference {
 
 template <typename T>
 struct IsAnyReference<T> {
   static const bool value = IsReference<T>::value;
 };
 
 } // namespace detail
 
-template <ListenerPolicy, typename... Ts> class MediaEventSourceImpl;
+template <DispatchPolicy, ListenerPolicy, typename... Ts>
+class MediaEventSourceImpl;
 
 /**
  * Not thread-safe since this is not meant to be shared and therefore only
  * move constructor is provided. Used to hold the result of
  * MediaEventSource<T>::Connect() and call Disconnect() to disconnect the
  * listener from an event source.
  */
 class MediaEventListener {
-  template <ListenerPolicy, typename... Ts>
+  template <DispatchPolicy, ListenerPolicy, typename... Ts>
   friend class MediaEventSourceImpl;
 
 public:
   MediaEventListener() {}
 
   MediaEventListener(MediaEventListener&& aOther)
     : mToken(Move(aOther.mToken)) {}
 
@@ -350,32 +382,32 @@ private:
   // listeners can be disconnected in a controlled manner.
   explicit MediaEventListener(RevocableToken* aToken) : mToken(aToken) {}
   RefPtr<RevocableToken> mToken;
 };
 
 /**
  * A generic and thread-safe class to implement the observer pattern.
  */
-template <ListenerPolicy Lp, typename... Es>
+template <DispatchPolicy Dp, ListenerPolicy Lp, typename... Es>
 class MediaEventSourceImpl {
   static_assert(!detail::IsAnyReference<Es...>::value,
                 "Ref-type not supported!");
 
   template <typename T>
   using ArgType = typename detail::EventTypeTraits<T>::ArgType;
 
   static const detail::EventPassMode PassMode =
     detail::PassModePicker<Lp>::Value;
 
   typedef detail::Listener<PassMode, ArgType<Es>...> Listener;
 
   template<typename Target, typename Func>
   using ListenerImpl =
-    detail::ListenerImpl<Target, Func, PassMode, ArgType<Es>...>;
+    detail::ListenerImpl<Dp, Target, Func, PassMode, ArgType<Es>...>;
 
   template <typename Method>
   using TakeArgs = detail::TakeArgs<Method>;
 
   template<typename Target, typename Function>
   MediaEventListener
   ConnectInternal(Target* aTarget, const Function& aFunction) {
     MutexAutoLock lock(mMutex);
@@ -471,21 +503,22 @@ protected:
 
 private:
   Mutex mMutex;
   nsTArray<UniquePtr<Listener>> mListeners;
 };
 
 template <typename... Es>
 using MediaEventSource =
-  MediaEventSourceImpl<ListenerPolicy::NonExclusive, Es...>;
+  MediaEventSourceImpl<DispatchPolicy::Async,
+                       ListenerPolicy::NonExclusive, Es...>;
 
 template <typename... Es>
 using MediaEventSourceExc =
-  MediaEventSourceImpl<ListenerPolicy::Exclusive, Es...>;
+  MediaEventSourceImpl<DispatchPolicy::Async, ListenerPolicy::Exclusive, Es...>;
 
 /**
  * A class to separate the interface of event subject (MediaEventSource)
  * and event publisher. Mostly used as a member variable to publish events
  * to the listeners.
  */
 template <typename... Es>
 class MediaEventProducer : public MediaEventSource<Es...> {
@@ -515,11 +548,41 @@ template <typename... Es>
 class MediaEventProducerExc : public MediaEventSourceExc<Es...> {
 public:
   template <typename... Ts>
   void Notify(Ts&&... aEvents) {
     this->NotifyInternal(Forward<Ts>(aEvents)...);
   }
 };
 
+/**
+ * Events are passed directly to the callback function of the listeners without
+ * dispatching. Note this class is not thread-safe. Both Connect() and Notify()
+ * must be called on the same thread.
+ */
+template <typename... Es>
+class MediaCallback
+  : public MediaEventSourceImpl<DispatchPolicy::Sync,
+                                ListenerPolicy::NonExclusive, Es...> {
+public:
+  template <typename... Ts>
+  void Notify(Ts&&... aEvents) {
+    this->NotifyInternal(Forward<Ts>(aEvents)...);
+  }
+};
+
+/**
+ * A special version of MediaCallback which allows at most one listener.
+ */
+template <typename... Es>
+class MediaCallbackExc
+  : public MediaEventSourceImpl<DispatchPolicy::Sync,
+                                ListenerPolicy::Exclusive, Es...> {
+public:
+  template <typename... Ts>
+  void Notify(Ts&&... aEvents) {
+    this->NotifyInternal(Forward<Ts>(aEvents)...);
+  }
+};
+
 } // namespace mozilla
 
 #endif //MediaEventSource_h_