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
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"
22 #include "nsIAsyncShutdown.h"
23 #include "nsISupportsImpl.h"
24 #include "nsProxyRelease.h"
25 #include "nsThreadUtils.h"
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:
37 * class FooRunnable : public Runnable
40 * FooRunnable(const Bar &aBar) : mBar(aBar) {}
41 * NS_IMETHOD Run() override
49 * RefPtr<Bar> bar = new Bar();
50 * NS_DispatchToMainThread(new FooRunnable(bar);
53 * It's worse with more variables. Lambdas have a leg up with variable capture:
57 * RefPtr<Bar> bar = new Bar();
58 * NS_DispatchToMainThread(media::NewRunnableFrom([bar]() mutable {
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
{
72 explicit LambdaRunnable(OnRunType
&& aOnRun
)
73 : Runnable("media::LambdaRunnable"), mOnRun(std::move(aOnRun
)) {}
77 Run() override
{ return mOnRun(); }
81 template <typename OnRunType
>
82 already_AddRefed
<LambdaRunnable
<OnRunType
>> NewRunnableFrom(
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
{
107 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RefcountableBase
)
109 virtual ~RefcountableBase() = default;
112 template <typename T
>
113 class Refcountable
: public T
, public RefcountableBase
{
115 Refcountable
& operator=(T
&& aOther
) {
116 T::operator=(std::move(aOther
));
120 Refcountable
& operator=(T
& aOther
) {
121 T::operator=(aOther
);
126 template <typename T
>
127 class Refcountable
<UniquePtr
<T
>> : public UniquePtr
<T
>,
128 public RefcountableBase
{
130 explicit Refcountable(T
* aPtr
) : UniquePtr
<T
>(aPtr
) {}
134 class Refcountable
<bool> : public RefcountableBase
{
136 explicit Refcountable(bool aValue
) : mValue(aValue
) {}
138 Refcountable
& operator=(bool aOther
) {
143 Refcountable
& operator=(const Refcountable
& aOther
) {
144 mValue
= aOther
.mValue
;
148 explicit operator bool() const { return 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
{
165 ShutdownBlocker(nsString aName
) : mName(std::move(aName
)) {}
168 BlockShutdown(nsIAsyncShutdownClient
* aProfileBeforeChange
) override
= 0;
170 NS_IMETHOD
GetName(nsAString
& aName
) override
{
175 NS_IMETHOD
GetState(nsIPropertyBag
**) override
{ return NS_OK
; }
179 virtual ~ShutdownBlocker() = default;
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
{
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
,
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__
);
232 [&](ResolveValueType
&& aResolveValue
) {
233 MonitorAutoLock
lock(mon
);
234 aResolveFunction(std::forward
<ResolveValueType
>(aResolveValue
));
238 [&](RejectValueType
&& aRejectValue
) {
239 MonitorAutoLock
lock(mon
);
240 aRejectFunction(std::forward
<RejectValueType
>(aRejectValue
));
245 MonitorAutoLock
lock(mon
);
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__
);
261 typename MozPromise
<ResolveValueType
, RejectValueType
,
262 Excl
>::ResolveOrRejectValue val
;
265 [&](ResolveValueType aResolveValue
) {
266 val
.SetResolve(std::move(aResolveValue
));
267 MonitorAutoLock
lock(mon
);
271 [&](RejectValueType aRejectValue
) {
272 val
.SetReject(std::move(aRejectValue
));
273 MonitorAutoLock
lock(mon
);
278 MonitorAutoLock
lock(mon
);
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
>
293 already_AddRefed
<nsIEventTarget
> aPool
,
294 nsTArray
<RefPtr
<MozPromise
<ResolveValueType
, RejectValueType
, true>>>&
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>>>&
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