tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

MediaUtils.h (11894B)


      1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=2 et sw=2 tw=80: */
      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 "MediaEventSource.h"
     11 #include "mozilla/Assertions.h"
     12 #include "mozilla/Monitor.h"
     13 #include "mozilla/MozPromise.h"
     14 #include "mozilla/Mutex.h"
     15 #include "mozilla/RefPtr.h"
     16 #include "mozilla/SharedThreadPool.h"
     17 #include "mozilla/TaskQueue.h"
     18 #include "mozilla/UniquePtr.h"
     19 #include "nsCOMPtr.h"
     20 #include "nsIAsyncShutdown.h"
     21 #include "nsISupportsImpl.h"
     22 #include "nsProxyRelease.h"
     23 #include "nsThreadUtils.h"
     24 
     25 class nsIEventTarget;
     26 
     27 namespace mozilla::media {
     28 
     29 /* Utility function, given a string pref and an URI, returns whether or not
     30 * the URI occurs in the pref. Wildcards are supported (e.g. *.example.com)
     31 * and multiple hostnames can be present, separated by commas.
     32 */
     33 bool HostnameInPref(const char* aPrefList, const nsCString& aHostName);
     34 
     35 /* media::NewRunnableFrom() - Create a Runnable from a lambda.
     36 *
     37 * Passing variables (closures) to an async function is clunky with Runnable:
     38 *
     39 *   void Foo()
     40 *   {
     41 *     class FooRunnable : public Runnable
     42 *     {
     43 *     public:
     44 *       FooRunnable(const Bar &aBar) : mBar(aBar) {}
     45 *       NS_IMETHOD Run() override
     46 *       {
     47 *         // Use mBar
     48 *       }
     49 *     private:
     50 *       RefPtr<Bar> mBar;
     51 *     };
     52 *
     53 *     RefPtr<Bar> bar = new Bar();
     54 *     NS_DispatchToMainThread(new FooRunnable(bar);
     55 *   }
     56 *
     57 * It's worse with more variables. Lambdas have a leg up with variable capture:
     58 *
     59 *   void Foo()
     60 *   {
     61 *     RefPtr<Bar> bar = new Bar();
     62 *     NS_DispatchToMainThread(media::NewRunnableFrom([bar]() mutable {
     63 *       // use bar
     64 *     }));
     65 *   }
     66 *
     67 * Capture is by-copy by default, so the nsRefPtr 'bar' is safely copied for
     68 * access on the other thread (threadsafe refcounting in bar is assumed).
     69 *
     70 * The 'mutable' keyword is only needed for non-const access to bar.
     71 */
     72 
     73 template <typename OnRunType>
     74 class LambdaRunnable : public Runnable {
     75 public:
     76  explicit LambdaRunnable(OnRunType&& aOnRun)
     77      : Runnable("media::LambdaRunnable"), mOnRun(std::move(aOnRun)) {}
     78 
     79 private:
     80  NS_IMETHODIMP
     81  Run() override { return mOnRun(); }
     82  OnRunType mOnRun;
     83 };
     84 
     85 template <typename OnRunType>
     86 already_AddRefed<LambdaRunnable<OnRunType>> NewRunnableFrom(
     87    OnRunType&& aOnRun) {
     88  typedef LambdaRunnable<OnRunType> LambdaType;
     89  RefPtr<LambdaType> lambda = new LambdaType(std::forward<OnRunType>(aOnRun));
     90  return lambda.forget();
     91 }
     92 
     93 /* media::Refcountable - Add threadsafe ref-counting to something that isn't.
     94 *
     95 * Often, reference counting is the most practical way to share an object with
     96 * another thread without imposing lifetime restrictions, even if there's
     97 * otherwise no concurrent access happening on the object.  For instance, an
     98 * algorithm on another thread may find it more expedient to modify a passed-in
     99 * object, rather than pass expensive copies back and forth.
    100 *
    101 * Lists in particular often aren't ref-countable, yet are expensive to copy,
    102 * e.g. nsTArray<RefPtr<Foo>>. Refcountable can be used to make such objects
    103 * (or owning smart-pointers to such objects) refcountable.
    104 *
    105 * Technical limitation: A template specialization is needed for types that take
    106 * a constructor. Please add below (UniquePtr covers a lot of ground though).
    107 */
    108 
    109 class RefcountableBase {
    110 public:
    111  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RefcountableBase)
    112 protected:
    113  virtual ~RefcountableBase() = default;
    114 };
    115 
    116 template <typename T>
    117 class Refcountable : public T, public RefcountableBase {
    118 public:
    119  Refcountable& operator=(T&& aOther) {
    120    T::operator=(std::move(aOther));
    121    return *this;
    122  }
    123 
    124  Refcountable& operator=(T& aOther) {
    125    T::operator=(aOther);
    126    return *this;
    127  }
    128 };
    129 
    130 template <typename T>
    131 class Refcountable<UniquePtr<T>> : public UniquePtr<T>,
    132                                   public RefcountableBase {
    133 public:
    134  explicit Refcountable(T* aPtr) : UniquePtr<T>(aPtr) {}
    135 };
    136 
    137 template <>
    138 class Refcountable<bool> : public RefcountableBase {
    139 public:
    140  explicit Refcountable(bool aValue) : mValue(aValue) {}
    141 
    142  Refcountable& operator=(bool aOther) {
    143    mValue = aOther;
    144    return *this;
    145  }
    146 
    147  Refcountable& operator=(const Refcountable& aOther) {
    148    mValue = aOther.mValue;
    149    return *this;
    150  }
    151 
    152  explicit operator bool() const { return mValue; }
    153 
    154 private:
    155  bool mValue;
    156 };
    157 
    158 /*
    159 * Async shutdown helpers
    160 */
    161 
    162 nsCOMPtr<nsIAsyncShutdownClient> GetShutdownBarrier();
    163 
    164 // Like GetShutdownBarrier but will release assert that the result is not null.
    165 nsCOMPtr<nsIAsyncShutdownClient> MustGetShutdownBarrier();
    166 
    167 class ShutdownBlocker : public nsIAsyncShutdownBlocker {
    168 public:
    169  ShutdownBlocker(const nsAString& aName) : mName(aName) {}
    170 
    171  NS_IMETHOD
    172  BlockShutdown(nsIAsyncShutdownClient* aProfileBeforeChange) override = 0;
    173 
    174  NS_IMETHOD GetName(nsAString& aName) override {
    175    aName = mName;
    176    return NS_OK;
    177  }
    178 
    179  NS_IMETHOD GetState(nsIPropertyBag**) override { return NS_OK; }
    180 
    181  NS_DECL_THREADSAFE_ISUPPORTS
    182 protected:
    183  virtual ~ShutdownBlocker() = default;
    184 
    185 private:
    186  const nsString mName;
    187 };
    188 
    189 /**
    190 * A convenience class representing a "ticket" that keeps the process from
    191 * shutting down until it is destructed. It does this by blocking
    192 * xpcom-will-shutdown. Constructed and destroyed on any thread.
    193 */
    194 class ShutdownBlockingTicket {
    195 public:
    196  using ShutdownMozPromise = MozPromise<bool, bool, false>;
    197 
    198  /**
    199   * Construct with an arbitrary name, __FILE__ and __LINE__.
    200   * Note that __FILE__ needs to be made wide, typically through
    201   * NS_LITERAL_STRING_FROM_CSTRING(__FILE__).
    202   * Returns nullptr if we are too far in the shutdown sequence to add a
    203   * blocker. Any thread.
    204   */
    205  static UniquePtr<ShutdownBlockingTicket> Create(const nsAString& aName,
    206                                                  const nsAString& aFileName,
    207                                                  int32_t aLineNr);
    208 
    209  virtual ~ShutdownBlockingTicket() = default;
    210 
    211  /**
    212   * MozPromise that gets resolved upon xpcom-will-shutdown.
    213   * Should the ticket get destroyed before the MozPromise has been resolved,
    214   * the MozPromise will get rejected.
    215   */
    216  virtual ShutdownMozPromise* ShutdownPromise() = 0;
    217 };
    218 
    219 /**
    220 * A convenience class intended to be subclassed by DOM objects wanting to be
    221 * notified when its owning thread shutdown is about to occur.
    222 */
    223 class ShutdownConsumer {
    224 public:
    225  /**
    226   * On the main thread, this is called with the xpcom-will-shutdown event.
    227   * On a worker thread, this is called by the WeakWorkerRef callback.
    228   */
    229  virtual void OnShutdown() = 0;
    230 };
    231 
    232 /**
    233 * A convenience class intended to be held by DOM objects wanting to be notified
    234 * when its owning thread shutdown is about to occur.
    235 */
    236 class ShutdownWatcher : public nsISupports {
    237 public:
    238  /**
    239   * Create a shutdown watcher for the given consumer.
    240   */
    241  static already_AddRefed<ShutdownWatcher> Create(ShutdownConsumer* aConsumer);
    242 
    243  /**
    244   * Destroy a shutdown watcher. Must be called by the owning object prior to
    245   * clearing its reference.
    246   */
    247  virtual void Destroy() = 0;
    248 
    249 protected:
    250  explicit ShutdownWatcher(ShutdownConsumer* aConsumer) : mConsumer(aConsumer) {
    251    MOZ_ASSERT(aConsumer);
    252  }
    253 
    254  virtual ~ShutdownWatcher() { MOZ_ASSERT(!mConsumer); }
    255 
    256  ShutdownConsumer* mConsumer;
    257 };
    258 
    259 /**
    260 * Await convenience methods to block until the promise has been resolved or
    261 * rejected. The Resolve/Reject functions, while called on a different thread,
    262 * would be running just as on the current thread thanks to the memory barrier
    263 * provided by the monitor.
    264 * For now Await can only be used with an exclusive MozPromise if passed a
    265 * Resolve/Reject function.
    266 * Await() can *NOT* be called from a task queue/nsISerialEventTarget used for
    267 * resolving/rejecting aPromise, otherwise things will deadlock.
    268 */
    269 template <typename ResolveValueType, typename RejectValueType,
    270          typename ResolveFunction, typename RejectFunction>
    271 void Await(already_AddRefed<nsIEventTarget> aPool,
    272           RefPtr<MozPromise<ResolveValueType, RejectValueType, true>> aPromise,
    273           ResolveFunction&& aResolveFunction,
    274           RejectFunction&& aRejectFunction) {
    275  RefPtr<TaskQueue> taskQueue =
    276      TaskQueue::Create(std::move(aPool), "MozPromiseAwait");
    277  Monitor mon MOZ_UNANNOTATED(__func__);
    278  bool done = false;
    279 
    280  aPromise->Then(
    281      taskQueue, __func__,
    282      [&](ResolveValueType&& aResolveValue) {
    283        MonitorAutoLock lock(mon);
    284        aResolveFunction(std::forward<ResolveValueType>(aResolveValue));
    285        done = true;
    286        mon.Notify();
    287      },
    288      [&](RejectValueType&& aRejectValue) {
    289        MonitorAutoLock lock(mon);
    290        aRejectFunction(std::forward<RejectValueType>(aRejectValue));
    291        done = true;
    292        mon.Notify();
    293      });
    294 
    295  MonitorAutoLock lock(mon);
    296  while (!done) {
    297    mon.Wait();
    298  }
    299 }
    300 
    301 template <typename ResolveValueType, typename RejectValueType, bool Excl>
    302 typename MozPromise<ResolveValueType, RejectValueType,
    303                    Excl>::ResolveOrRejectValue
    304 Await(already_AddRefed<nsIEventTarget> aPool,
    305      RefPtr<MozPromise<ResolveValueType, RejectValueType, Excl>> aPromise) {
    306  RefPtr<TaskQueue> taskQueue =
    307      TaskQueue::Create(std::move(aPool), "MozPromiseAwait");
    308  Monitor mon MOZ_UNANNOTATED(__func__);
    309  bool done = false;
    310 
    311  typename MozPromise<ResolveValueType, RejectValueType,
    312                      Excl>::ResolveOrRejectValue val;
    313  aPromise->Then(
    314      taskQueue, __func__,
    315      [&](ResolveValueType aResolveValue) {
    316        val.SetResolve(std::move(aResolveValue));
    317        MonitorAutoLock lock(mon);
    318        done = true;
    319        mon.Notify();
    320      },
    321      [&](RejectValueType aRejectValue) {
    322        val.SetReject(std::move(aRejectValue));
    323        MonitorAutoLock lock(mon);
    324        done = true;
    325        mon.Notify();
    326      });
    327 
    328  MonitorAutoLock lock(mon);
    329  while (!done) {
    330    mon.Wait();
    331  }
    332 
    333  return val;
    334 }
    335 
    336 /**
    337 * Similar to Await, takes an array of promises of the same type.
    338 * MozPromise::All is used to handle the resolution/rejection of the promises.
    339 */
    340 template <typename ResolveValueType, typename RejectValueType,
    341          typename ResolveFunction, typename RejectFunction>
    342 void AwaitAll(
    343    already_AddRefed<nsIEventTarget> aPool,
    344    nsTArray<RefPtr<MozPromise<ResolveValueType, RejectValueType, true>>>&
    345        aPromises,
    346    ResolveFunction&& aResolveFunction, RejectFunction&& aRejectFunction) {
    347  typedef MozPromise<ResolveValueType, RejectValueType, true> Promise;
    348  RefPtr<nsIEventTarget> pool = aPool;
    349  RefPtr<TaskQueue> taskQueue =
    350      TaskQueue::Create(do_AddRef(pool), "MozPromiseAwaitAll");
    351  RefPtr<typename Promise::AllPromiseType> p =
    352      Promise::All(taskQueue, aPromises);
    353  Await(pool.forget(), p, std::move(aResolveFunction),
    354        std::move(aRejectFunction));
    355 }
    356 
    357 // Note: only works with exclusive MozPromise, as Promise::All would attempt
    358 // to perform copy of nsTArrays which are disallowed.
    359 template <typename ResolveValueType, typename RejectValueType>
    360 typename MozPromise<ResolveValueType, RejectValueType,
    361                    true>::AllPromiseType::ResolveOrRejectValue
    362 AwaitAll(already_AddRefed<nsIEventTarget> aPool,
    363         nsTArray<RefPtr<MozPromise<ResolveValueType, RejectValueType, true>>>&
    364             aPromises) {
    365  typedef MozPromise<ResolveValueType, RejectValueType, true> Promise;
    366  RefPtr<nsIEventTarget> pool = aPool;
    367  RefPtr<TaskQueue> taskQueue =
    368      TaskQueue::Create(do_AddRef(pool), "MozPromiseAwaitAll");
    369  RefPtr<typename Promise::AllPromiseType> p =
    370      Promise::All(taskQueue, aPromises);
    371  return Await(pool.forget(), p);
    372 }
    373 
    374 }  // namespace mozilla::media
    375 
    376 #endif  // mozilla_MediaUtils_h