summaryrefslogtreecommitdiffstats
path: root/dom/media/systemservices/MediaUtils.h
diff options
context:
space:
mode:
Diffstat (limited to 'dom/media/systemservices/MediaUtils.h')
-rw-r--r--dom/media/systemservices/MediaUtils.h373
1 files changed, 373 insertions, 0 deletions
diff --git a/dom/media/systemservices/MediaUtils.h b/dom/media/systemservices/MediaUtils.h
new file mode 100644
index 000000000..18f7d3e41
--- /dev/null
+++ b/dom/media/systemservices/MediaUtils.h
@@ -0,0 +1,373 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et ft=cpp : */
+/* 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 "nsThreadUtils.h"
+#include "nsIAsyncShutdown.h"
+#include "mozilla/UniquePtr.h"
+
+namespace mozilla {
+namespace media {
+
+/*
+ * media::Pledge - A promise-like pattern for c++ that takes lambda functions.
+ *
+ * Asynchronous APIs that proxy to another thread or to the chrome process and
+ * back may find it useful to return a pledge to callers who then use
+ * pledge.Then(func) to specify a lambda function to be invoked with the result
+ * later back on this same thread.
+ *
+ * Callers will enjoy that lambdas allow "capturing" of local variables, much
+ * like closures in JavaScript (safely by-copy by default).
+ *
+ * Callers will also enjoy that they do not need to be thread-safe (their code
+ * runs on the same thread after all).
+ *
+ * Advantageously, pledges are non-threadsafe by design (because locking and
+ * event queues are redundant). This means none of the lambdas you pass in,
+ * or variables you lambda-capture into them, need be threasafe or support
+ * threadsafe refcounting. After all, they'll run later on the same thread.
+ *
+ * RefPtr<media::Pledge<Foo>> p = GetFooAsynchronously(); // returns a pledge
+ * p->Then([](const Foo& foo) {
+ * // use foo here (same thread. Need not be thread-safe!)
+ * });
+ *
+ * See media::CoatCheck below for an example of GetFooAsynchronously().
+ */
+
+class PledgeBase
+{
+public:
+ NS_INLINE_DECL_REFCOUNTING(PledgeBase);
+protected:
+ virtual ~PledgeBase() {};
+};
+
+template<typename ValueType, typename ErrorType = nsresult>
+class Pledge : public PledgeBase
+{
+ // TODO: Remove workaround once mozilla allows std::function from <functional>
+ // wo/std::function support, do template + virtual trick to accept lambdas
+ class FunctorsBase
+ {
+ public:
+ FunctorsBase() {}
+ virtual void Succeed(ValueType& result) = 0;
+ virtual void Fail(ErrorType& error) = 0;
+ virtual ~FunctorsBase() {};
+ };
+
+public:
+ explicit Pledge() : mDone(false), mRejected(false) {}
+ Pledge(const Pledge& aOther) = delete;
+ Pledge& operator = (const Pledge&) = delete;
+
+ template<typename OnSuccessType>
+ void Then(OnSuccessType&& aOnSuccess)
+ {
+ Then(Forward<OnSuccessType>(aOnSuccess), [](ErrorType&){});
+ }
+
+ template<typename OnSuccessType, typename OnFailureType>
+ void Then(OnSuccessType&& aOnSuccess, OnFailureType&& aOnFailure)
+ {
+ class Functors : public FunctorsBase
+ {
+ public:
+ Functors(OnSuccessType&& aOnSuccessRef, OnFailureType&& aOnFailureRef)
+ : mOnSuccess(Move(aOnSuccessRef)), mOnFailure(Move(aOnFailureRef)) {}
+
+ void Succeed(ValueType& result)
+ {
+ mOnSuccess(result);
+ }
+ void Fail(ErrorType& error)
+ {
+ mOnFailure(error);
+ };
+
+ OnSuccessType mOnSuccess;
+ OnFailureType mOnFailure;
+ };
+ mFunctors = MakeUnique<Functors>(Forward<OnSuccessType>(aOnSuccess),
+ Forward<OnFailureType>(aOnFailure));
+ if (mDone) {
+ if (!mRejected) {
+ mFunctors->Succeed(mValue);
+ } else {
+ mFunctors->Fail(mError);
+ }
+ }
+ }
+
+ void Resolve(const ValueType& aValue)
+ {
+ mValue = aValue;
+ Resolve();
+ }
+
+ void Reject(ErrorType rv)
+ {
+ if (!mDone) {
+ mDone = mRejected = true;
+ mError = rv;
+ if (mFunctors) {
+ mFunctors->Fail(mError);
+ }
+ }
+ }
+
+protected:
+ void Resolve()
+ {
+ if (!mDone) {
+ mDone = true;
+ MOZ_ASSERT(!mRejected);
+ if (mFunctors) {
+ mFunctors->Succeed(mValue);
+ }
+ }
+ }
+
+ ValueType mValue;
+private:
+ ~Pledge() {};
+ bool mDone;
+ bool mRejected;
+ ErrorType mError;
+ UniquePtr<FunctorsBase> mFunctors;
+};
+
+/* 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) : mOnRun(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(Forward<OnRunType>(aOnRun));
+ return lambda.forget();
+}
+
+/* media::CoatCheck - There and back again. Park an object in exchange for an id.
+ *
+ * A common problem with calling asynchronous functions that do work on other
+ * threads or processes is how to pass in a heap object for use once the
+ * function completes, without requiring that object to have threadsafe
+ * refcounting, contain mutexes, be marshaled, or leak if things fail
+ * (or worse, intermittent use-after-free because of lifetime issues).
+ *
+ * One solution is to set up a coat-check on the caller side, park your object
+ * in exchange for an id, and send the id. Common in IPC, but equally useful
+ * for same-process thread-hops, because by never leaving the thread there's
+ * no need for objects to be threadsafe or use threadsafe refcounting. E.g.
+ *
+ * class FooDoer
+ * {
+ * CoatCheck<Foo> mOutstandingFoos;
+ *
+ * public:
+ * void DoFoo()
+ * {
+ * RefPtr<Foo> foo = new Foo();
+ * uint32_t requestId = mOutstandingFoos.Append(*foo);
+ * sChild->SendFoo(requestId);
+ * }
+ *
+ * void RecvFooResponse(uint32_t requestId)
+ * {
+ * RefPtr<Foo> foo = mOutstandingFoos.Remove(requestId);
+ * if (foo) {
+ * // use foo
+ * }
+ * }
+ * };
+ *
+ * If you read media::Pledge earlier, here's how this is useful for pledges:
+ *
+ * class FooGetter
+ * {
+ * CoatCheck<Pledge<Foo>> mOutstandingPledges;
+ *
+ * public:
+ * already_addRefed<Pledge<Foo>> GetFooAsynchronously()
+ * {
+ * RefPtr<Pledge<Foo>> p = new Pledge<Foo>();
+ * uint32_t requestId = mOutstandingPledges.Append(*p);
+ * sChild->SendFoo(requestId);
+ * return p.forget();
+ * }
+ *
+ * void RecvFooResponse(uint32_t requestId, const Foo& fooResult)
+ * {
+ * RefPtr<Foo> p = mOutstandingPledges.Remove(requestId);
+ * if (p) {
+ * p->Resolve(fooResult);
+ * }
+ * }
+ * };
+ *
+ * This helper is currently optimized for very small sets (i.e. not optimized).
+ * It is also not thread-safe as the whole point is to stay on the same thread.
+ */
+
+template<class T>
+class CoatCheck
+{
+public:
+ typedef std::pair<uint32_t, RefPtr<T>> Element;
+
+ uint32_t Append(T& t)
+ {
+ uint32_t id = GetNextId();
+ mElements.AppendElement(Element(id, RefPtr<T>(&t)));
+ return id;
+ }
+
+ already_AddRefed<T> Remove(uint32_t aId)
+ {
+ for (auto& element : mElements) {
+ if (element.first == aId) {
+ RefPtr<T> ref;
+ ref.swap(element.second);
+ mElements.RemoveElement(element);
+ return ref.forget();
+ }
+ }
+ MOZ_ASSERT_UNREACHABLE("Received id with no matching parked object!");
+ return nullptr;
+ }
+
+private:
+ static uint32_t GetNextId()
+ {
+ static uint32_t counter = 0;
+ return ++counter;
+ };
+ AutoTArray<Element, 3> mElements;
+};
+
+/* 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).
+ */
+
+template<typename T>
+class Refcountable : public T
+{
+public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Refcountable<T>)
+private:
+ ~Refcountable<T>() {}
+};
+
+template<typename T>
+class Refcountable<UniquePtr<T>> : public UniquePtr<T>
+{
+public:
+ explicit Refcountable<UniquePtr<T>>(T* aPtr) : UniquePtr<T>(aPtr) {}
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Refcountable<T>)
+private:
+ ~Refcountable<UniquePtr<T>>() {}
+};
+
+/* media::ShutdownBlocker - Async shutdown helper.
+ */
+
+class ShutdownBlocker : public nsIAsyncShutdownBlocker
+{
+public:
+ ShutdownBlocker(const nsString& 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_ISUPPORTS
+protected:
+ virtual ~ShutdownBlocker() {}
+private:
+ const nsString mName;
+};
+
+} // namespace media
+} // namespace mozilla
+
+#endif // mozilla_MediaUtils_h