Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/dom/media/   (Firefox Browser Version 153.0.1©)  Datei vom 27.6.2026 mit Größe 26 kB image not shown  

Quelle  MediaEventSource.h

  Sprache: C
 

/* 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/. */


#ifndef MediaEventSource_h_
#define MediaEventSource_h_

#include <type_traits>
#include <utility>

#include "mozilla/DataMutex.h"
#include "mozilla/Mutex.h"
#include "nsISupportsImpl.h"
#include "nsTArray.h"
#include "nsThreadUtils.h"

namespace mozilla {

/**
 * A thread-safe tool to communicate "revocation" across threads. It is used to
 * disconnect a listener from the event source to prevent future notifications
 * from coming. Revoke() can be called on any thread. However, it is recommended
 * to be called on the target thread to avoid race condition.
 *
 * RevocableToken is not exposed to the client code directly.
 * Use MediaEventListener below to do the job.
 */

class RevocableToken {
  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RevocableToken);

 public:
  RevocableToken() = default;

  virtual void Revoke() = 0;
  virtual bool IsRevoked() const = 0;

 protected:
  // Virtual destructor is required since we might delete a Listener object
  // through its base type pointer.
  virtual ~RevocableToken() = default;
};

enum class ListenerPolicy : int8_t {
  // Allow at most one listener. Move will be used when possible
  // to pass the event data to save copy.
  Exclusive,
  // Allow multiple listeners, which will be given thread-scoped refs of
  // the event data. For N targets/threads, this results in N-1 copies.
  OneCopyPerThread,
  // Allow multiple listeners, which will all be given a const& to the same
  // event data.
  NonExclusive
};

namespace detail {

/**
 * Define how an event type is passed internally in MediaEventSource and to the
 * listeners. Specialized for the void type to pass a dummy bool instead.
 */

template <typename T>
struct EventTypeTraits {
  typedef T ArgType;
};

template <>
struct EventTypeTraits<void> {
  typedef bool ArgType;
};

/**
 * Test if a method function or lambda accepts one or more arguments.
 */

template <typename T>
class TakeArgsHelper {
  template <typename C>
  static std::false_type test(void (C::*)(), int);
  template <typename C>
  static std::false_type test(void (C::*)() constint);
  template <typename C>
  static std::false_type test(void (C::*)() volatileint);
  template <typename C>
  static std::false_type test(void (C::*)() const volatileint);
  template <typename F>
  static std::false_type test(F&&, decltype(std::declval<F>()(), 0));
  static std::true_type test(...);

 public:
  using type = decltype(test(std::declval<T>(), 0));
};

template <typename T>
struct TakeArgs : public TakeArgsHelper<T>::type {};

/**
 * Encapsulate a raw pointer to be captured by a lambda without causing
 * static-analysis errors.
 */

template <typename T>
class RawPtr {
 public:
  explicit RawPtr(T* aPtr) : mPtr(aPtr) {}
  T* get() const { return mPtr; }

 private:
  T* const mPtr;
};

// A list of listeners with some helper functions. Used to batch notifications
// for a single thread/target.
template <typename Listener>
class ListenerBatch {
 public:
  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ListenerBatch);

  explicit ListenerBatch(nsCOMPtr<nsIEventTarget>&& aTarget)
      : mTarget(std::move(aTarget)) {}

  bool MaybeAddListener(const RefPtr<Listener>& aListener) {
    auto target = aListener->GetTarget();
    // It does not matter what batch disconnected listeners go in.
    if (!target) {
      // It _also_ does not matter if we actually add a disconnected listener.
      // Pretend we did, but don't.
      return true;
    }
    if (target != mTarget) {
      return false;
    }
    mListeners.AppendElement(aListener);
    return true;
  }

  bool CanTakeArgs() const {
    for (auto& listener : mListeners) {
      if (listener->CanTakeArgs()) {
        return true;
      }
    }
    return false;
  }

  template <typename Storage>
  void ApplyWithArgsTuple(Storage&& aStorage) {
    std::apply(
        [&](auto&&... aArgs) mutable {
          for (auto& listener : mListeners) {
            if (listener->CanTakeArgs()) {
              listener->ApplyWithArgsImpl(
                  std::forward<decltype(aArgs)>(aArgs)...);
            } else {
              listener->ApplyWithNoArgs();
            }
          }
        },
        std::forward<Storage>(aStorage));
  }

  void ApplyWithNoArgs() {
    for (auto& listener : mListeners) {
      listener->ApplyWithNoArgs();
    }
  }

  void DispatchTask(already_AddRefed<nsIRunnable> aTask) {
    // Every listener might or might not have disconnected, so find the
    // first one that can actually perform the dispatch. If all of them are
    // disconnected, this is a no-op, which is fine.
    nsCOMPtr task = aTask;
    for (auto& listener : mListeners) {
      if (listener->TryDispatchTask(do_AddRef(task))) {
        return;
      }
    }
  }

  size_t Length() const { return mListeners.Length(); }

 private:
  ~ListenerBatch() = default;
  nsTArray<RefPtr<Listener>> mListeners;
  nsCOMPtr<nsIEventTarget> mTarget;
};

template <ListenerPolicy, typename Listener>
class NotificationPolicy;

template <typename Listener>
class NotificationPolicy<ListenerPolicy::Exclusive, Listener> {
 public:
  using ListenerBatch = typename detail::ListenerBatch<Listener>;

  template <typename... Ts>
  static void DispatchNotifications(
      const nsTArray<RefPtr<ListenerBatch>>& aListenerBatches,
      Ts&&... aEvents) {
    using Storage = std::tuple<std::decay_t<Ts>...>;
    // Should we let extra argless listeners slide?
    MOZ_ASSERT(aListenerBatches.Length() == 1);
    auto& batch = aListenerBatches[0];
    if (batch->CanTakeArgs()) {
      Storage storage(std::move(aEvents)...);
      batch->DispatchTask(NS_NewRunnableFunction(
          "ListenerBatch::DispatchTask(with args)",
          [batch, storage = std::move(storage)]() mutable {
            batch->ApplyWithArgsTuple(std::move(storage));
          }));
    } else {
      batch->DispatchTask(
          NewRunnableMethod("ListenerBatch::DispatchTask(without args)", batch,
                            &ListenerBatch::ApplyWithNoArgs));
    }
  }
};

template <typename Listener>
class NotificationPolicy<ListenerPolicy::OneCopyPerThread, Listener> {
 public:
  using ListenerBatch = typename detail::ListenerBatch<Listener>;

  template <typename... Ts>
  static void DispatchNotifications(
      const nsTArray<RefPtr<ListenerBatch>>& aListenerBatches,
      Ts&&... aEvents) {
    using Storage = std::tuple<std::decay_t<Ts>...>;

    // Find the last batch that takes args, and remember the index; that
    // batch will get the original copy (aEvents).
    Maybe<size_t> lastBatchWithArgs;
    for (size_t i = 0; i < aListenerBatches.Length(); ++i) {
      if (aListenerBatches[i]->CanTakeArgs()) {
        lastBatchWithArgs = Some(i);
      }
    }

    Storage storage(std::move(aEvents)...);
    for (size_t i = 0; i < aListenerBatches.Length(); ++i) {
      auto& batch = aListenerBatches[i];
      if (batch->CanTakeArgs()) {
        if (i != *lastBatchWithArgs) {
          // Copy |storage| into everything but the last args-taking dispatch
          batch->DispatchTask(
              NS_NewRunnableFunction("ListenerBatch::DispatchTask(with args)",
                                     [batch, storage]() mutable {
                                       batch->ApplyWithArgsTuple(storage);
                                     }));
        } else {
          // Move |storage| into the last args-taking dispatch
          batch->DispatchTask(NS_NewRunnableFunction(
              "ListenerBatch::DispatchTask(with args)",
              [batch, storage = std::move(storage)]() mutable {
                batch->ApplyWithArgsTuple(storage);
              }));
        }
      } else {
        batch->DispatchTask(
            NewRunnableMethod("ListenerBatch::DispatchTask(without args)",
                              batch, &ListenerBatch::ApplyWithNoArgs));
      }
    }
  }
};

template <typename Listener>
class NotificationPolicy<ListenerPolicy::NonExclusive, Listener> {
 public:
  using ListenerBatch = typename detail::ListenerBatch<Listener>;

  // This base class exists solely to keep the refcount logging code happy :(
  // It cannot handle templates inside the _THREADSAFE_REFCOUNTING macro
  // properly; it is all keyed off string matching, and that string would end
  // up being "SharedArgs<As>" verbatim, which is the same regardless of what
  // |As| is.
  class SharedArgsBase {
   public:
    NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SharedArgsBase);

   protected:
    virtual ~SharedArgsBase() = default;
  };
  template <typename... As>
  class SharedArgs : public SharedArgsBase {
   public:
    using Storage = std::tuple<std::decay_t<As>...>;
    explicit SharedArgs(As&&... aArgs) : mStorage(std::forward<As>(aArgs)...) {}
    // We should not ever be copying this, it is refcounted
    SharedArgs(const SharedArgs& aOrig) = delete;

    void ApplyWithArgs(ListenerBatch* aBatch) {
      aBatch->ApplyWithArgsTuple(mStorage);
    }

   private:
    const Storage mStorage;
  };

  template <typename... Ts>
  static void DispatchNotifications(
      const nsTArray<RefPtr<ListenerBatch>>& aListenerBatches,
      Ts&&... aEvents) {
    // Lazy initted when we see the first args-taking batch
    RefPtr<SharedArgs<Ts...>> args;

    for (auto& batch : aListenerBatches) {
      if (batch->CanTakeArgs()) {
        if (!args) {
          // Lazy init
          args = MakeRefPtr<SharedArgs<Ts...>>(std::forward<Ts>(aEvents)...);
        }
        batch->DispatchTask(NewRunnableMethod<RefPtr<ListenerBatch>>(
            "ListenerBatch::DispatchTask(with args)", args,
            &SharedArgs<Ts...>::ApplyWithArgs, batch));
      } else {
        batch->DispatchTask(
            NewRunnableMethod("ListenerBatch::DispatchTask(without args)",
                              batch, &ListenerBatch::ApplyWithNoArgs));
      }
    }
  }
};

// Bottom-level base class for Listeners. Declares virtual functions that are
// always present, regardless of template parameters. This is where we handle
// the fact that different listeners have different targets, and even different
// ways of dispatching to those targets.
class ListenerBase : public RevocableToken {
 public:
  virtual bool TryDispatchTask(already_AddRefed<nsIRunnable> aTask) = 0;

  // True if the underlying listener function takes non-zero arguments.
  virtual bool CanTakeArgs() const = 0;
  // Invoke the underlying listener function. Should be called only when
  // CanTakeArgs() returns false.
  virtual void ApplyWithNoArgs() = 0;

  virtual nsCOMPtr<nsIEventTarget> GetTarget() const = 0;
};

// This is where we handle the fact that listeners will typically have
// different function signatures on their callbacks, and also the fact that
// different policies require different function signatures when perfect
// forwarding.
// We cannot simply have a single virtual ApplyWithArgs function in our
// superclass, because it is not possible to have a templated virtual function,
// and the policies all have different params that are passed (eg; NonExclusive
// passes const refs, which is not compatible with passing by rvalue ref or
// non-const lvalue ref)
template <ListenerPolicy, typename...>
class Listener;

template <typename... As>
class Listener<ListenerPolicy::Exclusive, As...> : public ListenerBase {
 public:
  virtual void ApplyWithArgsImpl(As&&... aEvents) = 0;
};

template <typename... As>
class Listener<ListenerPolicy::OneCopyPerThread, As...> : public ListenerBase {
 public:
  virtual void ApplyWithArgsImpl(As&... aEvents) = 0;
};

template <typename... As>
class Listener<ListenerPolicy::NonExclusive, As...> : public ListenerBase {
 public:
  virtual void ApplyWithArgsImpl(const As&... aEvents) = 0;
};

/**
 * Store the registered event target and function so it knows where and to
 * whom to send the event data.
 * The only way to make the existence of a virtual function override contingent
 * on a template parameter is to use template specialization. So, this
 * implements everything but ApplyWithArgs, which will be handled by yet
 * another subclass.
 */

template <ListenerPolicy Policy, typename Function, typename... As>
class ListenerImpl : public Listener<Policy, As...> {
  // Strip CV and reference from Function.
  using FunctionStorage = std::decay_t<Function>;
  using SelfType = ListenerImpl<Policy, Function, As...>;

 public:
  ListenerImpl(nsCOMPtr<nsIEventTarget>&& aTarget, Function&& aFunction)
      : mData(MakeRefPtr<Data>(std::move(aTarget),
                               std::forward<Function>(aFunction)),
              "MediaEvent ListenerImpl::mData") {}

 protected:
  virtual ~ListenerImpl() {
    MOZ_ASSERT(IsRevoked(), "Must disconnect the listener.");
  }

  nsCOMPtr<nsIEventTarget> GetTarget() const override {
    auto d = mData.Lock();
    if (d.ref()) {
      return d.ref()->mTarget;
    }
    return nullptr;
  }

  bool TryDispatchTask(already_AddRefed<nsIRunnable> aTask) override {
    nsCOMPtr task = aTask;
    RefPtr<Data> data;
    {
      auto d = mData.Lock();
      data = *d;
    }
    if (!data) {
      return false;
    }
    data->mTarget->Dispatch(task.forget());
    return true;
  }

  bool CanTakeArgs() const override { return TakeArgs<FunctionStorage>::value; }

  template <typename... Ts>
  void ApplyWithArgs(Ts&&... aEvents) {
    if constexpr (TakeArgs<Function>::value) {
      // Don't call the listener if it is disconnected.
      RefPtr<Data> data;
      {
        auto d = mData.Lock();
        data = *d;
      }
      if (!data) {
        return;
      }
      MOZ_DIAGNOSTIC_ASSERT(data->mTarget->IsOnCurrentThread());
      data->mFunction(std::forward<Ts>(aEvents)...);
    } else {
      MOZ_CRASH(
          "Don't use ApplyWithArgsImpl on listeners that don't take args! Use "
          "ApplyWithNoArgsImpl instead.");
    }
  }

  // |F| takes no arguments.
  void ApplyWithNoArgs() override {
    if constexpr (!TakeArgs<Function>::value) {
      // Don't call the listener if it is disconnected.
      RefPtr<Data> data;
      {
        auto d = mData.Lock();
        data = *d;
      }
      if (!data) {
        return;
      }
      MOZ_DIAGNOSTIC_ASSERT(data->mTarget->IsOnCurrentThread());
      data->mFunction();
    } else {
      MOZ_CRASH(
          "Don't use ApplyWithNoArgsImpl on listeners that take args! Use "
          "ApplyWithArgsImpl instead.");
    }
  }

  void Revoke() override {
    {
      auto data = mData.Lock();
      *data = nullptr;
    }
  }

  bool IsRevoked() const override {
    auto data = mData.Lock();
    return !*data;
  }

  struct RefCountedMediaEventListenerData {
    // Keep ref-counting here since Data holds a template member, leading to
    // instances of varying size, which the memory leak logging system dislikes.
    NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RefCountedMediaEventListenerData)
   protected:
    virtual ~RefCountedMediaEventListenerData() = default;
  };
  struct Data : public RefCountedMediaEventListenerData {
    Data(nsCOMPtr<nsIEventTarget>&& aTarget, Function&& aFunction)
        : mTarget(std::move(aTarget)),
          mFunction(std::forward<Function>(aFunction)) {
      MOZ_DIAGNOSTIC_ASSERT(mTarget);
    }
    const nsCOMPtr<nsIEventTarget> mTarget;
    FunctionStorage mFunction;
  };

  // Storage for target and function. Also used to track revocation.
  mutable DataMutex<RefPtr<Data>> mData;
};

// We're finally at the end of the class hierarchy. The implementation code
// within ApplyWithArgs is a one-liner that is mostly identical for all
// policies; this could have all been a single template function if these
// weren't virtual functions.
template <ListenerPolicy, typenametypename...>
class ListenerImplFinal;

template <typename Function, typename... As>
class ListenerImplFinal<ListenerPolicy::Exclusive, Function, As...> final
    : public ListenerImpl<ListenerPolicy::Exclusive, Function, As...> {
 public:
  using BaseType = ListenerImpl<ListenerPolicy::Exclusive, Function, As...>;
  ListenerImplFinal(nsIEventTarget* aTarget, Function&& aFunction)
      : BaseType(aTarget, std::forward<Function>(aFunction)) {}

  void ApplyWithArgsImpl(As&&... aEvents) override {
    BaseType::ApplyWithArgs(std::move(aEvents)...);
  }
};

template <typename Function, typename... As>
class ListenerImplFinal<ListenerPolicy::OneCopyPerThread, Function, As...> final
    : public ListenerImpl<ListenerPolicy::OneCopyPerThread, Function, As...> {
 public:
  using BaseType =
      ListenerImpl<ListenerPolicy::OneCopyPerThread, Function, As...>;
  ListenerImplFinal(nsIEventTarget* aTarget, Function&& aFunction)
      : BaseType(aTarget, std::forward<Function>(aFunction)) {}

  void ApplyWithArgsImpl(As&... aEvents) override {
    BaseType::ApplyWithArgs(aEvents...);
  }
};

template <typename Function, typename... As>
class ListenerImplFinal<ListenerPolicy::NonExclusive, Function, As...> final
    : public ListenerImpl<ListenerPolicy::NonExclusive, Function, As...> {
 public:
  using BaseType = ListenerImpl<ListenerPolicy::NonExclusive, Function, As...>;
  ListenerImplFinal(nsIEventTarget* aTarget, Function&& aFunction)
      : BaseType(aTarget, std::forward<Function>(aFunction)) {}

  void ApplyWithArgsImpl(const As&... aEvents) override {
    BaseType::ApplyWithArgs(aEvents...);
  }
};

/**
 * Return true if any type is a reference type.
 */

template <typename Head, typename... Tails>
struct IsAnyReference {
  static const bool value =
      std::is_reference_v<Head> || IsAnyReference<Tails...>::value;
};

template <typename T>
struct IsAnyReference<T> {
  static const bool value = std::is_reference_v<T>;
};

}  // namespace detail

template <ListenerPolicy, typename... Ts>
class MediaEventSourceImpl;

/**
 * Not thread-safe since this is not meant to be shared and therefore only
 * move constructor is provided. Used to hold the result of
 * MediaEventSource<T>::Connect() and call Disconnect() to disconnect the
 * listener from an event source.
 */

class MediaEventListener {
  template <ListenerPolicy, typename... Ts>
  friend class MediaEventSourceImpl;

 public:
  MediaEventListener() = default;

  MediaEventListener(MediaEventListener&& aOther)
      : mToken(std::move(aOther.mToken)) {}

  MediaEventListener& operator=(MediaEventListener&& aOther) {
    MOZ_ASSERT(!mToken, "Must disconnect the listener.");
    mToken = std::move(aOther.mToken);
    return *this;
  }

  ~MediaEventListener() {
    MOZ_ASSERT(!mToken, "Must disconnect the listener.");
  }

  void Disconnect() {
    mToken->Revoke();
    mToken = nullptr;
  }

  void DisconnectIfExists() {
    if (mToken) {
      Disconnect();
    }
  }

 private:
  // Avoid exposing RevocableToken directly to the client code so that
  // listeners can be disconnected in a controlled manner.
  explicit MediaEventListener(RevocableToken* aToken) : mToken(aToken) {}
  RefPtr<RevocableToken> mToken;
};

/**
 * A generic and thread-safe class to implement the observer pattern.
 */

template <ListenerPolicy Lp, typename... Es>
class MediaEventSourceImpl {
  static_assert(!detail::IsAnyReference<Es...>::value,
                "Ref-type not supported!");

  template <typename T>
  using ArgType = typename detail::EventTypeTraits<T>::ArgType;

  using Listener = detail::Listener<Lp, ArgType<Es>...>;

  template <typename Func>
  using ListenerImpl = detail::ListenerImplFinal<Lp, Func, ArgType<Es>...>;

  using ListenerBatch = typename detail::ListenerBatch<Listener>;

  template <typename Method>
  using TakeArgs = detail::TakeArgs<Method>;

  void PruneListeners() {
    mListeners.RemoveElementsBy(
        [](const auto& listener) { return listener->IsRevoked(); });
  }

  template <typename Function>
  MediaEventListener ConnectInternal(nsIEventTarget* aTarget,
                                     Function&& aFunction) {
    MutexAutoLock lock(mMutex);
    PruneListeners();
    MOZ_ASSERT(Lp != ListenerPolicy::Exclusive || mListeners.IsEmpty());
    auto l = mListeners.AppendElement();
    *l = new ListenerImpl<Function>(aTarget, std::forward<Function>(aFunction));
    return MediaEventListener(*l);
  }

 public:
  /**
   * Register a function to receive notifications from the event source.
   *
   * @param aTarget The event target on which the function will run.
   * @param aFunction A function to be called on the target thread. The function
   *                  parameter must be convertible from |EventType|.
   * @return An object used to disconnect from the event source.
   */

  template <typename Function>
  MediaEventListener Connect(nsIEventTarget* aTarget, Function&& aFunction) {
    return ConnectInternal(aTarget, std::forward<Function>(aFunction));
  }

  /**
   * As above.
   *
   * Note we deliberately keep a weak reference to |aThis| in order not to
   * change its lifetime. This is because notifications are dispatched
   * asynchronously and removing a listener doesn't always break the reference
   * cycle for the pending event could still hold a reference to |aThis|.
   *
   * The caller must call MediaEventListener::Disconnect() to avoid dangling
   * pointers.
   */

  template <typename Thistypename Method>
  MediaEventListener Connect(nsIEventTarget* aTarget, This* aThis,
                             Method aMethod) {
    if constexpr (TakeArgs<Method>::value) {
      detail::RawPtr<This> thiz(aThis);
      if constexpr (Lp == ListenerPolicy::Exclusive) {
        return ConnectInternal(aTarget, [=](ArgType<Es>&&... aEvents) {
          (thiz.get()->*aMethod)(std::move(aEvents)...);
        });
      } else if constexpr (Lp == ListenerPolicy::OneCopyPerThread) {
        return ConnectInternal(aTarget, [=](ArgType<Es>&... aEvents) {
          (thiz.get()->*aMethod)(aEvents...);
        });
      } else if constexpr (Lp == ListenerPolicy::NonExclusive) {
        return ConnectInternal(aTarget, [=](const ArgType<Es>&... aEvents) {
          (thiz.get()->*aMethod)(aEvents...);
        });
      }
    } else {
      detail::RawPtr<This> thiz(aThis);
      return ConnectInternal(aTarget, [=]() { (thiz.get()->*aMethod)(); });
    }
  }

 protected:
  MediaEventSourceImpl() : mMutex("MediaEventSourceImpl::mMutex") {}

  template <typename... Ts>
  void NotifyInternal(Ts&&... aEvents) {
    MutexAutoLock lock(mMutex);
    nsTArray<RefPtr<ListenerBatch>> listenerBatches;
    for (size_t i = 0; i < mListeners.Length();) {
      auto& l = mListeners[i];
      // Remove disconnected listeners.
      // It is not optimal but is simple and works well.
      nsCOMPtr<nsIEventTarget> target = l->GetTarget();
      if (!target) {
        mListeners.RemoveElementAt(i);
        continue;
      }

      ++i;

      // Find a batch (or create one) for this listener's target, and add the
      // listener to it.
      bool added = false;
      for (auto& batch : listenerBatches) {
        if (batch->MaybeAddListener(l)) {
          added = true;
          break;
        }
      }

      if (!added) {
        // The listener might not have a target anymore, but we still place it
        // in a Batch with the target we observed up top.
        listenerBatches.AppendElement(new ListenerBatch(nsCOMPtr(target)));
        (void)listenerBatches.LastElement()->MaybeAddListener(l);
      }
    }

    if (listenerBatches.Length()) {
      detail::NotificationPolicy<Lp, Listener>::DispatchNotifications(
          listenerBatches, std::forward<Ts>(aEvents)...);
    }
  }

  using Listeners = nsTArray<RefPtr<Listener>>;

 private:
  Mutex mMutex MOZ_UNANNOTATED;
  nsTArray<RefPtr<Listener>> mListeners;
};

template <typename... Es>
using MediaEventSource =
    MediaEventSourceImpl<ListenerPolicy::NonExclusive, Es...>;

template <typename... Es>
using MediaEventSourceExc =
    MediaEventSourceImpl<ListenerPolicy::Exclusive, Es...>;

template <typename... Es>
using MediaEventSourceOneCopyPerThread =
    MediaEventSourceImpl<ListenerPolicy::OneCopyPerThread, Es...>;

/**
 * A class to separate the interface of event subject (MediaEventSource)
 * and event publisher. Mostly used as a member variable to publish events
 * to the listeners.
 */

template <typename... Es>
class MediaEventProducer : public MediaEventSource<Es...> {
 public:
  template <typename... Ts>
  void Notify(Ts&&... aEvents) {
    this->NotifyInternal(std::forward<Ts>(aEvents)...);
  }
};

/**
 * Specialization for void type. A dummy bool is passed to NotifyInternal
 * since there is no way to pass a void value.
 */

template <>
class MediaEventProducer<void> : public MediaEventSource<void> {
 public:
  void Notify() { this->NotifyInternal(true /* dummy */); }
};

/**
 * A producer allowing at most one listener.
 */

template <typename... Es>
class MediaEventProducerExc : public MediaEventSourceExc<Es...> {
 public:
  template <typename... Ts>
  void Notify(Ts&&... aEvents) {
    this->NotifyInternal(std::forward<Ts>(aEvents)...);
  }
};

template <typename... Es>
class MediaEventProducerOneCopyPerThread
    : public MediaEventSourceOneCopyPerThread<Es...> {
 public:
  template <typename... Ts>
  void Notify(Ts&&... aEvents) {
    this->NotifyInternal(std::forward<Ts>(aEvents)...);
  }
};

/**
 * A class that facilitates forwarding MediaEvents from multiple sources of the
 * same type into a single source.
 *
 * Lifetimes are convenient. A forwarded source is disconnected either by
 * the source itself going away, or the forwarder being destroyed.
 *
 * Not threadsafe. The caller is responsible for calling Forward() in a
 * threadsafe manner.
 */

template <typename... Es>
class MediaEventForwarder : public MediaEventSource<Es...> {
 public:
  template <typename T>
  using ArgType = typename detail::EventTypeTraits<T>::ArgType;

  explicit MediaEventForwarder(nsCOMPtr<nsISerialEventTarget> aEventTarget)
      : mEventTarget(std::move(aEventTarget)) {}

  MediaEventForwarder(MediaEventForwarder&& aOther)
      : mEventTarget(aOther.mEventTarget),
        mListeners(std::move(aOther.mListeners)) {}

  ~MediaEventForwarder() { MOZ_ASSERT(mListeners.IsEmpty()); }

  MediaEventForwarder& operator=(MediaEventForwarder&& aOther) {
    MOZ_RELEASE_ASSERT(mEventTarget == aOther.mEventTarget);
    MOZ_ASSERT(mListeners.IsEmpty());
    mListeners = std::move(aOther.mListeners);
  }

  void Forward(MediaEventSource<Es...>& aSource) {
    // Forwarding a rawptr `this` here is fine, since DisconnectAll disconnect
    // all mListeners synchronously and prevents this handler from running.
    mListeners.AppendElement(
        aSource.Connect(mEventTarget, [this](const ArgType<Es>&... aEvents) {
          this->NotifyInternal(aEvents...);
        }));
  }

  template <typename Function>
  void ForwardIf(MediaEventSource<Es...>& aSource, Function&& aFunction) {
    // Forwarding a rawptr `this` here is fine, since DisconnectAll disconnect
    // all mListeners synchronously and prevents this handler from running.
    mListeners.AppendElement(aSource.Connect(
        mEventTarget, [this, func = aFunction](const ArgType<Es>&... aEvents) {
          if (!func()) {
            return;
          }
          this->NotifyInternal(aEvents...);
        }));
  }

  void DisconnectAll() {
    for (auto& l : mListeners) {
      l.Disconnect();
    }
    mListeners.Clear();
  }

 private:
  const nsCOMPtr<nsISerialEventTarget> mEventTarget;
  nsTArray<MediaEventListener> mListeners;
};

}  // namespace mozilla

#endif  // MediaEventSource_h_

Messung V0.5 in Prozent
C=88 H=100 G=94

¤ Dauer der Verarbeitung: 0.22 Sekunden  (vorverarbeitet am  2026-08-25) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.