Line data Source code
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/. */
6 :
7 : #ifndef mozilla_MediaUtils_h
8 : #define mozilla_MediaUtils_h
9 :
10 : #include "nsThreadUtils.h"
11 : #include "nsIAsyncShutdown.h"
12 : #include "mozilla/UniquePtr.h"
13 :
14 : namespace mozilla {
15 : namespace media {
16 :
17 : /*
18 : * media::Pledge - A promise-like pattern for c++ that takes lambda functions.
19 : *
20 : * Asynchronous APIs that proxy to another thread or to the chrome process and
21 : * back may find it useful to return a pledge to callers who then use
22 : * pledge.Then(func) to specify a lambda function to be invoked with the result
23 : * later back on this same thread.
24 : *
25 : * Callers will enjoy that lambdas allow "capturing" of local variables, much
26 : * like closures in JavaScript (safely by-copy by default).
27 : *
28 : * Callers will also enjoy that they do not need to be thread-safe (their code
29 : * runs on the same thread after all).
30 : *
31 : * Advantageously, pledges are non-threadsafe by design (because locking and
32 : * event queues are redundant). This means none of the lambdas you pass in,
33 : * or variables you lambda-capture into them, need be threasafe or support
34 : * threadsafe refcounting. After all, they'll run later on the same thread.
35 : *
36 : * RefPtr<media::Pledge<Foo>> p = GetFooAsynchronously(); // returns a pledge
37 : * p->Then([](const Foo& foo) {
38 : * // use foo here (same thread. Need not be thread-safe!)
39 : * });
40 : *
41 : * See media::CoatCheck below for an example of GetFooAsynchronously().
42 : */
43 :
44 0 : class PledgeBase
45 : {
46 : public:
47 0 : NS_INLINE_DECL_REFCOUNTING(PledgeBase);
48 : protected:
49 0 : virtual ~PledgeBase() {};
50 : };
51 :
52 : template<typename ValueType, typename ErrorType = nsresult>
53 : class Pledge : public PledgeBase
54 : {
55 : // TODO: Remove workaround once mozilla allows std::function from <functional>
56 : // wo/std::function support, do template + virtual trick to accept lambdas
57 : class FunctorsBase
58 : {
59 : public:
60 0 : FunctorsBase() {}
61 : virtual void Succeed(ValueType& result) = 0;
62 : virtual void Fail(ErrorType& error) = 0;
63 0 : virtual ~FunctorsBase() {};
64 : };
65 :
66 : public:
67 0 : explicit Pledge() : mDone(false), mRejected(false) {}
68 : Pledge(const Pledge& aOther) = delete;
69 : Pledge& operator = (const Pledge&) = delete;
70 :
71 : template<typename OnSuccessType>
72 0 : void Then(OnSuccessType&& aOnSuccess)
73 : {
74 0 : Then(Forward<OnSuccessType>(aOnSuccess), [](ErrorType&){});
75 0 : }
76 :
77 : template<typename OnSuccessType, typename OnFailureType>
78 0 : void Then(OnSuccessType&& aOnSuccess, OnFailureType&& aOnFailure)
79 : {
80 0 : class Functors : public FunctorsBase
81 : {
82 : public:
83 0 : Functors(OnSuccessType&& aOnSuccessRef, OnFailureType&& aOnFailureRef)
84 0 : : mOnSuccess(Move(aOnSuccessRef)), mOnFailure(Move(aOnFailureRef)) {}
85 :
86 0 : void Succeed(ValueType& result)
87 : {
88 0 : mOnSuccess(result);
89 0 : }
90 0 : void Fail(ErrorType& error)
91 : {
92 0 : mOnFailure(error);
93 0 : };
94 :
95 : OnSuccessType mOnSuccess;
96 : OnFailureType mOnFailure;
97 : };
98 0 : mFunctors = MakeUnique<Functors>(Forward<OnSuccessType>(aOnSuccess),
99 : Forward<OnFailureType>(aOnFailure));
100 0 : if (mDone) {
101 0 : if (!mRejected) {
102 0 : mFunctors->Succeed(mValue);
103 : } else {
104 0 : mFunctors->Fail(mError);
105 : }
106 : }
107 0 : }
108 :
109 0 : void Resolve(const ValueType& aValue)
110 : {
111 0 : mValue = aValue;
112 0 : Resolve();
113 0 : }
114 :
115 0 : void Reject(ErrorType rv)
116 : {
117 0 : if (!mDone) {
118 0 : mDone = mRejected = true;
119 0 : mError = rv;
120 0 : if (mFunctors) {
121 0 : mFunctors->Fail(mError);
122 : }
123 : }
124 0 : }
125 :
126 : protected:
127 0 : void Resolve()
128 : {
129 0 : if (!mDone) {
130 0 : mDone = true;
131 0 : MOZ_ASSERT(!mRejected);
132 0 : if (mFunctors) {
133 0 : mFunctors->Succeed(mValue);
134 : }
135 : }
136 0 : }
137 :
138 : ValueType mValue;
139 : private:
140 0 : ~Pledge() {};
141 : bool mDone;
142 : bool mRejected;
143 : ErrorType mError;
144 : UniquePtr<FunctorsBase> mFunctors;
145 : };
146 :
147 : /* media::NewRunnableFrom() - Create a Runnable from a lambda.
148 : *
149 : * Passing variables (closures) to an async function is clunky with Runnable:
150 : *
151 : * void Foo()
152 : * {
153 : * class FooRunnable : public Runnable
154 : * {
155 : * public:
156 : * FooRunnable(const Bar &aBar) : mBar(aBar) {}
157 : * NS_IMETHOD Run() override
158 : * {
159 : * // Use mBar
160 : * }
161 : * private:
162 : * RefPtr<Bar> mBar;
163 : * };
164 : *
165 : * RefPtr<Bar> bar = new Bar();
166 : * NS_DispatchToMainThread(new FooRunnable(bar);
167 : * }
168 : *
169 : * It's worse with more variables. Lambdas have a leg up with variable capture:
170 : *
171 : * void Foo()
172 : * {
173 : * RefPtr<Bar> bar = new Bar();
174 : * NS_DispatchToMainThread(media::NewRunnableFrom([bar]() mutable {
175 : * // use bar
176 : * }));
177 : * }
178 : *
179 : * Capture is by-copy by default, so the nsRefPtr 'bar' is safely copied for
180 : * access on the other thread (threadsafe refcounting in bar is assumed).
181 : *
182 : * The 'mutable' keyword is only needed for non-const access to bar.
183 : */
184 :
185 : template<typename OnRunType>
186 0 : class LambdaRunnable : public Runnable
187 : {
188 : public:
189 0 : explicit LambdaRunnable(OnRunType&& aOnRun)
190 : : Runnable("media::LambdaRunnable")
191 0 : , mOnRun(Move(aOnRun))
192 : {
193 0 : }
194 :
195 : private:
196 : NS_IMETHODIMP
197 0 : Run() override
198 : {
199 0 : return mOnRun();
200 : }
201 : OnRunType mOnRun;
202 : };
203 :
204 : template<typename OnRunType>
205 : already_AddRefed<LambdaRunnable<OnRunType>>
206 0 : NewRunnableFrom(OnRunType&& aOnRun)
207 : {
208 : typedef LambdaRunnable<OnRunType> LambdaType;
209 0 : RefPtr<LambdaType> lambda = new LambdaType(Forward<OnRunType>(aOnRun));
210 0 : return lambda.forget();
211 : }
212 :
213 : /* media::CoatCheck - There and back again. Park an object in exchange for an id.
214 : *
215 : * A common problem with calling asynchronous functions that do work on other
216 : * threads or processes is how to pass in a heap object for use once the
217 : * function completes, without requiring that object to have threadsafe
218 : * refcounting, contain mutexes, be marshaled, or leak if things fail
219 : * (or worse, intermittent use-after-free because of lifetime issues).
220 : *
221 : * One solution is to set up a coat-check on the caller side, park your object
222 : * in exchange for an id, and send the id. Common in IPC, but equally useful
223 : * for same-process thread-hops, because by never leaving the thread there's
224 : * no need for objects to be threadsafe or use threadsafe refcounting. E.g.
225 : *
226 : * class FooDoer
227 : * {
228 : * CoatCheck<Foo> mOutstandingFoos;
229 : *
230 : * public:
231 : * void DoFoo()
232 : * {
233 : * RefPtr<Foo> foo = new Foo();
234 : * uint32_t requestId = mOutstandingFoos.Append(*foo);
235 : * sChild->SendFoo(requestId);
236 : * }
237 : *
238 : * void RecvFooResponse(uint32_t requestId)
239 : * {
240 : * RefPtr<Foo> foo = mOutstandingFoos.Remove(requestId);
241 : * if (foo) {
242 : * // use foo
243 : * }
244 : * }
245 : * };
246 : *
247 : * If you read media::Pledge earlier, here's how this is useful for pledges:
248 : *
249 : * class FooGetter
250 : * {
251 : * CoatCheck<Pledge<Foo>> mOutstandingPledges;
252 : *
253 : * public:
254 : * already_addRefed<Pledge<Foo>> GetFooAsynchronously()
255 : * {
256 : * RefPtr<Pledge<Foo>> p = new Pledge<Foo>();
257 : * uint32_t requestId = mOutstandingPledges.Append(*p);
258 : * sChild->SendFoo(requestId);
259 : * return p.forget();
260 : * }
261 : *
262 : * void RecvFooResponse(uint32_t requestId, const Foo& fooResult)
263 : * {
264 : * RefPtr<Foo> p = mOutstandingPledges.Remove(requestId);
265 : * if (p) {
266 : * p->Resolve(fooResult);
267 : * }
268 : * }
269 : * };
270 : *
271 : * This helper is currently optimized for very small sets (i.e. not optimized).
272 : * It is also not thread-safe as the whole point is to stay on the same thread.
273 : */
274 :
275 : template<class T>
276 0 : class CoatCheck
277 : {
278 : public:
279 : typedef std::pair<uint32_t, RefPtr<T>> Element;
280 :
281 0 : uint32_t Append(T& t)
282 : {
283 0 : uint32_t id = GetNextId();
284 0 : mElements.AppendElement(Element(id, RefPtr<T>(&t)));
285 0 : return id;
286 : }
287 :
288 0 : already_AddRefed<T> Remove(uint32_t aId)
289 : {
290 0 : for (auto& element : mElements) {
291 0 : if (element.first == aId) {
292 0 : RefPtr<T> ref;
293 0 : ref.swap(element.second);
294 0 : mElements.RemoveElement(element);
295 0 : return ref.forget();
296 : }
297 : }
298 0 : MOZ_ASSERT_UNREACHABLE("Received id with no matching parked object!");
299 : return nullptr;
300 : }
301 :
302 : private:
303 0 : static uint32_t GetNextId()
304 : {
305 : static uint32_t counter = 0;
306 0 : return ++counter;
307 : };
308 : AutoTArray<Element, 3> mElements;
309 : };
310 :
311 : /* media::Refcountable - Add threadsafe ref-counting to something that isn't.
312 : *
313 : * Often, reference counting is the most practical way to share an object with
314 : * another thread without imposing lifetime restrictions, even if there's
315 : * otherwise no concurrent access happening on the object. For instance, an
316 : * algorithm on another thread may find it more expedient to modify a passed-in
317 : * object, rather than pass expensive copies back and forth.
318 : *
319 : * Lists in particular often aren't ref-countable, yet are expensive to copy,
320 : * e.g. nsTArray<RefPtr<Foo>>. Refcountable can be used to make such objects
321 : * (or owning smart-pointers to such objects) refcountable.
322 : *
323 : * Technical limitation: A template specialization is needed for types that take
324 : * a constructor. Please add below (UniquePtr covers a lot of ground though).
325 : */
326 :
327 : template<typename T>
328 : class Refcountable : public T
329 : {
330 : public:
331 : NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Refcountable<T>)
332 : private:
333 : ~Refcountable<T>() {}
334 : };
335 :
336 : template<typename T>
337 : class Refcountable<UniquePtr<T>> : public UniquePtr<T>
338 : {
339 : public:
340 0 : explicit Refcountable<UniquePtr<T>>(T* aPtr) : UniquePtr<T>(aPtr) {}
341 0 : NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Refcountable<T>)
342 : private:
343 0 : ~Refcountable<UniquePtr<T>>() {}
344 : };
345 :
346 : /* media::ShutdownBlocker - Async shutdown helper.
347 : */
348 :
349 : class ShutdownBlocker : public nsIAsyncShutdownBlocker
350 : {
351 : public:
352 0 : ShutdownBlocker(const nsString& aName) : mName(aName) {}
353 :
354 : NS_IMETHOD
355 : BlockShutdown(nsIAsyncShutdownClient* aProfileBeforeChange) override = 0;
356 :
357 0 : NS_IMETHOD GetName(nsAString& aName) override
358 : {
359 0 : aName = mName;
360 0 : return NS_OK;
361 : }
362 :
363 0 : NS_IMETHOD GetState(nsIPropertyBag**) override
364 : {
365 0 : return NS_OK;
366 : }
367 :
368 : NS_DECL_ISUPPORTS
369 : protected:
370 0 : virtual ~ShutdownBlocker() {}
371 : private:
372 : const nsString mName;
373 : };
374 :
375 : } // namespace media
376 : } // namespace mozilla
377 :
378 : #endif // mozilla_MediaUtils_h
|