2020-08-10 01:05:57 +03:00
|
|
|
/* -*- Mode: C++; tab-width: 4; 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 mozilla_PrintBackgroundTask_h_
|
|
|
|
#define mozilla_PrintBackgroundTask_h_
|
|
|
|
|
|
|
|
#include "mozilla/dom/Promise.h"
|
|
|
|
#include "mozilla/ErrorResult.h"
|
2020-08-31 17:36:45 +03:00
|
|
|
#include "mozilla/Telemetry.h"
|
2020-08-10 01:05:57 +03:00
|
|
|
#include <utility>
|
|
|
|
#include <tuple>
|
|
|
|
|
|
|
|
// A helper to resolve a DOM Promise with the result of a const method, executed
|
|
|
|
// in another thread.
|
|
|
|
//
|
|
|
|
// Once in the main thread, the caller can turn the result of the method into a
|
|
|
|
// JSValue by specializing ResolveOrReject.
|
|
|
|
namespace mozilla {
|
|
|
|
|
|
|
|
template <typename T, typename Result>
|
|
|
|
void ResolveOrReject(dom::Promise& aPromise, T&, Result& aResult) {
|
|
|
|
aPromise.MaybeResolve(std::forward<Result>(aResult));
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T, typename Result, typename... Args>
|
|
|
|
using PrintBackgroundTask = Result (T::*)(Args...) const;
|
|
|
|
|
|
|
|
template <typename T, typename Result, typename... Args>
|
|
|
|
void SpawnPrintBackgroundTask(
|
2020-08-31 17:36:45 +03:00
|
|
|
T& aReceiver, dom::Promise& aPromise, const nsCString& aTelemetryKey,
|
2020-08-10 01:05:57 +03:00
|
|
|
PrintBackgroundTask<T, Result, Args...> aBackgroundTask, Args... aArgs) {
|
|
|
|
auto promiseHolder = MakeRefPtr<nsMainThreadPtrHolder<dom::Promise>>(
|
|
|
|
"nsPrinterBase::SpawnBackgroundTaskPromise", &aPromise);
|
|
|
|
// We actually want to allow to access the printer data from the callback, so
|
|
|
|
// disable strict checking. They should of course only access immutable
|
|
|
|
// members.
|
|
|
|
auto holder = MakeRefPtr<nsMainThreadPtrHolder<T>>(
|
|
|
|
"nsPrinterBase::SpawnBackgroundTaskPrinter", &aReceiver,
|
|
|
|
/* strict = */ false);
|
|
|
|
// See
|
|
|
|
// https://stackoverflow.com/questions/47496358/c-lambdas-how-to-capture-variadic-parameter-pack-from-the-upper-scope
|
|
|
|
// about the tuple shenanigans. It could be improved with C++20
|
2020-08-26 23:55:47 +03:00
|
|
|
NS_DispatchBackgroundTask(
|
|
|
|
NS_NewRunnableFunction(
|
|
|
|
"SpawnPrintBackgroundTask",
|
|
|
|
[holder = std::move(holder), promiseHolder = std::move(promiseHolder),
|
2020-08-31 17:36:45 +03:00
|
|
|
aTelemetryKey, startRoundTrip = TimeStamp::Now(),
|
|
|
|
backgroundTask = aBackgroundTask,
|
2020-08-26 23:55:47 +03:00
|
|
|
aArgs = std::make_tuple(std::forward<Args>(aArgs)...)] {
|
2020-08-31 17:36:45 +03:00
|
|
|
auto start = TimeStamp::Now();
|
2020-08-26 23:55:47 +03:00
|
|
|
Result result = std::apply(
|
|
|
|
[&](auto&&... args) {
|
2020-08-31 17:36:45 +03:00
|
|
|
return (holder->get()->*backgroundTask)(args...);
|
2020-08-26 23:55:47 +03:00
|
|
|
},
|
|
|
|
std::move(aArgs));
|
2020-08-31 17:36:45 +03:00
|
|
|
Telemetry::AccumulateTimeDelta(
|
|
|
|
Telemetry::PRINT_BACKGROUND_TASK_TIME_MS, aTelemetryKey, start);
|
2020-08-26 23:55:47 +03:00
|
|
|
NS_DispatchToMainThread(NS_NewRunnableFunction(
|
|
|
|
"SpawnPrintBackgroundTaskResolution",
|
|
|
|
[holder = std::move(holder),
|
|
|
|
promiseHolder = std::move(promiseHolder),
|
2020-08-31 17:36:45 +03:00
|
|
|
telemetryKey = std::move(aTelemetryKey), startRoundTrip,
|
2020-08-26 23:55:47 +03:00
|
|
|
result = std::move(result)] {
|
2020-08-31 17:36:45 +03:00
|
|
|
Telemetry::AccumulateTimeDelta(
|
|
|
|
Telemetry::PRINT_BACKGROUND_TASK_ROUND_TRIP_TIME_MS,
|
|
|
|
telemetryKey, startRoundTrip);
|
2020-08-26 23:55:47 +03:00
|
|
|
ResolveOrReject(*promiseHolder->get(), *holder->get(),
|
|
|
|
result);
|
|
|
|
}));
|
|
|
|
}),
|
|
|
|
NS_DISPATCH_EVENT_MAY_BLOCK);
|
2020-08-10 01:05:57 +03:00
|
|
|
}
|
|
|
|
|
2020-08-14 21:03:05 +03:00
|
|
|
// Gets a fresh promise into aResultPromise, that resolves whenever the print
|
|
|
|
// background task finishes.
|
|
|
|
template <typename T, typename Result, typename... Args>
|
|
|
|
nsresult PrintBackgroundTaskPromise(
|
|
|
|
T& aReceiver, JSContext* aCx, dom::Promise** aResultPromise,
|
2020-08-31 17:36:45 +03:00
|
|
|
const nsCString& aTelemetryKey,
|
2020-08-14 21:03:05 +03:00
|
|
|
PrintBackgroundTask<T, Result, Args...> aTask, Args... aArgs) {
|
|
|
|
ErrorResult rv;
|
|
|
|
RefPtr<dom::Promise> promise =
|
|
|
|
dom::Promise::Create(xpc::CurrentNativeGlobal(aCx), rv);
|
|
|
|
if (MOZ_UNLIKELY(rv.Failed())) {
|
|
|
|
return rv.StealNSResult();
|
|
|
|
}
|
|
|
|
|
2020-08-31 17:36:45 +03:00
|
|
|
SpawnPrintBackgroundTask(aReceiver, *promise, aTelemetryKey, aTask,
|
2020-08-14 21:03:05 +03:00
|
|
|
std::forward<Args>(aArgs)...);
|
|
|
|
|
|
|
|
promise.forget(aResultPromise);
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2020-08-10 01:05:57 +03:00
|
|
|
// Resolves an async attribute via a background task, creating and storing a
|
|
|
|
// promise as needed in aPromiseSlot.
|
|
|
|
template <typename T, typename Result, typename... Args>
|
|
|
|
nsresult AsyncPromiseAttributeGetter(
|
|
|
|
T& aReceiver, RefPtr<dom::Promise>& aPromiseSlot, JSContext* aCx,
|
2020-08-31 17:36:45 +03:00
|
|
|
dom::Promise** aResultPromise, const nsCString& aTelemetryKey,
|
2020-08-10 01:05:57 +03:00
|
|
|
PrintBackgroundTask<T, Result, Args...> aTask, Args... aArgs) {
|
|
|
|
if (RefPtr<dom::Promise> existing = aPromiseSlot) {
|
|
|
|
existing.forget(aResultPromise);
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2020-08-31 17:36:45 +03:00
|
|
|
nsresult rv =
|
|
|
|
PrintBackgroundTaskPromise(aReceiver, aCx, aResultPromise, aTelemetryKey,
|
|
|
|
aTask, std::forward<Args>(aArgs)...);
|
2020-08-14 21:03:05 +03:00
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
2020-08-10 01:05:57 +03:00
|
|
|
|
2020-08-14 21:03:05 +03:00
|
|
|
aPromiseSlot = *aResultPromise;
|
2020-08-10 01:05:57 +03:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace mozilla
|
|
|
|
|
|
|
|
#endif
|