tor-browser

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

MediaTimer.h (5426B)


      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 #if !defined(MediaTimer_h_)
      8 #  define MediaTimer_h_
      9 
     10 #  include <queue>
     11 
     12 #  include "mozilla/AbstractThread.h"
     13 #  include "mozilla/AwakeTimeStamp.h"
     14 #  include "mozilla/Monitor.h"
     15 #  include "mozilla/MozPromise.h"
     16 #  include "mozilla/RefPtr.h"
     17 #  include "mozilla/SharedThreadPool.h"
     18 #  include "mozilla/TimeStamp.h"
     19 #  include "nsITimer.h"
     20 
     21 namespace mozilla {
     22 
     23 extern LazyLogModule gMediaTimerLog;
     24 
     25 #  define TIMER_LOG(x, ...)                                    \
     26    MOZ_ASSERT(gMediaTimerLog);                                \
     27    MOZ_LOG(gMediaTimerLog, LogLevel::Debug,                   \
     28            ("[MediaTimer=%p relative_t=%" PRId64 "]" x, this, \
     29             RelativeMicroseconds(T::Now()), ##__VA_ARGS__))
     30 
     31 // This promise type is only exclusive because so far there isn't a reason for
     32 // it not to be. Feel free to change that.
     33 using MediaTimerPromise = MozPromise<bool, bool, true>;
     34 
     35 // Timers only know how to fire at a given thread, which creates an impedence
     36 // mismatch with code that operates with TaskQueues. This class solves
     37 // that mismatch with a dedicated (but shared) thread and a nice MozPromise-y
     38 // interface.
     39 template <typename T>
     40 class MediaTimer {
     41 public:
     42  explicit MediaTimer(bool aFuzzy = false);
     43 
     44  NS_INLINE_DECL_THREADSAFE_REFCOUNTING_WITH_DESTROY(MediaTimer,
     45                                                     DispatchDestroy());
     46 
     47  RefPtr<MediaTimerPromise> WaitFor(const typename T::DurationType& aDuration,
     48                                    StaticString aCallSite);
     49 
     50  RefPtr<MediaTimerPromise> WaitUntil(const T& aTimeStamp,
     51                                      StaticString aCallSite);
     52 
     53  // Cancel and reject any unresolved promises with false.
     54  void Cancel();
     55 
     56 private:
     57  virtual ~MediaTimer() { MOZ_ASSERT(OnMediaTimerThread()); }
     58 
     59  void DispatchDestroy();
     60  // Runs on the timer thread.
     61  void Destroy();
     62  bool OnMediaTimerThread();
     63  void ScheduleUpdate();
     64  void Update();
     65  void UpdateLocked();
     66  bool IsExpired(const T& aTarget, const T& aNow);
     67  void Reject();
     68  /*
     69   * We use a callback function, rather than a callback method, to ensure that
     70   * the nsITimer does not artifically keep the refcount of the MediaTimer above
     71   * zero. When the MediaTimer is destroyed, it safely cancels the nsITimer so
     72   * that we never fire against a dangling closure.
     73   */
     74  static void TimerCallback(nsITimer* aTimer, void* aClosure);
     75  void TimerFired();
     76  void ArmTimer(const T& aTarget, const T& aNow);
     77 
     78  bool TimerIsArmed();
     79  void CancelTimerIfArmed();
     80 
     81  struct Entry {
     82    T mTimeStamp;
     83    RefPtr<MediaTimerPromise::Private> mPromise;
     84 
     85    explicit Entry(const T& aTimeStamp, StaticString aCallSite)
     86        : mTimeStamp(aTimeStamp),
     87          mPromise(new MediaTimerPromise::Private(aCallSite)) {}
     88 
     89    // Define a < overload that reverses ordering because std::priority_queue
     90    // provides access to the largest element, and we want the smallest
     91    // (i.e. the soonest).
     92    bool operator<(const Entry& aOther) const {
     93      return mTimeStamp > aOther.mTimeStamp;
     94    }
     95  };
     96 
     97  nsCOMPtr<nsIEventTarget> mThread;
     98  std::priority_queue<Entry> mEntries;
     99  Monitor mMonitor MOZ_UNANNOTATED;
    100  nsCOMPtr<nsITimer> mTimer;
    101  Maybe<T> mCurrentTimerTarget;
    102 
    103  // Timestamps only have relative meaning, so we need a base timestamp for
    104  // logging purposes.
    105  T mCreationTimeStamp;
    106  int64_t RelativeMicroseconds(const T& aTimeStamp) {
    107    return (int64_t)(aTimeStamp - mCreationTimeStamp).ToMicroseconds();
    108  }
    109 
    110  bool mUpdateScheduled;
    111  const bool mFuzzy;
    112 };
    113 
    114 // Class for managing delayed dispatches on target thread.
    115 template <typename T>
    116 class DelayedScheduler {
    117 public:
    118  explicit DelayedScheduler(nsISerialEventTarget* aTargetThread,
    119                            bool aFuzzy = false)
    120      : mTargetThread(aTargetThread), mMediaTimer(new MediaTimer<T>(aFuzzy)) {
    121    MOZ_ASSERT(mTargetThread);
    122  }
    123 
    124  bool IsScheduled() const { return mTarget.isSome(); }
    125 
    126  void Reset() {
    127    MOZ_ASSERT(mTargetThread->IsOnCurrentThread(),
    128               "Must be on target thread to disconnect");
    129    mRequest.DisconnectIfExists();
    130    mTarget = Nothing();
    131  }
    132 
    133  template <typename ResolveFunc, typename RejectFunc>
    134  void Ensure(T& aTarget, ResolveFunc&& aResolver, RejectFunc&& aRejector) {
    135    MOZ_ASSERT(mTargetThread->IsOnCurrentThread());
    136    if (IsScheduled() && mTarget.value() <= aTarget) {
    137      return;
    138    }
    139    Reset();
    140    mTarget.emplace(aTarget);
    141    mMediaTimer->WaitUntil(mTarget.value(), __func__)
    142        ->Then(mTargetThread, __func__, std::forward<ResolveFunc>(aResolver),
    143               std::forward<RejectFunc>(aRejector))
    144        ->Track(mRequest);
    145  }
    146 
    147  void CompleteRequest() {
    148    MOZ_ASSERT(mTargetThread->IsOnCurrentThread());
    149    mRequest.Complete();
    150    mTarget = Nothing();
    151  }
    152 
    153 private:
    154  nsCOMPtr<nsISerialEventTarget> mTargetThread;
    155  RefPtr<MediaTimer<T>> mMediaTimer;
    156  Maybe<T> mTarget;
    157  MozPromiseRequestHolder<mozilla::MediaTimerPromise> mRequest;
    158 };
    159 
    160 using MediaTimerTimeStamp = MediaTimer<TimeStamp>;
    161 using MediaTimerAwakeTimeStamp = MediaTimer<AwakeTimeStamp>;
    162 
    163 }  // namespace mozilla
    164 
    165 #endif