Bug 1772053 - Enable dynamic code disable mitigations only on Windows 10 1703+ r...
[gecko.git] / dom / media / systemservices / MediaUtils.h
blobe63148483c40cb34b1584a82d2db6e6e5e4aae71
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set sw=2 ts=8 et ft=cpp : */
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 file,
5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #ifndef mozilla_MediaUtils_h
8 #define mozilla_MediaUtils_h
10 #include <map>
12 #include "mozilla/Assertions.h"
13 #include "mozilla/Monitor.h"
14 #include "mozilla/MozPromise.h"
15 #include "mozilla/Mutex.h"
16 #include "mozilla/RefPtr.h"
17 #include "mozilla/SharedThreadPool.h"
18 #include "mozilla/TaskQueue.h"
19 #include "mozilla/UniquePtr.h"
20 #include "MediaEventSource.h"
21 #include "nsCOMPtr.h"
22 #include "nsIAsyncShutdown.h"
23 #include "nsISupportsImpl.h"
24 #include "nsProxyRelease.h"
25 #include "nsThreadUtils.h"
27 class nsIEventTarget;
29 namespace mozilla::media {
31 /* media::NewRunnableFrom() - Create a Runnable from a lambda.
33 * Passing variables (closures) to an async function is clunky with Runnable:
35 * void Foo()
36 * {
37 * class FooRunnable : public Runnable
38 * {
39 * public:
40 * FooRunnable(const Bar &aBar) : mBar(aBar) {}
41 * NS_IMETHOD Run() override
42 * {
43 * // Use mBar
44 * }
45 * private:
46 * RefPtr<Bar> mBar;
47 * };
49 * RefPtr<Bar> bar = new Bar();
50 * NS_DispatchToMainThread(new FooRunnable(bar);
51 * }
53 * It's worse with more variables. Lambdas have a leg up with variable capture:
55 * void Foo()
56 * {
57 * RefPtr<Bar> bar = new Bar();
58 * NS_DispatchToMainThread(media::NewRunnableFrom([bar]() mutable {
59 * // use bar
60 * }));
61 * }
63 * Capture is by-copy by default, so the nsRefPtr 'bar' is safely copied for
64 * access on the other thread (threadsafe refcounting in bar is assumed).
66 * The 'mutable' keyword is only needed for non-const access to bar.
69 template <typename OnRunType>
70 class LambdaRunnable : public Runnable {
71 public:
72 explicit LambdaRunnable(OnRunType&& aOnRun)
73 : Runnable("media::LambdaRunnable"), mOnRun(std::move(aOnRun)) {}
75 private:
76 NS_IMETHODIMP
77 Run() override { return mOnRun(); }
78 OnRunType mOnRun;
81 template <typename OnRunType>
82 already_AddRefed<LambdaRunnable<OnRunType>> NewRunnableFrom(
83 OnRunType&& aOnRun) {
84 typedef LambdaRunnable<OnRunType> LambdaType;
85 RefPtr<LambdaType> lambda = new LambdaType(std::forward<OnRunType>(aOnRun));
86 return lambda.forget();
89 /* media::Refcountable - Add threadsafe ref-counting to something that isn't.
91 * Often, reference counting is the most practical way to share an object with
92 * another thread without imposing lifetime restrictions, even if there's
93 * otherwise no concurrent access happening on the object. For instance, an
94 * algorithm on another thread may find it more expedient to modify a passed-in
95 * object, rather than pass expensive copies back and forth.
97 * Lists in particular often aren't ref-countable, yet are expensive to copy,
98 * e.g. nsTArray<RefPtr<Foo>>. Refcountable can be used to make such objects
99 * (or owning smart-pointers to such objects) refcountable.
101 * Technical limitation: A template specialization is needed for types that take
102 * a constructor. Please add below (UniquePtr covers a lot of ground though).
105 class RefcountableBase {
106 public:
107 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RefcountableBase)
108 protected:
109 virtual ~RefcountableBase() = default;
112 template <typename T>
113 class Refcountable : public T, public RefcountableBase {
114 public:
115 Refcountable& operator=(T&& aOther) {
116 T::operator=(std::move(aOther));
117 return *this;
120 Refcountable& operator=(T& aOther) {
121 T::operator=(aOther);
122 return *this;
126 template <typename T>
127 class Refcountable<UniquePtr<T>> : public UniquePtr<T>,
128 public RefcountableBase {
129 public:
130 explicit Refcountable(T* aPtr) : UniquePtr<T>(aPtr) {}
133 template <>
134 class Refcountable<bool> : public RefcountableBase {
135 public:
136 explicit Refcountable(bool aValue) : mValue(aValue) {}
138 Refcountable& operator=(bool aOther) {
139 mValue = aOther;
140 return *this;
143 Refcountable& operator=(const Refcountable& aOther) {
144 mValue = aOther.mValue;
145 return *this;
148 explicit operator bool() const { return mValue; }
150 private:
151 bool mValue;
155 * Async shutdown helpers
158 nsCOMPtr<nsIAsyncShutdownClient> GetShutdownBarrier();
160 // Like GetShutdownBarrier but will release assert that the result is not null.
161 nsCOMPtr<nsIAsyncShutdownClient> MustGetShutdownBarrier();
163 class ShutdownBlocker : public nsIAsyncShutdownBlocker {
164 public:
165 ShutdownBlocker(nsString aName) : mName(std::move(aName)) {}
167 NS_IMETHOD
168 BlockShutdown(nsIAsyncShutdownClient* aProfileBeforeChange) override = 0;
170 NS_IMETHOD GetName(nsAString& aName) override {
171 aName = mName;
172 return NS_OK;
175 NS_IMETHOD GetState(nsIPropertyBag**) override { return NS_OK; }
177 NS_DECL_ISUPPORTS
178 protected:
179 virtual ~ShutdownBlocker() = default;
181 private:
182 const nsString mName;
186 * A convenience class representing a "ticket" that keeps the process from
187 * shutting down until it is destructed. It does this by blocking
188 * xpcom-will-shutdown. Constructed and destroyed on any thread.
190 class ShutdownBlockingTicket {
191 public:
193 * Construct with an arbitrary name, __FILE__ and __LINE__.
194 * Note that __FILE__ needs to be made wide, typically through
195 * NS_LITERAL_STRING_FROM_CSTRING(__FILE__).
197 static UniquePtr<ShutdownBlockingTicket> Create(nsString aName,
198 nsString aFileName,
199 int32_t aLineNr);
201 virtual ~ShutdownBlockingTicket() = default;
204 * MediaEvent that gets notified once upon xpcom-will-shutdown.
206 virtual MediaEventSource<void>& ShutdownEvent() = 0;
210 * Await convenience methods to block until the promise has been resolved or
211 * rejected. The Resolve/Reject functions, while called on a different thread,
212 * would be running just as on the current thread thanks to the memory barrier
213 * provided by the monitor.
214 * For now Await can only be used with an exclusive MozPromise if passed a
215 * Resolve/Reject function.
216 * Await() can *NOT* be called from a task queue/nsISerialEventTarget used for
217 * resolving/rejecting aPromise, otherwise things will deadlock.
219 template <typename ResolveValueType, typename RejectValueType,
220 typename ResolveFunction, typename RejectFunction>
221 void Await(already_AddRefed<nsIEventTarget> aPool,
222 RefPtr<MozPromise<ResolveValueType, RejectValueType, true>> aPromise,
223 ResolveFunction&& aResolveFunction,
224 RejectFunction&& aRejectFunction) {
225 RefPtr<TaskQueue> taskQueue =
226 TaskQueue::Create(std::move(aPool), "MozPromiseAwait");
227 Monitor mon MOZ_UNANNOTATED(__func__);
228 bool done = false;
230 aPromise->Then(
231 taskQueue, __func__,
232 [&](ResolveValueType&& aResolveValue) {
233 MonitorAutoLock lock(mon);
234 aResolveFunction(std::forward<ResolveValueType>(aResolveValue));
235 done = true;
236 mon.Notify();
238 [&](RejectValueType&& aRejectValue) {
239 MonitorAutoLock lock(mon);
240 aRejectFunction(std::forward<RejectValueType>(aRejectValue));
241 done = true;
242 mon.Notify();
245 MonitorAutoLock lock(mon);
246 while (!done) {
247 mon.Wait();
251 template <typename ResolveValueType, typename RejectValueType, bool Excl>
252 typename MozPromise<ResolveValueType, RejectValueType,
253 Excl>::ResolveOrRejectValue
254 Await(already_AddRefed<nsIEventTarget> aPool,
255 RefPtr<MozPromise<ResolveValueType, RejectValueType, Excl>> aPromise) {
256 RefPtr<TaskQueue> taskQueue =
257 TaskQueue::Create(std::move(aPool), "MozPromiseAwait");
258 Monitor mon MOZ_UNANNOTATED(__func__);
259 bool done = false;
261 typename MozPromise<ResolveValueType, RejectValueType,
262 Excl>::ResolveOrRejectValue val;
263 aPromise->Then(
264 taskQueue, __func__,
265 [&](ResolveValueType aResolveValue) {
266 val.SetResolve(std::move(aResolveValue));
267 MonitorAutoLock lock(mon);
268 done = true;
269 mon.Notify();
271 [&](RejectValueType aRejectValue) {
272 val.SetReject(std::move(aRejectValue));
273 MonitorAutoLock lock(mon);
274 done = true;
275 mon.Notify();
278 MonitorAutoLock lock(mon);
279 while (!done) {
280 mon.Wait();
283 return val;
287 * Similar to Await, takes an array of promises of the same type.
288 * MozPromise::All is used to handle the resolution/rejection of the promises.
290 template <typename ResolveValueType, typename RejectValueType,
291 typename ResolveFunction, typename RejectFunction>
292 void AwaitAll(
293 already_AddRefed<nsIEventTarget> aPool,
294 nsTArray<RefPtr<MozPromise<ResolveValueType, RejectValueType, true>>>&
295 aPromises,
296 ResolveFunction&& aResolveFunction, RejectFunction&& aRejectFunction) {
297 typedef MozPromise<ResolveValueType, RejectValueType, true> Promise;
298 RefPtr<nsIEventTarget> pool = aPool;
299 RefPtr<TaskQueue> taskQueue =
300 TaskQueue::Create(do_AddRef(pool), "MozPromiseAwaitAll");
301 RefPtr<typename Promise::AllPromiseType> p =
302 Promise::All(taskQueue, aPromises);
303 Await(pool.forget(), p, std::move(aResolveFunction),
304 std::move(aRejectFunction));
307 // Note: only works with exclusive MozPromise, as Promise::All would attempt
308 // to perform copy of nsTArrays which are disallowed.
309 template <typename ResolveValueType, typename RejectValueType>
310 typename MozPromise<ResolveValueType, RejectValueType,
311 true>::AllPromiseType::ResolveOrRejectValue
312 AwaitAll(already_AddRefed<nsIEventTarget> aPool,
313 nsTArray<RefPtr<MozPromise<ResolveValueType, RejectValueType, true>>>&
314 aPromises) {
315 typedef MozPromise<ResolveValueType, RejectValueType, true> Promise;
316 RefPtr<nsIEventTarget> pool = aPool;
317 RefPtr<TaskQueue> taskQueue =
318 TaskQueue::Create(do_AddRef(pool), "MozPromiseAwaitAll");
319 RefPtr<typename Promise::AllPromiseType> p =
320 Promise::All(taskQueue, aPromises);
321 return Await(pool.forget(), p);
324 } // namespace mozilla::media
326 #endif // mozilla_MediaUtils_h