Bug 1194112. Part 4 - reimplement Listener/ListenerImpl to support Move. r=kinetik.
authorJW Wang <jwwang@mozilla.com>
Mon, 24 Aug 2015 10:38:33 +0800
changeset 259021 1254f92e2493d81474705f7fa90ab455682febb6
parent 259020 37fb89797a7f3f7ec50e98fea978939efb2e4ad2
child 259022 d9d782e4fbc443688f8c0db2d88259cd8b0a2ffa
push id29268
push userryanvm@gmail.com
push dateTue, 25 Aug 2015 00:37:23 +0000
treeherdermozilla-central@08015770c9d6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskinetik
bugs1194112
milestone43.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 1194112. Part 4 - reimplement Listener/ListenerImpl to support Move. r=kinetik.
dom/media/MediaEventSource.h
--- a/dom/media/MediaEventSource.h
+++ b/dom/media/MediaEventSource.h
@@ -185,16 +185,134 @@ public:
   }
 
 private:
   nsRefPtr<RevocableToken> mToken;
   const nsRefPtr<Target> mTarget;
   Function mFunction;
 };
 
+/**
+ * Define whether an event data should be copied or moved to the listeners.
+ *
+ * @Copy Data will always be copied. Each listener gets a copy.
+ * @Move Data will always be moved.
+ * @Both Data will be moved when possible or copied when necessary.
+ */
+enum class EventPassMode : int8_t {
+  Copy,
+  Move,
+  Both
+};
+
+class ListenerBase {
+public:
+  ListenerBase() : mToken(new RevocableToken()) {}
+  ~ListenerBase() {
+    MOZ_ASSERT(Token()->IsRevoked(), "Must disconnect the listener.");
+  }
+  RevocableToken* Token() const {
+    return mToken;
+  }
+private:
+  const nsRefPtr<RevocableToken> mToken;
+};
+
+/**
+ * Stored by MediaEventSource to send notifications to the listener.
+ * Since virtual methods can not be templated, this class is specialized
+ * to provide different Dispatch() overloads depending on EventPassMode.
+ */
+template <typename ArgType, EventPassMode Mode = EventPassMode::Copy>
+class Listener : public ListenerBase {
+public:
+  virtual ~Listener() {}
+  virtual void Dispatch(const ArgType& aEvent) = 0;
+};
+
+template <typename ArgType>
+class Listener<ArgType, EventPassMode::Both> : public ListenerBase {
+public:
+  virtual ~Listener() {}
+  virtual void Dispatch(const ArgType& aEvent) = 0;
+  virtual void Dispatch(ArgType&& aEvent) = 0;
+};
+
+template <typename ArgType>
+class Listener<ArgType, EventPassMode::Move> : public ListenerBase {
+public:
+  virtual ~Listener() {}
+  virtual void Dispatch(ArgType&& aEvent) = 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, typename ArgType, EventPassMode>
+class ListenerImpl : public Listener<ArgType, EventPassMode::Copy> {
+public:
+  ListenerImpl(Target* aTarget, const Function& aFunction)
+    : mHelper(ListenerBase::Token(), aTarget, aFunction) {}
+  void Dispatch(const ArgType& aEvent) override {
+    mHelper.Dispatch(aEvent);
+  }
+private:
+  ListenerHelper<Target, Function> mHelper;
+};
+
+template <typename Target, typename Function, typename ArgType>
+class ListenerImpl<Target, Function, ArgType, EventPassMode::Both>
+  : public Listener<ArgType, EventPassMode::Both> {
+public:
+  ListenerImpl(Target* aTarget, const Function& aFunction)
+    : mHelper(ListenerBase::Token(), aTarget, aFunction) {}
+  void Dispatch(const ArgType& aEvent) override {
+    mHelper.Dispatch(aEvent);
+  }
+  void Dispatch(ArgType&& aEvent) override {
+    mHelper.Dispatch(Move(aEvent));
+  }
+private:
+  ListenerHelper<Target, Function> mHelper;
+};
+
+template <typename Target, typename Function, typename ArgType>
+class ListenerImpl<Target, Function, ArgType, EventPassMode::Move>
+  : public Listener<ArgType, EventPassMode::Move> {
+public:
+  ListenerImpl(Target* aTarget, const Function& aFunction)
+    : mHelper(ListenerBase::Token(), aTarget, aFunction) {}
+  void Dispatch(ArgType&& aEvent) override {
+    mHelper.Dispatch(Move(aEvent));
+  }
+private:
+  ListenerHelper<Target, Function> mHelper;
+};
+
+/**
+ * Select EventPassMode based on ListenerMode and if the type is copyable.
+ *
+ * @Copy Selected when ListenerMode is NonExclusive because each listener
+ * must get a copy.
+ *
+ * @Move Selected when ListenerMode is Exclusive and the type is move-only.
+ *
+ * @Both Selected when ListenerMode is Exclusive and the type is copyable.
+ * The data will be moved when possible and copied when necessary.
+ */
+template <typename ArgType, ListenerMode Mode>
+struct PassModePicker {
+  // TODO: pick EventPassMode::Both when we can detect if a type is
+  // copy-constructible to allow copy-only types in Exclusive mode.
+  static const EventPassMode Value =
+    Mode == ListenerMode::NonExclusive ?
+    EventPassMode::Copy : EventPassMode::Move;
+};
+
 } // namespace detail
 
 template <typename T, ListenerMode> class MediaEventSource;
 
 /**
  * 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
@@ -234,16 +352,22 @@ private:
 
 /**
  * A generic and thread-safe class to implement the observer pattern.
  */
 template <typename EventType, ListenerMode Mode = ListenerMode::NonExclusive>
 class MediaEventSource {
   static_assert(!IsReference<EventType>::value, "Ref-type not supported!");
   typedef typename detail::EventTypeTraits<EventType>::ArgType ArgType;
+  static const detail::EventPassMode PassMode
+    = detail::PassModePicker<ArgType, Mode>::Value;
+  typedef detail::Listener<ArgType, PassMode> Listener;
+
+  template<typename Target, typename Func>
+  using ListenerImpl = detail::ListenerImpl<Target, Func, ArgType, PassMode>;
 
   template <typename Method>
   using TakeArgs = detail::TakeArgs<Method>;
 
   /**
    * Stored by MediaEventSource to send notifications to the listener.
    */
   class Listener {