tor-browser

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

WaitFor.h (9618B)


      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
      5 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
      6 
      7 #ifndef TESTING_GTEST_MOZILLA_WAITFOR_H_
      8 #define TESTING_GTEST_MOZILLA_WAITFOR_H_
      9 
     10 #include "MediaEventSource.h"
     11 #include "mozilla/media/MediaUtils.h"
     12 #include "mozilla/Maybe.h"
     13 #include "mozilla/MozPromise.h"
     14 #include "mozilla/SpinEventLoopUntil.h"
     15 
     16 namespace mozilla {
     17 
     18 /**
     19 * Waits for an occurrence of aEvent on the current thread (by blocking it,
     20 * except tasks added to the event loop may run) and returns the event's
     21 * templated value, if it's non-void.
     22 *
     23 * The caller must be wary of eventloop issues, in
     24 * particular cases where we rely on a stable state runnable, but there is never
     25 * a task to trigger stable state. In such cases it is the responsibility of the
     26 * caller to create the needed tasks, as JS would. A noteworthy API that relies
     27 * on stable state is MediaTrackGraph::GetInstance.
     28 */
     29 template <ListenerPolicy Lp, typename First, typename... Rest>
     30 inline auto WaitFor(MediaEventSourceImpl<Lp, First, Rest...>& aEvent) {
     31  constexpr size_t num_params = 1 + sizeof...(Rest);
     32  using Storage =
     33      std::conditional_t<num_params == 1, First, std::tuple<First, Rest...>>;
     34  Maybe<Storage> value;
     35  if constexpr (Lp == ListenerPolicy::NonExclusive) {
     36    MediaEventListener listener =
     37        aEvent.Connect(AbstractThread::GetCurrent(),
     38                       [&value](const First& aFirst, const Rest&... aRest) {
     39                         if constexpr (num_params == 1) {
     40                           value = Some(aFirst);
     41                         } else {
     42                           value = Some<Storage>({aFirst, aRest...});
     43                         }
     44                       });
     45    SpinEventLoopUntil<ProcessFailureBehavior::IgnoreAndContinue>(
     46        "WaitFor(MediaEventSource<T>& aEvent)"_ns,
     47        [&] { return value.isSome(); });
     48    listener.Disconnect();
     49    return value.value();
     50  } else {
     51    MediaEventListener listener = aEvent.Connect(
     52        AbstractThread::GetCurrent(),
     53        [&value](First&& aFirst, Rest&&... aRest) {
     54          if constexpr (num_params == 1) {
     55            value = Some<Storage>(std::forward<First>(aFirst));
     56          } else {
     57            value = Some<Storage>(
     58                {std::forward<First>(aFirst), std::forward<Rest...>(aRest...)});
     59          }
     60        });
     61    SpinEventLoopUntil<ProcessFailureBehavior::IgnoreAndContinue>(
     62        "WaitFor(MediaEventSource<T>& aEvent)"_ns,
     63        [&] { return value.isSome(); });
     64    listener.Disconnect();
     65    return value.value();
     66  }
     67 }
     68 
     69 /**
     70 * Specialization of WaitFor<T> for void.
     71 */
     72 template <ListenerPolicy Lp>
     73 inline void WaitFor(MediaEventSourceImpl<Lp, void>& aEvent) {
     74  bool done = false;
     75  MediaEventListener listener =
     76      aEvent.Connect(AbstractThread::GetCurrent(), [&] { done = true; });
     77  SpinEventLoopUntil<ProcessFailureBehavior::IgnoreAndContinue>(
     78      "WaitFor(MediaEventSource<void>& aEvent)"_ns, [&] { return done; });
     79  listener.Disconnect();
     80 }
     81 
     82 /**
     83 * Variant of WaitFor that spins the event loop until a MozPromise has either
     84 * been resolved or rejected.  Result accepts R and E only if their types
     85 * differ.  Consider also WaitForResolve() and WaitForReject(), which are
     86 * suitable even when resolve and reject types are the same.
     87 */
     88 template <typename R, typename E, bool Exc>
     89 inline Result<R, E> WaitFor(const RefPtr<MozPromise<R, E, Exc>>& aPromise) {
     90  Maybe<R> success;
     91  Maybe<E> error;
     92  // Use r-value reference for exclusive promises to support move-only types.
     93  using RRef = typename std::conditional_t<Exc, R&&, const R&>;
     94  using ERef = typename std::conditional_t<Exc, E&&, const E&>;
     95  aPromise->Then(
     96      GetCurrentSerialEventTarget(), __func__,
     97      [&](RRef aResult) { success.emplace(std::forward<RRef>(aResult)); },
     98      [&](ERef aError) { error.emplace(std::forward<ERef>(aError)); });
     99  SpinEventLoopUntil<ProcessFailureBehavior::IgnoreAndContinue>(
    100      "WaitFor(const RefPtr<MozPromise<R, E, Exc>>& aPromise)"_ns,
    101      [&] { return success.isSome() || error.isSome(); });
    102  if (success.isSome()) {
    103    return success.extract();
    104  }
    105  return Err(error.extract());
    106 }
    107 
    108 /**
    109 * Variation on WaitFor that spins the event loop until a MozPromise has been
    110 * resolved.
    111 */
    112 template <typename R, typename E, bool Exc>
    113 inline R WaitForResolve(const RefPtr<MozPromise<R, E, Exc>>& aPromise) {
    114  Maybe<R> success;
    115  // Use r-value reference for exclusive promises to support move-only types.
    116  using RRef = typename std::conditional_t<Exc, R&&, const R&>;
    117  using ERef = typename std::conditional_t<Exc, E&&, const E&>;
    118  aPromise->Then(
    119      GetCurrentSerialEventTarget(), __func__,
    120      [&](RRef aResult) { success.emplace(std::forward<RRef>(aResult)); },
    121      [&](ERef aError) { MOZ_CRASH("rejection was not expected"); });
    122  SpinEventLoopUntil<ProcessFailureBehavior::IgnoreAndContinue>(
    123      "WaitForResolve(const RefPtr<MozPromise<R, E, Exc>>& aPromise)"_ns,
    124      [&] { return success.isSome(); });
    125  return success.extract();
    126 }
    127 
    128 /**
    129 * Variation on WaitFor that spins the event loop until a MozPromise has been
    130 * rejected.
    131 */
    132 template <typename R, typename E, bool Exc>
    133 inline E WaitForReject(const RefPtr<MozPromise<R, E, Exc>>& aPromise) {
    134  Maybe<E> error;
    135  // Use r-value reference for exclusive promises to support move-only types.
    136  using RRef = typename std::conditional_t<Exc, R&&, const R&>;
    137  using ERef = typename std::conditional_t<Exc, E&&, const E&>;
    138  aPromise->Then(
    139      GetCurrentSerialEventTarget(), __func__,
    140      [&](RRef aResult) { MOZ_CRASH("resolution was not expected"); },
    141      [&](ERef aError) { error.emplace(std::forward<ERef>(aError)); });
    142  SpinEventLoopUntil<ProcessFailureBehavior::IgnoreAndContinue>(
    143      "WaitForReject(const RefPtr<MozPromise<R, E, Exc>>& aPromise)"_ns,
    144      [&] { return error.isSome(); });
    145  return error.extract();
    146 }
    147 
    148 /**
    149 * A variation of WaitFor that takes a callback to be called each time aEvent is
    150 * raised. Blocks the caller until the callback function returns true.
    151 */
    152 template <ListenerPolicy Lp, typename... Args, typename CallbackFunction>
    153 inline void WaitUntil(MediaEventSourceImpl<Lp, Args...>& aEvent,
    154                      CallbackFunction&& aF) {
    155  bool done = false;
    156  MediaEventListener listener =
    157      aEvent.Connect(AbstractThread::GetCurrent(), [&](Args... aValue) {
    158        if (!done) {
    159          done = aF(std::forward<Args>(aValue)...);
    160        }
    161      });
    162  SpinEventLoopUntil<ProcessFailureBehavior::IgnoreAndContinue>(
    163      "WaitUntil(MediaEventSource<Args...>& aEvent, CallbackFunction&& aF)"_ns,
    164      [&] { return done; });
    165  listener.Disconnect();
    166 }
    167 
    168 template <typename... Args>
    169 using TakeNPromise = MozPromise<std::vector<std::tuple<Args...>>, bool, true>;
    170 
    171 template <ListenerPolicy Lp, typename... Args>
    172 inline auto TakeN(MediaEventSourceImpl<Lp, Args...>& aEvent, size_t aN)
    173    -> RefPtr<TakeNPromise<Args...>> {
    174  using Storage = std::vector<std::tuple<Args...>>;
    175  using Promise = TakeNPromise<Args...>;
    176  using Holder = media::Refcountable<MozPromiseHolder<Promise>>;
    177  using Values = media::Refcountable<Storage>;
    178  using Listener = media::Refcountable<MediaEventListener>;
    179  auto values = MakeRefPtr<Values>();
    180  values->reserve(aN);
    181  auto listener = MakeRefPtr<Listener>();
    182  auto holder = MakeRefPtr<Holder>();
    183  *listener = aEvent.Connect(AbstractThread::GetCurrent(),
    184                             [values, listener, aN, holder](Args... aValue) {
    185                               values->push_back({aValue...});
    186                               if (values->size() == aN) {
    187                                 listener->Disconnect();
    188                                 holder->Resolve(std::move(*values),
    189                                                 "TakeN listener callback");
    190                               }
    191                             });
    192  return holder->Ensure(__func__);
    193 }
    194 
    195 using TakeNVoidPromise = MozPromise<size_t, bool, true>;
    196 
    197 template <ListenerPolicy Lp>
    198 inline auto TakeN(MediaEventSourceImpl<Lp, void>& aEvent, size_t aN)
    199    -> RefPtr<TakeNVoidPromise> {
    200  using Storage = Maybe<size_t>;
    201  using Promise = TakeNVoidPromise;
    202  using Holder = media::Refcountable<MozPromiseHolder<Promise>>;
    203  using Values = media::Refcountable<Storage>;
    204  using Listener = media::Refcountable<MediaEventListener>;
    205  auto values = MakeRefPtr<Values>();
    206  *values = Some(0);
    207  auto listener = MakeRefPtr<Listener>();
    208  auto holder = MakeRefPtr<Holder>();
    209  *listener = aEvent.Connect(
    210      AbstractThread::GetCurrent(), [values, listener, aN, holder]() {
    211        if (++(values->ref()) == aN) {
    212          listener->Disconnect();
    213          holder->Resolve(**values, "TakeN (void) listener callback");
    214        }
    215      });
    216  return holder->Ensure(__func__);
    217 }
    218 
    219 /**
    220 * Helper that, given that canonicals have just been updated on the current
    221 * thread, will block its execution until mirrors and their watchers have
    222 * executed on aTarget.
    223 */
    224 inline void WaitForMirrors(const RefPtr<nsISerialEventTarget>& aTarget) {
    225  (void)WaitFor(InvokeAsync(aTarget, __func__, [] {
    226    return GenericPromise::CreateAndResolve(true, "WaitForMirrors resolver");
    227  }));
    228 }
    229 
    230 /**
    231 * Short form of WaitForMirrors that assumes mirrors are on the current thread
    232 * (like canonicals).
    233 */
    234 inline void WaitForMirrors() { WaitForMirrors(GetCurrentSerialEventTarget()); }
    235 
    236 }  // namespace mozilla
    237 
    238 #endif  // TESTING_GTEST_MOZILLA_WAITFOR_H_