diff options
Diffstat (limited to '')
-rw-r--r-- | dom/media/systemservices/MediaUtils.h | 332 |
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 |