summaryrefslogtreecommitdiffstats
path: root/dom/media/systemservices/MediaUtils.h
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--dom/media/systemservices/MediaUtils.h332
1 files changed, 332 insertions, 0 deletions
diff --git a/dom/media/systemservices/MediaUtils.h b/dom/media/systemservices/MediaUtils.h
new file mode 100644
index 0000000000..90e21ce9aa
--- /dev/null
+++ b/dom/media/systemservices/MediaUtils.h
@@ -0,0 +1,332 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* 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 mozilla_MediaUtils_h
+#define mozilla_MediaUtils_h
+
+#include <map>
+
+#include "mozilla/Assertions.h"
+#include "mozilla/Monitor.h"
+#include "mozilla/MozPromise.h"
+#include "mozilla/Mutex.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/SharedThreadPool.h"
+#include "mozilla/TaskQueue.h"
+#include "mozilla/UniquePtr.h"
+#include "MediaEventSource.h"
+#include "nsCOMPtr.h"
+#include "nsIAsyncShutdown.h"
+#include "nsISupportsImpl.h"
+#include "nsProxyRelease.h"
+#include "nsThreadUtils.h"
+
+class nsIEventTarget;
+
+namespace mozilla::media {
+
+/* media::NewRunnableFrom() - Create a Runnable from a lambda.
+ *
+ * Passing variables (closures) to an async function is clunky with Runnable:
+ *
+ * void Foo()
+ * {
+ * class FooRunnable : public Runnable
+ * {
+ * public:
+ * FooRunnable(const Bar &aBar) : mBar(aBar) {}
+ * NS_IMETHOD Run() override
+ * {
+ * // Use mBar
+ * }
+ * private:
+ * RefPtr<Bar> mBar;
+ * };
+ *
+ * RefPtr<Bar> bar = new Bar();
+ * NS_DispatchToMainThread(new FooRunnable(bar);
+ * }
+ *
+ * It's worse with more variables. Lambdas have a leg up with variable capture:
+ *
+ * void Foo()
+ * {
+ * RefPtr<Bar> bar = new Bar();
+ * NS_DispatchToMainThread(media::NewRunnableFrom([bar]() mutable {
+ * // use bar
+ * }));
+ * }
+ *
+ * Capture is by-copy by default, so the nsRefPtr 'bar' is safely copied for
+ * access on the other thread (threadsafe refcounting in bar is assumed).
+ *
+ * The 'mutable' keyword is only needed for non-const access to bar.
+ */
+
+template <typename OnRunType>
+class LambdaRunnable : public Runnable {
+ public:
+ explicit LambdaRunnable(OnRunType&& aOnRun)
+ : Runnable("media::LambdaRunnable"), mOnRun(std::move(aOnRun)) {}
+
+ private:
+ NS_IMETHODIMP
+ Run() override { return mOnRun(); }
+ OnRunType mOnRun;
+};
+
+template <typename OnRunType>
+already_AddRefed<LambdaRunnable<OnRunType>> NewRunnableFrom(
+ OnRunType&& aOnRun) {
+ typedef LambdaRunnable<OnRunType> LambdaType;
+ RefPtr<LambdaType> lambda = new LambdaType(std::forward<OnRunType>(aOnRun));
+ return lambda.forget();
+}
+
+/* media::Refcountable - Add threadsafe ref-counting to something that isn't.
+ *
+ * Often, reference counting is the most practical way to share an object with
+ * another thread without imposing lifetime restrictions, even if there's
+ * otherwise no concurrent access happening on the object. For instance, an
+ * algorithm on another thread may find it more expedient to modify a passed-in
+ * object, rather than pass expensive copies back and forth.
+ *
+ * Lists in particular often aren't ref-countable, yet are expensive to copy,
+ * e.g. nsTArray<RefPtr<Foo>>. Refcountable can be used to make such objects
+ * (or owning smart-pointers to such objects) refcountable.
+ *
+ * Technical limitation: A template specialization is needed for types that take
+ * a constructor. Please add below (UniquePtr covers a lot of ground though).
+ */
+
+class RefcountableBase {
+ public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RefcountableBase)
+ protected:
+ virtual ~RefcountableBase() = default;
+};
+
+template <typename T>
+class Refcountable : public T, public RefcountableBase {
+ public:
+ Refcountable& operator=(T&& aOther) {
+ T::operator=(std::move(aOther));
+ return *this;
+ }
+
+ Refcountable& operator=(T& aOther) {
+ T::operator=(aOther);
+ return *this;
+ }
+};
+
+template <typename T>
+class Refcountable<UniquePtr<T>> : public UniquePtr<T>,
+ public RefcountableBase {
+ public:
+ explicit Refcountable(T* aPtr) : UniquePtr<T>(aPtr) {}
+};
+
+template <>
+class Refcountable<bool> : public RefcountableBase {
+ public:
+ explicit Refcountable(bool aValue) : mValue(aValue) {}
+
+ Refcountable& operator=(bool aOther) {
+ mValue = aOther;
+ return *this;
+ }
+
+ Refcountable& operator=(const Refcountable& aOther) {
+ mValue = aOther.mValue;
+ return *this;
+ }
+
+ explicit operator bool() const { return mValue; }
+
+ private:
+ bool mValue;
+};
+
+/*
+ * Async shutdown helpers
+ */
+
+nsCOMPtr<nsIAsyncShutdownClient> GetShutdownBarrier();
+
+// Like GetShutdownBarrier but will release assert that the result is not null.
+nsCOMPtr<nsIAsyncShutdownClient> MustGetShutdownBarrier();
+
+class ShutdownBlocker : public nsIAsyncShutdownBlocker {
+ public:
+ ShutdownBlocker(const nsAString& aName) : mName(aName) {}
+
+ NS_IMETHOD
+ BlockShutdown(nsIAsyncShutdownClient* aProfileBeforeChange) override = 0;
+
+ NS_IMETHOD GetName(nsAString& aName) override {
+ aName = mName;
+ return NS_OK;
+ }
+
+ NS_IMETHOD GetState(nsIPropertyBag**) override { return NS_OK; }
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+ protected:
+ virtual ~ShutdownBlocker() = default;
+
+ private:
+ const nsString mName;
+};
+
+/**
+ * A convenience class representing a "ticket" that keeps the process from
+ * shutting down until it is destructed. It does this by blocking
+ * xpcom-will-shutdown. Constructed and destroyed on any thread.
+ */
+class ShutdownBlockingTicket {
+ public:
+ using ShutdownMozPromise = MozPromise<bool, bool, false>;
+
+ /**
+ * Construct with an arbitrary name, __FILE__ and __LINE__.
+ * Note that __FILE__ needs to be made wide, typically through
+ * NS_LITERAL_STRING_FROM_CSTRING(__FILE__).
+ * Returns nullptr if we are too far in the shutdown sequence to add a
+ * blocker. Any thread.
+ */
+ static UniquePtr<ShutdownBlockingTicket> Create(const nsAString& aName,
+ const nsAString& aFileName,
+ int32_t aLineNr);
+
+ virtual ~ShutdownBlockingTicket() = default;
+
+ /**
+ * MozPromise that gets resolved upon xpcom-will-shutdown.
+ * Should the ticket get destroyed before the MozPromise has been resolved,
+ * the MozPromise will get rejected.
+ */
+ virtual ShutdownMozPromise* ShutdownPromise() = 0;
+};
+
+/**
+ * Await convenience methods to block until the promise has been resolved or
+ * rejected. The Resolve/Reject functions, while called on a different thread,
+ * would be running just as on the current thread thanks to the memory barrier
+ * provided by the monitor.
+ * For now Await can only be used with an exclusive MozPromise if passed a
+ * Resolve/Reject function.
+ * Await() can *NOT* be called from a task queue/nsISerialEventTarget used for
+ * resolving/rejecting aPromise, otherwise things will deadlock.
+ */
+template <typename ResolveValueType, typename RejectValueType,
+ typename ResolveFunction, typename RejectFunction>
+void Await(already_AddRefed<nsIEventTarget> aPool,
+ RefPtr<MozPromise<ResolveValueType, RejectValueType, true>> aPromise,
+ ResolveFunction&& aResolveFunction,
+ RejectFunction&& aRejectFunction) {
+ RefPtr<TaskQueue> taskQueue =
+ TaskQueue::Create(std::move(aPool), "MozPromiseAwait");
+ Monitor mon MOZ_UNANNOTATED(__func__);
+ bool done = false;
+
+ aPromise->Then(
+ taskQueue, __func__,
+ [&](ResolveValueType&& aResolveValue) {
+ MonitorAutoLock lock(mon);
+ aResolveFunction(std::forward<ResolveValueType>(aResolveValue));
+ done = true;
+ mon.Notify();
+ },
+ [&](RejectValueType&& aRejectValue) {
+ MonitorAutoLock lock(mon);
+ aRejectFunction(std::forward<RejectValueType>(aRejectValue));
+ done = true;
+ mon.Notify();
+ });
+
+ MonitorAutoLock lock(mon);
+ while (!done) {
+ mon.Wait();
+ }
+}
+
+template <typename ResolveValueType, typename RejectValueType, bool Excl>
+typename MozPromise<ResolveValueType, RejectValueType,
+ Excl>::ResolveOrRejectValue
+Await(already_AddRefed<nsIEventTarget> aPool,
+ RefPtr<MozPromise<ResolveValueType, RejectValueType, Excl>> aPromise) {
+ RefPtr<TaskQueue> taskQueue =
+ TaskQueue::Create(std::move(aPool), "MozPromiseAwait");
+ Monitor mon MOZ_UNANNOTATED(__func__);
+ bool done = false;
+
+ typename MozPromise<ResolveValueType, RejectValueType,
+ Excl>::ResolveOrRejectValue val;
+ aPromise->Then(
+ taskQueue, __func__,
+ [&](ResolveValueType aResolveValue) {
+ val.SetResolve(std::move(aResolveValue));
+ MonitorAutoLock lock(mon);
+ done = true;
+ mon.Notify();
+ },
+ [&](RejectValueType aRejectValue) {
+ val.SetReject(std::move(aRejectValue));
+ MonitorAutoLock lock(mon);
+ done = true;
+ mon.Notify();
+ });
+
+ MonitorAutoLock lock(mon);
+ while (!done) {
+ mon.Wait();
+ }
+
+ return val;
+}
+
+/**
+ * Similar to Await, takes an array of promises of the same type.
+ * MozPromise::All is used to handle the resolution/rejection of the promises.
+ */
+template <typename ResolveValueType, typename RejectValueType,
+ typename ResolveFunction, typename RejectFunction>
+void AwaitAll(
+ already_AddRefed<nsIEventTarget> aPool,
+ nsTArray<RefPtr<MozPromise<ResolveValueType, RejectValueType, true>>>&
+ aPromises,
+ ResolveFunction&& aResolveFunction, RejectFunction&& aRejectFunction) {
+ typedef MozPromise<ResolveValueType, RejectValueType, true> Promise;
+ RefPtr<nsIEventTarget> pool = aPool;
+ RefPtr<TaskQueue> taskQueue =
+ TaskQueue::Create(do_AddRef(pool), "MozPromiseAwaitAll");
+ RefPtr<typename Promise::AllPromiseType> p =
+ Promise::All(taskQueue, aPromises);
+ Await(pool.forget(), p, std::move(aResolveFunction),
+ std::move(aRejectFunction));
+}
+
+// Note: only works with exclusive MozPromise, as Promise::All would attempt
+// to perform copy of nsTArrays which are disallowed.
+template <typename ResolveValueType, typename RejectValueType>
+typename MozPromise<ResolveValueType, RejectValueType,
+ true>::AllPromiseType::ResolveOrRejectValue
+AwaitAll(already_AddRefed<nsIEventTarget> aPool,
+ nsTArray<RefPtr<MozPromise<ResolveValueType, RejectValueType, true>>>&
+ aPromises) {
+ typedef MozPromise<ResolveValueType, RejectValueType, true> Promise;
+ RefPtr<nsIEventTarget> pool = aPool;
+ RefPtr<TaskQueue> taskQueue =
+ TaskQueue::Create(do_AddRef(pool), "MozPromiseAwaitAll");
+ RefPtr<typename Promise::AllPromiseType> p =
+ Promise::All(taskQueue, aPromises);
+ return Await(pool.forget(), p);
+}
+
+} // namespace mozilla::media
+
+#endif // mozilla_MediaUtils_h