summaryrefslogtreecommitdiffstats
path: root/dom/media/gtest/WaitFor.h
diff options
context:
space:
mode:
Diffstat (limited to 'dom/media/gtest/WaitFor.h')
-rw-r--r--dom/media/gtest/WaitFor.h134
1 files changed, 134 insertions, 0 deletions
diff --git a/dom/media/gtest/WaitFor.h b/dom/media/gtest/WaitFor.h
new file mode 100644
index 0000000000..00e979b408
--- /dev/null
+++ b/dom/media/gtest/WaitFor.h
@@ -0,0 +1,134 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
+/* 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 WAITFOR_H_
+#define WAITFOR_H_
+
+#include "MediaEventSource.h"
+#include "MediaUtils.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/MozPromise.h"
+#include "mozilla/ResultVariant.h"
+#include "mozilla/SpinEventLoopUntil.h"
+
+namespace mozilla {
+
+/**
+ * Waits for an occurrence of aEvent on the current thread (by blocking it,
+ * except tasks added to the event loop may run) and returns the event's
+ * templated value, if it's non-void.
+ *
+ * The caller must be wary of eventloop issues, in
+ * particular cases where we rely on a stable state runnable, but there is never
+ * a task to trigger stable state. In such cases it is the responsibility of the
+ * caller to create the needed tasks, as JS would. A noteworthy API that relies
+ * on stable state is MediaTrackGraph::GetInstance.
+ */
+template <typename T>
+T WaitFor(MediaEventSource<T>& aEvent) {
+ Maybe<T> value;
+ MediaEventListener listener = aEvent.Connect(
+ AbstractThread::GetCurrent(), [&](T aValue) { value = Some(aValue); });
+ SpinEventLoopUntil<ProcessFailureBehavior::IgnoreAndContinue>(
+ "WaitFor(MediaEventSource<T>& aEvent)"_ns,
+ [&] { return value.isSome(); });
+ listener.Disconnect();
+ return value.value();
+}
+
+/**
+ * Specialization of WaitFor<T> for void.
+ */
+void WaitFor(MediaEventSource<void>& aEvent);
+
+/**
+ * Variant of WaitFor that blocks the caller until a MozPromise has either been
+ * resolved or rejected.
+ */
+template <typename R, typename E, bool Exc>
+Result<R, E> WaitFor(const RefPtr<MozPromise<R, E, Exc>>& aPromise) {
+ Maybe<R> success;
+ Maybe<E> error;
+ aPromise->Then(
+ GetCurrentSerialEventTarget(), __func__,
+ [&](R aResult) { success = Some(aResult); },
+ [&](E aError) { error = Some(aError); });
+ SpinEventLoopUntil<ProcessFailureBehavior::IgnoreAndContinue>(
+ "WaitFor(const RefPtr<MozPromise<R, E, Exc>>& aPromise)"_ns,
+ [&] { return success.isSome() || error.isSome(); });
+ if (success.isSome()) {
+ return success.extract();
+ }
+ return Err(error.extract());
+}
+
+/**
+ * A variation of WaitFor that takes a callback to be called each time aEvent is
+ * raised. Blocks the caller until the callback function returns true.
+ */
+template <typename... Args, typename CallbackFunction>
+void WaitUntil(MediaEventSource<Args...>& aEvent, CallbackFunction&& aF) {
+ bool done = false;
+ MediaEventListener listener =
+ aEvent.Connect(AbstractThread::GetCurrent(), [&](Args... aValue) {
+ if (!done) {
+ done = aF(std::forward<Args>(aValue)...);
+ }
+ });
+ SpinEventLoopUntil<ProcessFailureBehavior::IgnoreAndContinue>(
+ "WaitUntil(MediaEventSource<Args...>& aEvent, CallbackFunction&& aF)"_ns,
+ [&] { return done; });
+ listener.Disconnect();
+}
+
+template <typename... Args>
+using TakeNPromise = MozPromise<std::vector<std::tuple<Args...>>, bool, true>;
+
+template <ListenerPolicy Lp, typename... Args>
+auto TakeN(MediaEventSourceImpl<Lp, Args...>& aEvent, size_t aN)
+ -> RefPtr<TakeNPromise<Args...>> {
+ using Storage = std::vector<std::tuple<Args...>>;
+ using Promise = TakeNPromise<Args...>;
+ using Values = media::Refcountable<Storage>;
+ using Listener = media::Refcountable<MediaEventListener>;
+ RefPtr<Values> values = MakeRefPtr<Values>();
+ values->reserve(aN);
+ RefPtr<Listener> listener = MakeRefPtr<Listener>();
+ auto promise = InvokeAsync(
+ AbstractThread::GetCurrent(), __func__, [values, aN]() mutable {
+ SpinEventLoopUntil<ProcessFailureBehavior::IgnoreAndContinue>(
+ "TakeN(MediaEventSourceImpl<Lp, Args...>& aEvent, size_t aN)"_ns,
+ [&] { return values->size() == aN; });
+ return Promise::CreateAndResolve(std::move(*values), __func__);
+ });
+ *listener = aEvent.Connect(AbstractThread::GetCurrent(),
+ [values, listener, aN](Args... aValue) {
+ values->push_back({aValue...});
+ if (values->size() == aN) {
+ listener->Disconnect();
+ }
+ });
+ return promise;
+}
+
+/**
+ * Helper that, given that canonicals have just been updated on the current
+ * thread, will block its execution until mirrors and their watchers have
+ * executed on aTarget.
+ */
+inline void WaitForMirrors(const RefPtr<nsISerialEventTarget>& aTarget) {
+ Unused << WaitFor(InvokeAsync(aTarget, __func__, [] {
+ return GenericPromise::CreateAndResolve(true, "WaitForMirrors resolver");
+ }));
+}
+
+/**
+ * Short form of WaitForMirrors that assumes mirrors are on the current thread
+ * (like canonicals).
+ */
+inline void WaitForMirrors() { WaitForMirrors(GetCurrentSerialEventTarget()); }
+
+} // namespace mozilla
+
+#endif // WAITFOR_H_