LCOV - code coverage report
Current view: top level - dom/media/systemservices - MediaUtils.h (source / functions) Hit Total Coverage
Test: output.info Lines: 0 78 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 355 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim: set sw=2 ts=8 et ft=cpp : */
       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 file,
       5             :  * You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #ifndef mozilla_MediaUtils_h
       8             : #define mozilla_MediaUtils_h
       9             : 
      10             : #include "nsThreadUtils.h"
      11             : #include "nsIAsyncShutdown.h"
      12             : #include "mozilla/UniquePtr.h"
      13             : 
      14             : namespace mozilla {
      15             : namespace media {
      16             : 
      17             : /*
      18             :  * media::Pledge - A promise-like pattern for c++ that takes lambda functions.
      19             :  *
      20             :  * Asynchronous APIs that proxy to another thread or to the chrome process and
      21             :  * back may find it useful to return a pledge to callers who then use
      22             :  * pledge.Then(func) to specify a lambda function to be invoked with the result
      23             :  * later back on this same thread.
      24             :  *
      25             :  * Callers will enjoy that lambdas allow "capturing" of local variables, much
      26             :  * like closures in JavaScript (safely by-copy by default).
      27             :  *
      28             :  * Callers will also enjoy that they do not need to be thread-safe (their code
      29             :  * runs on the same thread after all).
      30             :  *
      31             :  * Advantageously, pledges are non-threadsafe by design (because locking and
      32             :  * event queues are redundant). This means none of the lambdas you pass in,
      33             :  * or variables you lambda-capture into them, need be threasafe or support
      34             :  * threadsafe refcounting. After all, they'll run later on the same thread.
      35             :  *
      36             :  *   RefPtr<media::Pledge<Foo>> p = GetFooAsynchronously(); // returns a pledge
      37             :  *   p->Then([](const Foo& foo) {
      38             :  *     // use foo here (same thread. Need not be thread-safe!)
      39             :  *   });
      40             :  *
      41             :  * See media::CoatCheck below for an example of GetFooAsynchronously().
      42             :  */
      43             : 
      44           0 : class PledgeBase
      45             : {
      46             : public:
      47           0 :   NS_INLINE_DECL_REFCOUNTING(PledgeBase);
      48             : protected:
      49           0 :   virtual ~PledgeBase() {};
      50             : };
      51             : 
      52             : template<typename ValueType, typename ErrorType = nsresult>
      53             : class Pledge : public PledgeBase
      54             : {
      55             :   // TODO: Remove workaround once mozilla allows std::function from <functional>
      56             :   // wo/std::function support, do template + virtual trick to accept lambdas
      57             :   class FunctorsBase
      58             :   {
      59             :   public:
      60           0 :     FunctorsBase() {}
      61             :     virtual void Succeed(ValueType& result) = 0;
      62             :     virtual void Fail(ErrorType& error) = 0;
      63           0 :     virtual ~FunctorsBase() {};
      64             :   };
      65             : 
      66             : public:
      67           0 :   explicit Pledge() : mDone(false), mRejected(false) {}
      68             :   Pledge(const Pledge& aOther) = delete;
      69             :   Pledge& operator = (const Pledge&) = delete;
      70             : 
      71             :   template<typename OnSuccessType>
      72           0 :   void Then(OnSuccessType&& aOnSuccess)
      73             :   {
      74           0 :     Then(Forward<OnSuccessType>(aOnSuccess), [](ErrorType&){});
      75           0 :   }
      76             : 
      77             :   template<typename OnSuccessType, typename OnFailureType>
      78           0 :   void Then(OnSuccessType&& aOnSuccess, OnFailureType&& aOnFailure)
      79             :   {
      80           0 :     class Functors : public FunctorsBase
      81             :     {
      82             :     public:
      83           0 :       Functors(OnSuccessType&& aOnSuccessRef, OnFailureType&& aOnFailureRef)
      84           0 :         : mOnSuccess(Move(aOnSuccessRef)), mOnFailure(Move(aOnFailureRef)) {}
      85             : 
      86           0 :       void Succeed(ValueType& result)
      87             :       {
      88           0 :         mOnSuccess(result);
      89           0 :       }
      90           0 :       void Fail(ErrorType& error)
      91             :       {
      92           0 :         mOnFailure(error);
      93           0 :       };
      94             : 
      95             :       OnSuccessType mOnSuccess;
      96             :       OnFailureType mOnFailure;
      97             :     };
      98           0 :     mFunctors = MakeUnique<Functors>(Forward<OnSuccessType>(aOnSuccess),
      99             :                                      Forward<OnFailureType>(aOnFailure));
     100           0 :     if (mDone) {
     101           0 :       if (!mRejected) {
     102           0 :         mFunctors->Succeed(mValue);
     103             :       } else {
     104           0 :         mFunctors->Fail(mError);
     105             :       }
     106             :     }
     107           0 :   }
     108             : 
     109           0 :   void Resolve(const ValueType& aValue)
     110             :   {
     111           0 :     mValue = aValue;
     112           0 :     Resolve();
     113           0 :   }
     114             : 
     115           0 :   void Reject(ErrorType rv)
     116             :   {
     117           0 :     if (!mDone) {
     118           0 :       mDone = mRejected = true;
     119           0 :       mError = rv;
     120           0 :       if (mFunctors) {
     121           0 :         mFunctors->Fail(mError);
     122             :       }
     123             :     }
     124           0 :   }
     125             : 
     126             : protected:
     127           0 :   void Resolve()
     128             :   {
     129           0 :     if (!mDone) {
     130           0 :       mDone = true;
     131           0 :       MOZ_ASSERT(!mRejected);
     132           0 :       if (mFunctors) {
     133           0 :         mFunctors->Succeed(mValue);
     134             :       }
     135             :     }
     136           0 :   }
     137             : 
     138             :   ValueType mValue;
     139             : private:
     140           0 :   ~Pledge() {};
     141             :   bool mDone;
     142             :   bool mRejected;
     143             :   ErrorType mError;
     144             :   UniquePtr<FunctorsBase> mFunctors;
     145             : };
     146             : 
     147             : /* media::NewRunnableFrom() - Create a Runnable from a lambda.
     148             :  *
     149             :  * Passing variables (closures) to an async function is clunky with Runnable:
     150             :  *
     151             :  *   void Foo()
     152             :  *   {
     153             :  *     class FooRunnable : public Runnable
     154             :  *     {
     155             :  *     public:
     156             :  *       FooRunnable(const Bar &aBar) : mBar(aBar) {}
     157             :  *       NS_IMETHOD Run() override
     158             :  *       {
     159             :  *         // Use mBar
     160             :  *       }
     161             :  *     private:
     162             :  *       RefPtr<Bar> mBar;
     163             :  *     };
     164             :  *
     165             :  *     RefPtr<Bar> bar = new Bar();
     166             :  *     NS_DispatchToMainThread(new FooRunnable(bar);
     167             :  *   }
     168             :  *
     169             :  * It's worse with more variables. Lambdas have a leg up with variable capture:
     170             :  *
     171             :  *   void Foo()
     172             :  *   {
     173             :  *     RefPtr<Bar> bar = new Bar();
     174             :  *     NS_DispatchToMainThread(media::NewRunnableFrom([bar]() mutable {
     175             :  *       // use bar
     176             :  *     }));
     177             :  *   }
     178             :  *
     179             :  * Capture is by-copy by default, so the nsRefPtr 'bar' is safely copied for
     180             :  * access on the other thread (threadsafe refcounting in bar is assumed).
     181             :  *
     182             :  * The 'mutable' keyword is only needed for non-const access to bar.
     183             :  */
     184             : 
     185             : template<typename OnRunType>
     186           0 : class LambdaRunnable : public Runnable
     187             : {
     188             : public:
     189           0 :   explicit LambdaRunnable(OnRunType&& aOnRun)
     190             :     : Runnable("media::LambdaRunnable")
     191           0 :     , mOnRun(Move(aOnRun))
     192             :   {
     193           0 :   }
     194             : 
     195             : private:
     196             :   NS_IMETHODIMP
     197           0 :   Run() override
     198             :   {
     199           0 :     return mOnRun();
     200             :   }
     201             :   OnRunType mOnRun;
     202             : };
     203             : 
     204             : template<typename OnRunType>
     205             : already_AddRefed<LambdaRunnable<OnRunType>>
     206           0 : NewRunnableFrom(OnRunType&& aOnRun)
     207             : {
     208             :   typedef LambdaRunnable<OnRunType> LambdaType;
     209           0 :   RefPtr<LambdaType> lambda = new LambdaType(Forward<OnRunType>(aOnRun));
     210           0 :   return lambda.forget();
     211             : }
     212             : 
     213             : /* media::CoatCheck - There and back again. Park an object in exchange for an id.
     214             :  *
     215             :  * A common problem with calling asynchronous functions that do work on other
     216             :  * threads or processes is how to pass in a heap object for use once the
     217             :  * function completes, without requiring that object to have threadsafe
     218             :  * refcounting, contain mutexes, be marshaled, or leak if things fail
     219             :  * (or worse, intermittent use-after-free because of lifetime issues).
     220             :  *
     221             :  * One solution is to set up a coat-check on the caller side, park your object
     222             :  * in exchange for an id, and send the id. Common in IPC, but equally useful
     223             :  * for same-process thread-hops, because by never leaving the thread there's
     224             :  * no need for objects to be threadsafe or use threadsafe refcounting. E.g.
     225             :  *
     226             :  *   class FooDoer
     227             :  *   {
     228             :  *     CoatCheck<Foo> mOutstandingFoos;
     229             :  *
     230             :  *   public:
     231             :  *     void DoFoo()
     232             :  *     {
     233             :  *       RefPtr<Foo> foo = new Foo();
     234             :  *       uint32_t requestId = mOutstandingFoos.Append(*foo);
     235             :  *       sChild->SendFoo(requestId);
     236             :  *     }
     237             :  *
     238             :  *     void RecvFooResponse(uint32_t requestId)
     239             :  *     {
     240             :  *       RefPtr<Foo> foo = mOutstandingFoos.Remove(requestId);
     241             :  *       if (foo) {
     242             :  *         // use foo
     243             :  *       }
     244             :  *     }
     245             :  *   };
     246             :  *
     247             :  * If you read media::Pledge earlier, here's how this is useful for pledges:
     248             :  *
     249             :  *   class FooGetter
     250             :  *   {
     251             :  *     CoatCheck<Pledge<Foo>> mOutstandingPledges;
     252             :  *
     253             :  *   public:
     254             :  *     already_addRefed<Pledge<Foo>> GetFooAsynchronously()
     255             :  *     {
     256             :  *       RefPtr<Pledge<Foo>> p = new Pledge<Foo>();
     257             :  *       uint32_t requestId = mOutstandingPledges.Append(*p);
     258             :  *       sChild->SendFoo(requestId);
     259             :  *       return p.forget();
     260             :  *     }
     261             :  *
     262             :  *     void RecvFooResponse(uint32_t requestId, const Foo& fooResult)
     263             :  *     {
     264             :  *       RefPtr<Foo> p = mOutstandingPledges.Remove(requestId);
     265             :  *       if (p) {
     266             :  *         p->Resolve(fooResult);
     267             :  *       }
     268             :  *     }
     269             :  *   };
     270             :  *
     271             :  * This helper is currently optimized for very small sets (i.e. not optimized).
     272             :  * It is also not thread-safe as the whole point is to stay on the same thread.
     273             :  */
     274             : 
     275             : template<class T>
     276           0 : class CoatCheck
     277             : {
     278             : public:
     279             :   typedef std::pair<uint32_t, RefPtr<T>> Element;
     280             : 
     281           0 :   uint32_t Append(T& t)
     282             :   {
     283           0 :     uint32_t id = GetNextId();
     284           0 :     mElements.AppendElement(Element(id, RefPtr<T>(&t)));
     285           0 :     return id;
     286             :   }
     287             : 
     288           0 :   already_AddRefed<T> Remove(uint32_t aId)
     289             :   {
     290           0 :     for (auto& element : mElements) {
     291           0 :       if (element.first == aId) {
     292           0 :         RefPtr<T> ref;
     293           0 :         ref.swap(element.second);
     294           0 :         mElements.RemoveElement(element);
     295           0 :         return ref.forget();
     296             :       }
     297             :     }
     298           0 :     MOZ_ASSERT_UNREACHABLE("Received id with no matching parked object!");
     299             :     return nullptr;
     300             :   }
     301             : 
     302             : private:
     303           0 :   static uint32_t GetNextId()
     304             :   {
     305             :     static uint32_t counter = 0;
     306           0 :     return ++counter;
     307             :   };
     308             :   AutoTArray<Element, 3> mElements;
     309             : };
     310             : 
     311             : /* media::Refcountable - Add threadsafe ref-counting to something that isn't.
     312             :  *
     313             :  * Often, reference counting is the most practical way to share an object with
     314             :  * another thread without imposing lifetime restrictions, even if there's
     315             :  * otherwise no concurrent access happening on the object.  For instance, an
     316             :  * algorithm on another thread may find it more expedient to modify a passed-in
     317             :  * object, rather than pass expensive copies back and forth.
     318             :  *
     319             :  * Lists in particular often aren't ref-countable, yet are expensive to copy,
     320             :  * e.g. nsTArray<RefPtr<Foo>>. Refcountable can be used to make such objects
     321             :  * (or owning smart-pointers to such objects) refcountable.
     322             :  *
     323             :  * Technical limitation: A template specialization is needed for types that take
     324             :  * a constructor. Please add below (UniquePtr covers a lot of ground though).
     325             :  */
     326             : 
     327             : template<typename T>
     328             : class Refcountable : public T
     329             : {
     330             : public:
     331             :   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Refcountable<T>)
     332             : private:
     333             :   ~Refcountable<T>() {}
     334             : };
     335             : 
     336             : template<typename T>
     337             : class Refcountable<UniquePtr<T>> : public UniquePtr<T>
     338             : {
     339             : public:
     340           0 :   explicit Refcountable<UniquePtr<T>>(T* aPtr) : UniquePtr<T>(aPtr) {}
     341           0 :   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Refcountable<T>)
     342             : private:
     343           0 :   ~Refcountable<UniquePtr<T>>() {}
     344             : };
     345             : 
     346             : /* media::ShutdownBlocker - Async shutdown helper.
     347             :  */
     348             : 
     349             : class ShutdownBlocker : public nsIAsyncShutdownBlocker
     350             : {
     351             : public:
     352           0 :   ShutdownBlocker(const nsString& aName) : mName(aName) {}
     353             : 
     354             :   NS_IMETHOD
     355             :   BlockShutdown(nsIAsyncShutdownClient* aProfileBeforeChange) override = 0;
     356             : 
     357           0 :   NS_IMETHOD GetName(nsAString& aName) override
     358             :   {
     359           0 :     aName = mName;
     360           0 :     return NS_OK;
     361             :   }
     362             : 
     363           0 :   NS_IMETHOD GetState(nsIPropertyBag**) override
     364             :   {
     365           0 :     return NS_OK;
     366             :   }
     367             : 
     368             :   NS_DECL_ISUPPORTS
     369             : protected:
     370           0 :   virtual ~ShutdownBlocker() {}
     371             : private:
     372             :   const nsString mName;
     373             : };
     374             : 
     375             : } // namespace media
     376             : } // namespace mozilla
     377             : 
     378             : #endif // mozilla_MediaUtils_h

Generated by: LCOV version 1.13