dom/media/MediaTimer.h
author Emilio Cobos Álvarez <emilio@crisal.io>
Fri, 01 Jun 2018 18:30:30 +0200
changeset 420970 bb85c5ee5afc151be0d07ecc48318dc69cfef446
parent 409761 fcf9d1e427a06dce192b20740c54e37b2b7fd3f2
child 447000 0ceae9db9ec0be18daa1a279511ad305723185d4
permissions -rw-r--r--
Bug 1466168: Remove mozilla::Forward in favor of std::forward. r=froydnj Same approach as the other bug, mostly replacing automatically by removing 'using mozilla::Forward;' and then: s/mozilla::Forward/std::forward/ s/Forward</std::forward</ The only file that required manual fixup was TestTreeTraversal.cpp, which had a class called TestNodeForward with template parameters :) MozReview-Commit-ID: A88qFG5AccP

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#if !defined(MediaTimer_h_)
#define MediaTimer_h_

#include "mozilla/AbstractThread.h"
#include "mozilla/IntegerPrintfMacros.h"
#include "mozilla/Monitor.h"
#include "mozilla/MozPromise.h"
#include "mozilla/RefPtr.h"
#include "mozilla/TimeStamp.h"
#include "mozilla/Unused.h"
#include "nsITimer.h"
#include <queue>

namespace mozilla {

extern LazyLogModule gMediaTimerLog;

#define TIMER_LOG(x, ...) \
  MOZ_ASSERT(gMediaTimerLog); \
  MOZ_LOG(gMediaTimerLog, LogLevel::Debug, ("[MediaTimer=%p relative_t=%" PRId64 "]" x, this, \
                                        RelativeMicroseconds(TimeStamp::Now()), ##__VA_ARGS__))

// This promise type is only exclusive because so far there isn't a reason for
// it not to be. Feel free to change that.
typedef MozPromise<bool, bool, /* IsExclusive = */ true> MediaTimerPromise;

// Timers only know how to fire at a given thread, which creates an impedence
// mismatch with code that operates with TaskQueues. This class solves
// that mismatch with a dedicated (but shared) thread and a nice MozPromise-y
// interface.
class MediaTimer
{
public:
  explicit MediaTimer(bool aFuzzy = false);

  // We use a release with a custom Destroy().
  NS_IMETHOD_(MozExternalRefCountType) AddRef(void);
  NS_IMETHOD_(MozExternalRefCountType) Release(void);

  RefPtr<MediaTimerPromise> WaitFor(const TimeDuration& aDuration, const char* aCallSite);
  RefPtr<MediaTimerPromise> WaitUntil(const TimeStamp& aTimeStamp, const char* aCallSite);
  void Cancel(); // Cancel and reject any unresolved promises with false.

private:
  virtual ~MediaTimer() { MOZ_ASSERT(OnMediaTimerThread()); }

  void DispatchDestroy(); // Invoked by Release on an arbitrary thread.
  void Destroy(); // Runs on the timer thread.

  bool OnMediaTimerThread();
  void ScheduleUpdate();
  void Update();
  void UpdateLocked();
  bool IsExpired(const TimeStamp& aTarget, const TimeStamp& aNow);
  void Reject();

  static void TimerCallback(nsITimer* aTimer, void* aClosure);
  void TimerFired();
  void ArmTimer(const TimeStamp& aTarget, const TimeStamp& aNow);

  bool TimerIsArmed()
  {
    return !mCurrentTimerTarget.IsNull();
  }

  void CancelTimerIfArmed()
  {
    MOZ_ASSERT(OnMediaTimerThread());
    if (TimerIsArmed()) {
      TIMER_LOG("MediaTimer::CancelTimerIfArmed canceling timer");
      mTimer->Cancel();
      mCurrentTimerTarget = TimeStamp();
    }
  }


  struct Entry
  {
    TimeStamp mTimeStamp;
    RefPtr<MediaTimerPromise::Private> mPromise;

    explicit Entry(const TimeStamp& aTimeStamp, const char* aCallSite)
      : mTimeStamp(aTimeStamp)
      , mPromise(new MediaTimerPromise::Private(aCallSite))
    {}

    // Define a < overload that reverses ordering because std::priority_queue
    // provides access to the largest element, and we want the smallest
    // (i.e. the soonest).
    bool operator<(const Entry& aOther) const
    {
      return mTimeStamp > aOther.mTimeStamp;
    }
  };

  ThreadSafeAutoRefCnt mRefCnt;
  NS_DECL_OWNINGTHREAD
  nsCOMPtr<nsIEventTarget> mThread;
  std::priority_queue<Entry> mEntries;
  Monitor mMonitor;
  nsCOMPtr<nsITimer> mTimer;
  TimeStamp mCurrentTimerTarget;

  // Timestamps only have relative meaning, so we need a base timestamp for
  // logging purposes.
  TimeStamp mCreationTimeStamp;
  int64_t RelativeMicroseconds(const TimeStamp& aTimeStamp)
  {
    return (int64_t) (aTimeStamp - mCreationTimeStamp).ToMicroseconds();
  }

  bool mUpdateScheduled;
  const bool mFuzzy;
};

// Class for managing delayed dispatches on target thread.
class DelayedScheduler {
public:
  explicit DelayedScheduler(AbstractThread* aTargetThread, bool aFuzzy = false)
    : mTargetThread(aTargetThread), mMediaTimer(new MediaTimer(aFuzzy))
  {
    MOZ_ASSERT(mTargetThread);
  }

  bool IsScheduled() const { return !mTarget.IsNull(); }

  void Reset()
  {
    MOZ_ASSERT(mTargetThread->IsCurrentThreadIn(),
      "Must be on target thread to disconnect");
    if (IsScheduled()) {
      mRequest.Disconnect();
      mTarget = TimeStamp();
    }
  }

  template <typename ResolveFunc, typename RejectFunc>
  void Ensure(mozilla::TimeStamp& aTarget,
              ResolveFunc&& aResolver,
              RejectFunc&& aRejector)
  {
    MOZ_ASSERT(mTargetThread->IsCurrentThreadIn());
    if (IsScheduled() && mTarget <= aTarget) {
      return;
    }
    Reset();
    mTarget = aTarget;
    mMediaTimer->WaitUntil(mTarget, __func__)->Then(
      mTargetThread, __func__,
      std::forward<ResolveFunc>(aResolver),
      std::forward<RejectFunc>(aRejector))
    ->Track(mRequest);
  }

  void CompleteRequest()
  {
    MOZ_ASSERT(mTargetThread->IsCurrentThreadIn());
    mRequest.Complete();
    mTarget = TimeStamp();
  }

private:
  RefPtr<AbstractThread> mTargetThread;
  RefPtr<MediaTimer> mMediaTimer;
  MozPromiseRequestHolder<mozilla::MediaTimerPromise> mRequest;
  TimeStamp mTarget;
};

} // namespace mozilla

#endif