LCOV - code coverage report
Current view: top level - dom/media - MediaEventSource.h (source / functions) Hit Total Coverage
Test: output.info Lines: 1 115 0.9 %
Date: 2017-07-14 16:53:18 Functions: 1 468 0.2 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim:set ts=2 sw=2 sts=2 et cindent: */
       3             : /* This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #ifndef MediaEventSource_h_
       8             : #define MediaEventSource_h_
       9             : 
      10             : #include "mozilla/AbstractThread.h"
      11             : #include "mozilla/Atomics.h"
      12             : #include "mozilla/IndexSequence.h"
      13             : #include "mozilla/Mutex.h"
      14             : #include "mozilla/Tuple.h"
      15             : #include "mozilla/TypeTraits.h"
      16             : 
      17             : #include "nsISupportsImpl.h"
      18             : #include "nsTArray.h"
      19             : #include "nsThreadUtils.h"
      20             : 
      21             : namespace mozilla {
      22             : 
      23             : /**
      24             :  * A thread-safe tool to communicate "revocation" across threads. It is used to
      25             :  * disconnect a listener from the event source to prevent future notifications
      26             :  * from coming. Revoke() can be called on any thread. However, it is recommended
      27             :  * to be called on the target thread to avoid race condition.
      28             :  *
      29             :  * RevocableToken is not exposed to the client code directly.
      30             :  * Use MediaEventListener below to do the job.
      31             :  */
      32             : class RevocableToken {
      33           0 :   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RevocableToken);
      34             : 
      35             : public:
      36           0 :   RevocableToken() : mRevoked(false) {}
      37             : 
      38           0 :   void Revoke() {
      39           0 :     mRevoked = true;
      40           0 :   }
      41             : 
      42           0 :   bool IsRevoked() const {
      43           0 :     return mRevoked;
      44             :   }
      45             : 
      46             : protected:
      47             :   // Virtual destructor is required since we might delete a Listener object
      48             :   // through its base type pointer.
      49           0 :   virtual ~RevocableToken() { }
      50             : 
      51             : private:
      52             :   Atomic<bool> mRevoked;
      53             : };
      54             : 
      55             : enum class ListenerPolicy : int8_t {
      56             :   // Allow at most one listener. Move will be used when possible
      57             :   // to pass the event data to save copy.
      58             :   Exclusive,
      59             :   // Allow multiple listeners. Event data will always be copied when passed
      60             :   // to the listeners.
      61             :   NonExclusive
      62             : };
      63             : 
      64             : namespace detail {
      65             : 
      66             : /**
      67             :  * Define how an event type is passed internally in MediaEventSource and to the
      68             :  * listeners. Specialized for the void type to pass a dummy bool instead.
      69             :  */
      70             : template <typename T>
      71             : struct EventTypeTraits {
      72             :   typedef T ArgType;
      73             : };
      74             : 
      75             : template <>
      76             : struct EventTypeTraits<void> {
      77             :   typedef bool ArgType;
      78             : };
      79             : 
      80             : /**
      81             :  * Test if a method function or lambda accepts one or more arguments.
      82             :  */
      83             : template <typename T>
      84             : class TakeArgsHelper {
      85             :   template <typename C> static FalseType test(void(C::*)(), int);
      86             :   template <typename C> static FalseType test(void(C::*)() const, int);
      87             :   template <typename C> static FalseType test(void(C::*)() volatile, int);
      88             :   template <typename C> static FalseType test(void(C::*)() const volatile, int);
      89             :   template <typename F> static FalseType test(F&&, decltype(DeclVal<F>()(), 0));
      90             :   static TrueType test(...);
      91             : public:
      92             :   typedef decltype(test(DeclVal<T>(), 0)) Type;
      93             : };
      94             : 
      95             : template <typename T>
      96             : struct TakeArgs : public TakeArgsHelper<T>::Type {};
      97             : 
      98             : template <typename T> struct EventTarget;
      99             : 
     100             : template <>
     101             : struct EventTarget<nsIEventTarget> {
     102             :   static void
     103             :   Dispatch(nsIEventTarget* aTarget, already_AddRefed<nsIRunnable> aTask) {
     104             :     aTarget->Dispatch(Move(aTask), NS_DISPATCH_NORMAL);
     105             :   }
     106             : };
     107             : 
     108             : template <>
     109             : struct EventTarget<AbstractThread> {
     110             :   static void
     111           0 :   Dispatch(AbstractThread* aTarget, already_AddRefed<nsIRunnable> aTask) {
     112           0 :     aTarget->Dispatch(Move(aTask), AbstractThread::DontAssertDispatchSuccess);
     113           0 :   }
     114             : };
     115             : 
     116             : /**
     117             :  * Encapsulate a raw pointer to be captured by a lambda without causing
     118             :  * static-analysis errors.
     119             :  */
     120             : template <typename T>
     121             : class RawPtr {
     122             : public:
     123           0 :   explicit RawPtr(T* aPtr) : mPtr(aPtr) {}
     124           0 :   T* get() const { return mPtr; }
     125             : private:
     126             :   T* const mPtr;
     127             : };
     128             : 
     129             : template <typename... As>
     130           0 : class Listener : public RevocableToken
     131             : {
     132             : public:
     133             :   template <typename... Ts>
     134           0 :   void Dispatch(Ts&&... aEvents)
     135             :   {
     136           0 :     if (CanTakeArgs()) {
     137           0 :       DispatchTask(NewRunnableMethod<typename Decay<Ts>::Type&&...>(
     138             :         "detail::Listener::ApplyWithArgs",
     139             :         this,
     140             :         &Listener::ApplyWithArgs,
     141             :         Forward<Ts>(aEvents)...));
     142             :     } else {
     143           0 :       DispatchTask(NewRunnableMethod(
     144             :         "detail::Listener::ApplyWithNoArgs", this, &Listener::ApplyWithNoArgs));
     145             :     }
     146           0 :   }
     147             : 
     148             : protected:
     149           0 :   virtual ~Listener()
     150             :   {
     151           0 :     MOZ_ASSERT(IsRevoked(), "Must disconnect the listener.");
     152           0 :   }
     153             : 
     154             : private:
     155             :   virtual void DispatchTask(already_AddRefed<nsIRunnable> aTask) = 0;
     156             : 
     157             :   // True if the underlying listener function takes non-zero arguments.
     158             :   virtual bool CanTakeArgs() const = 0;
     159             :   // Pass the event data to the underlying listener function. Should be called
     160             :   // only when CanTakeArgs() returns true.
     161             :   virtual void ApplyWithArgs(As&&... aEvents) = 0;
     162             :   // Invoke the underlying listener function. Should be called only when
     163             :   // CanTakeArgs() returns false.
     164             :   virtual void ApplyWithNoArgs() = 0;
     165             : };
     166             : 
     167             : /**
     168             :  * Store the registered target thread and function so it knows where and to
     169             :  * whom to send the event data.
     170             :  */
     171             : template <typename Target, typename Function, typename... As>
     172           0 : class ListenerImpl : public Listener<As...>
     173             : {
     174             :   // Strip CV and reference from Function.
     175             :   using FunctionStorage = typename Decay<Function>::Type;
     176             : 
     177             : public:
     178             :   template <typename F>
     179           0 :   ListenerImpl(Target* aTarget, F&& aFunction)
     180             :     : mTarget(aTarget)
     181           0 :     , mFunction(Forward<F>(aFunction))
     182             :   {
     183           0 :   }
     184             : 
     185             : private:
     186           0 :   void DispatchTask(already_AddRefed<nsIRunnable> aTask) override
     187             :   {
     188           0 :     EventTarget<Target>::Dispatch(mTarget.get(), Move(aTask));
     189           0 :   }
     190             : 
     191           0 :   bool CanTakeArgs() const override
     192             :   {
     193           0 :     return TakeArgs<FunctionStorage>::value;
     194             :   }
     195             : 
     196             :   // |F| takes one or more arguments.
     197             :   template <typename F>
     198             :   typename EnableIf<TakeArgs<F>::value, void>::Type
     199           0 :   ApplyWithArgsImpl(const F& aFunc, As&&... aEvents)
     200             :   {
     201           0 :     aFunc(Move(aEvents)...);
     202           0 :   }
     203             : 
     204             :   // |F| takes no arguments.
     205             :   template <typename F>
     206             :   typename EnableIf<!TakeArgs<F>::value, void>::Type
     207             :   ApplyWithArgsImpl(const F& aFunc, As&&... aEvents)
     208             :   {
     209             :     MOZ_CRASH("Call ApplyWithNoArgs instead.");
     210             :   }
     211             : 
     212           0 :   void ApplyWithArgs(As&&... aEvents) override
     213             :   {
     214           0 :     MOZ_RELEASE_ASSERT(TakeArgs<Function>::value);
     215             :     // Don't call the listener if it is disconnected.
     216           0 :     if (!RevocableToken::IsRevoked()) {
     217           0 :       ApplyWithArgsImpl(mFunction, Move(aEvents)...);
     218             :     }
     219           0 :   }
     220             : 
     221             :   // |F| takes one or more arguments.
     222             :   template <typename F>
     223             :   typename EnableIf<TakeArgs<F>::value, void>::Type
     224             :   ApplyWithNoArgsImpl(const F& aFunc)
     225             :   {
     226             :     MOZ_CRASH("Call ApplyWithArgs instead.");
     227             :   }
     228             : 
     229             :   // |F| takes no arguments.
     230             :   template <typename F>
     231             :   typename EnableIf<!TakeArgs<F>::value, void>::Type
     232           0 :   ApplyWithNoArgsImpl(const F& aFunc)
     233             :   {
     234           0 :     aFunc();
     235           0 :   }
     236             : 
     237           0 :   virtual void ApplyWithNoArgs() override
     238             :   {
     239           0 :     MOZ_RELEASE_ASSERT(!TakeArgs<Function>::value);
     240             :     // Don't call the listener if it is disconnected.
     241           0 :     if (!RevocableToken::IsRevoked()) {
     242           0 :       ApplyWithNoArgsImpl(mFunction);
     243             :     }
     244           0 :   }
     245             : 
     246             :   const RefPtr<Target> mTarget;
     247             :   FunctionStorage mFunction;
     248             : };
     249             : 
     250             : /**
     251             :  * Return true if any type is a reference type.
     252             :  */
     253             : template <typename Head, typename... Tails>
     254             : struct IsAnyReference {
     255             :   static const bool value = IsReference<Head>::value ||
     256             :                             IsAnyReference<Tails...>::value;
     257             : };
     258             : 
     259             : template <typename T>
     260             : struct IsAnyReference<T> {
     261             :   static const bool value = IsReference<T>::value;
     262             : };
     263             : 
     264             : } // namespace detail
     265             : 
     266             : template <ListenerPolicy, typename... Ts> class MediaEventSourceImpl;
     267             : 
     268             : /**
     269             :  * Not thread-safe since this is not meant to be shared and therefore only
     270             :  * move constructor is provided. Used to hold the result of
     271             :  * MediaEventSource<T>::Connect() and call Disconnect() to disconnect the
     272             :  * listener from an event source.
     273             :  */
     274             : class MediaEventListener {
     275             :   template <ListenerPolicy, typename... Ts>
     276             :   friend class MediaEventSourceImpl;
     277             : 
     278             : public:
     279           1 :   MediaEventListener() {}
     280             : 
     281             :   MediaEventListener(MediaEventListener&& aOther)
     282             :     : mToken(Move(aOther.mToken)) {}
     283             : 
     284           0 :   MediaEventListener& operator=(MediaEventListener&& aOther) {
     285           0 :     MOZ_ASSERT(!mToken, "Must disconnect the listener.");
     286           0 :     mToken = Move(aOther.mToken);
     287           0 :     return *this;
     288             :   }
     289             : 
     290           0 :   ~MediaEventListener() {
     291           0 :     MOZ_ASSERT(!mToken, "Must disconnect the listener.");
     292           0 :   }
     293             : 
     294           0 :   void Disconnect() {
     295           0 :     mToken->Revoke();
     296           0 :     mToken = nullptr;
     297           0 :   }
     298             : 
     299           0 :   void DisconnectIfExists() {
     300           0 :     if (mToken) {
     301           0 :       Disconnect();
     302             :     }
     303           0 :   }
     304             : 
     305             : private:
     306             :   // Avoid exposing RevocableToken directly to the client code so that
     307             :   // listeners can be disconnected in a controlled manner.
     308           0 :   explicit MediaEventListener(RevocableToken* aToken) : mToken(aToken) {}
     309             :   RefPtr<RevocableToken> mToken;
     310             : };
     311             : 
     312             : /**
     313             :  * A generic and thread-safe class to implement the observer pattern.
     314             :  */
     315             : template <ListenerPolicy Lp, typename... Es>
     316           0 : class MediaEventSourceImpl {
     317             :   static_assert(!detail::IsAnyReference<Es...>::value,
     318             :                 "Ref-type not supported!");
     319             : 
     320             :   template <typename T>
     321             :   using ArgType = typename detail::EventTypeTraits<T>::ArgType;
     322             : 
     323             :   typedef detail::Listener<ArgType<Es>...> Listener;
     324             : 
     325             :   template<typename Target, typename Func>
     326             :   using ListenerImpl = detail::ListenerImpl<Target, Func, ArgType<Es>...>;
     327             : 
     328             :   template <typename Method>
     329             :   using TakeArgs = detail::TakeArgs<Method>;
     330             : 
     331           0 :   void PruneListeners() {
     332           0 :     int32_t last = static_cast<int32_t>(mListeners.Length()) - 1;
     333           0 :     for (int32_t i = last; i >= 0; --i) {
     334           0 :       if (mListeners[i]->IsRevoked()) {
     335           0 :         mListeners.RemoveElementAt(i);
     336             :       }
     337             :     }
     338           0 :   }
     339             : 
     340             :   template<typename Target, typename Function>
     341             :   MediaEventListener
     342           0 :   ConnectInternal(Target* aTarget, Function&& aFunction) {
     343           0 :     MutexAutoLock lock(mMutex);
     344           0 :     PruneListeners();
     345           0 :     MOZ_ASSERT(Lp == ListenerPolicy::NonExclusive || mListeners.IsEmpty());
     346           0 :     auto l = mListeners.AppendElement();
     347           0 :     *l = new ListenerImpl<Target, Function>(
     348             :       aTarget, Forward<Function>(aFunction));
     349           0 :     return MediaEventListener(*l);
     350             :   }
     351             : 
     352             :   // |Method| takes one or more arguments.
     353             :   template <typename Target, typename This, typename Method>
     354             :   typename EnableIf<TakeArgs<Method>::value, MediaEventListener>::Type
     355           0 :   ConnectInternal(Target* aTarget, This* aThis, Method aMethod) {
     356           0 :     detail::RawPtr<This> thiz(aThis);
     357             :     return ConnectInternal(aTarget,
     358           0 :       [=](ArgType<Es>&&... aEvents) {
     359           0 :         (thiz.get()->*aMethod)(Move(aEvents)...);
     360           0 :       });
     361             :   }
     362             : 
     363             :   // |Method| takes no arguments. Don't bother passing the event data.
     364             :   template <typename Target, typename This, typename Method>
     365             :   typename EnableIf<!TakeArgs<Method>::value, MediaEventListener>::Type
     366           0 :   ConnectInternal(Target* aTarget, This* aThis, Method aMethod) {
     367           0 :     detail::RawPtr<This> thiz(aThis);
     368             :     return ConnectInternal(aTarget,
     369           0 :       [=]() {
     370           0 :         (thiz.get()->*aMethod)();
     371           0 :       });
     372             :   }
     373             : 
     374             : public:
     375             :   /**
     376             :    * Register a function to receive notifications from the event source.
     377             :    *
     378             :    * @param aTarget The target thread on which the function will run.
     379             :    * @param aFunction A function to be called on the target thread. The function
     380             :    *                  parameter must be convertible from |EventType|.
     381             :    * @return An object used to disconnect from the event source.
     382             :    */
     383             :   template<typename Function>
     384             :   MediaEventListener
     385           0 :   Connect(AbstractThread* aTarget, Function&& aFunction) {
     386           0 :     return ConnectInternal(aTarget, Forward<Function>(aFunction));
     387             :   }
     388             : 
     389             :   template<typename Function>
     390             :   MediaEventListener
     391             :   Connect(nsIEventTarget* aTarget, Function&& aFunction) {
     392             :     return ConnectInternal(aTarget, Forward<Function>(aFunction));
     393             :   }
     394             : 
     395             :   /**
     396             :    * As above.
     397             :    *
     398             :    * Note we deliberately keep a weak reference to |aThis| in order not to
     399             :    * change its lifetime. This is because notifications are dispatched
     400             :    * asynchronously and removing a listener doesn't always break the reference
     401             :    * cycle for the pending event could still hold a reference to |aThis|.
     402             :    *
     403             :    * The caller must call MediaEventListener::Disconnect() to avoid dangling
     404             :    * pointers.
     405             :    */
     406             :   template <typename This, typename Method>
     407             :   MediaEventListener
     408           0 :   Connect(AbstractThread* aTarget, This* aThis, Method aMethod) {
     409           0 :     return ConnectInternal(aTarget, aThis, aMethod);
     410             :   }
     411             : 
     412             :   template <typename This, typename Method>
     413             :   MediaEventListener
     414             :   Connect(nsIEventTarget* aTarget, This* aThis, Method aMethod) {
     415             :     return ConnectInternal(aTarget, aThis, aMethod);
     416             :   }
     417             : 
     418             : protected:
     419           0 :   MediaEventSourceImpl() : mMutex("MediaEventSourceImpl::mMutex") {}
     420             : 
     421             :   template <typename... Ts>
     422           0 :   void NotifyInternal(Ts&&... aEvents) {
     423           0 :     MutexAutoLock lock(mMutex);
     424           0 :     int32_t last = static_cast<int32_t>(mListeners.Length()) - 1;
     425           0 :     for (int32_t i = last; i >= 0; --i) {
     426           0 :       auto&& l = mListeners[i];
     427             :       // Remove disconnected listeners.
     428             :       // It is not optimal but is simple and works well.
     429           0 :       if (l->IsRevoked()) {
     430           0 :         mListeners.RemoveElementAt(i);
     431           0 :         continue;
     432             :       }
     433           0 :       l->Dispatch(Forward<Ts>(aEvents)...);
     434             :     }
     435           0 :   }
     436             : 
     437             : private:
     438             :   Mutex mMutex;
     439             :   nsTArray<RefPtr<Listener>> mListeners;
     440             : };
     441             : 
     442             : template <typename... Es>
     443             : using MediaEventSource =
     444             :   MediaEventSourceImpl<ListenerPolicy::NonExclusive, Es...>;
     445             : 
     446             : template <typename... Es>
     447             : using MediaEventSourceExc =
     448             :   MediaEventSourceImpl<ListenerPolicy::Exclusive, Es...>;
     449             : 
     450             : /**
     451             :  * A class to separate the interface of event subject (MediaEventSource)
     452             :  * and event publisher. Mostly used as a member variable to publish events
     453             :  * to the listeners.
     454             :  */
     455             : template <typename... Es>
     456           0 : class MediaEventProducer : public MediaEventSource<Es...> {
     457             : public:
     458             :   template <typename... Ts>
     459           0 :   void Notify(Ts&&... aEvents) {
     460             :     // Pass lvalues to prevent move in NonExclusive mode.
     461           0 :     this->NotifyInternal(aEvents...);
     462           0 :   }
     463             : };
     464             : 
     465             : /**
     466             :  * Specialization for void type. A dummy bool is passed to NotifyInternal
     467             :  * since there is no way to pass a void value.
     468             :  */
     469             : template <>
     470           0 : class MediaEventProducer<void> : public MediaEventSource<void> {
     471             : public:
     472           0 :   void Notify() {
     473           0 :     this->NotifyInternal(true /* dummy */);
     474           0 :   }
     475             : };
     476             : 
     477             : /**
     478             :  * A producer allowing at most one listener.
     479             :  */
     480             : template <typename... Es>
     481           0 : class MediaEventProducerExc : public MediaEventSourceExc<Es...> {
     482             : public:
     483             :   template <typename... Ts>
     484           0 :   void Notify(Ts&&... aEvents) {
     485           0 :     this->NotifyInternal(Forward<Ts>(aEvents)...);
     486           0 :   }
     487             : };
     488             : 
     489             : } // namespace mozilla
     490             : 
     491             : #endif //MediaEventSource_h_

Generated by: LCOV version 1.13