/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=2 et sw=2 tw=80: */ /* 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 https://mozilla.org/MPL/2.0/. */
// Simple function to be able to distinguish threads in output
size_t tid() { return std::hash<std::thread::id>{}(std::this_thread::get_id());
}
// Invoking something on a background thread, but getting the completion on the // main thread.
TEST(MozPromiseExamples, InvokeAsync)
{ bool done = false;
InvokeAsync(
GetCurrentSerialEventTarget(), __func__,
[]() {
printf("[%zu] Doing some work on a background thread...\n", tid());
std::this_thread::sleep_for(std::chrono::milliseconds(100));
printf("[%zu] Done...\n", tid());
// Simulate various outcomes:
srand(getpid()); switch (rand() % 4) { case 0: return GenericPromise::CreateAndResolve(true, __func__); case 1: return GenericPromise::CreateAndResolve(false, __func__); case 2: return GenericPromise::CreateAndReject(NS_ERROR_OUT_OF_MEMORY,
__func__); default: return GenericPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
}
})
->Then(
GetMainThreadSerialEventTarget(), __func__,
[&done](GenericPromise::ResolveOrRejectValue&& aResult) { if (aResult.IsReject()) {
printf("[%zu] Back on the main thread, the task failed: 0x%x\n",
tid(), (unsignedint)aResult.RejectValue());
done = true; return;
}
printf("[%zu] back on the main thread, sucess, return value: %s\n",
tid(), aResult.ResolveValue() ? "true" : "false");
done = true;
});
// Process all events and check that `done` was effectively set to true. This // is just for the purpose of this test.
MOZ_ALWAYS_TRUE(SpinEventLoopUntil( "xpcom:TEST(MozPromiseExamples, OneOff)"_ns, [&done]() { return done; }));
EXPECT_TRUE(done);
}
class Something final { public: explicit Something(uint32_t aMilliseconds = 100)
: mMilliseconds(aMilliseconds) {}
RefPtr<GenericPromise> DoIt() { // Do no dispatch the async task twice if still underway. if (mPromise) { return mPromise;
}
mPromise = mHolder.Ensure(__func__); // Kick off some work to another thread...
std::thread([self = RefPtr{this}, this] {
printf("[%zu] Working...\n", tid());
std::this_thread::sleep_for(std::chrono::milliseconds(mMilliseconds));
printf("[%zu] Resolving from background thread\n", tid());
self->mHolder.Resolve(true, __func__);
}).detach(); return mPromise;
}
// Process all events and check that `done` was effectively set to true. This // is just for the purpose of this test.
MOZ_ALWAYS_TRUE(SpinEventLoopUntil( "xpcom:TEST(MozPromiseExamples, OneOff)"_ns, [&done]() { return done; }));
}
class SomethingCancelable final { public:
RefPtr<GenericPromise> DoIt() { if (mPromise) { return mPromise;
}
mPromise = mHolder.Ensure(__func__); // Kick off some work to another thread...
std::thread([self = RefPtr{this}] {
printf("[%zu] Working...\n", tid());
std::this_thread::sleep_for(std::chrono::milliseconds(100)); // This is printed: despite being canceled, the thread runs normally and // resolves its promise.
printf("[%zu] Resolving from background thread\n", tid());
self->mHolder.Resolve(true, __func__);
}).detach(); return mPromise;
}
// Kick of an asynchronous job, and cancel it
TEST(MozPromiseExamples, OneOffCancelable)
{
RefPtr<SomethingCancelable> thing(new SomethingCancelable);
// Start a job that takes 100ms
MozPromiseRequestHolder<GenericPromise> holder;
thing->DoIt()
->Then(GetCurrentSerialEventTarget(), __func__,
[&holder] {
holder.Complete(); // This is never printed: in this example we disconnect the // request before completion.
printf("[%zu] Async work finished", tid());
})
->Track(holder); // But cancel it after just 10ms
std::this_thread::sleep_for(std::chrono::milliseconds(10));
holder.Disconnect();
}
// Waiting for multiple asynchronous tasks to complete, from outside the // instance
TEST(MozPromiseExamples, MultipleWaits)
{
nsTArray<RefPtr<Something>> things;
uint32_t count = 10; while (count--) {
things.AppendElement(new Something(count * 10));
} bool done = false;
nsTArray<RefPtr<GenericPromise>> promises; for (auto& thing : things) {
promises.AppendElement(thing->DoIt());
}
GenericPromise::All(GetCurrentSerialEventTarget(), promises)
->Then(
GetCurrentSerialEventTarget(), __func__,
[&done](nsTArray<bool>&& aResults) {
nsCString results; for (auto b : aResults) {
results.AppendPrintf("%s, ", b ? "true" : "false");
}
printf("[%zu] All succeeded: %s\n", tid(), results.get());
done = true;
},
[&done](nsresult aError) {
printf("[%zu] One failed: 0x%x\n", tid(), (unsigned)aError);
done = true;
});
// Process all events and check that `done` was effectively set to true. This // is just for the purpose of this test.
MOZ_ALWAYS_TRUE(SpinEventLoopUntil( "xpcom:TEST(MozPromiseExamples, OneOff)"_ns, [&done]() { return done; }));
}
// This test uses various MozPromise facilities and prints a message to the // console, to show how the scheduling works.
TEST(MozPromiseExamples, SyncReturn)
{ bool done = false; // Dispatch a runnable to the current even loop, for the sole purpose of // understanding ordering.
NS_DispatchToCurrentThread(NS_NewRunnableFunction("Initial runnable", [] {
printf("[%zu] Dispatched before sync promise operation\n", tid());
})); // SyncOperation synchronously returns a resolved promise. However, `Then` // works by dispatching so the printf will happen after InitialRunnable.
SyncOperation(3)->Then(
GetCurrentSerialEventTarget(), __func__,
[](bool aResult) {
printf("[%zu] Sync promise value: %s\n", tid(),
aResult ? "true" : "false");
},
[](nsresult aError) {
printf("[%zu] Error: 0x%x\n", tid(), (unsigned)aError);
}); // Now call the same method, but invoke it async on the current event queue. // The resolve will also be in its own event loop task. It follows that this // will be printed after the "Final Runnable" below. // MozPromise can be put in tail dispatch mode,or sync mode, and in those // cases, the ordering will be different.
InvokeAsync(GetCurrentSerialEventTarget(), __func__,
[]() { return SyncOperation(4); })
->Then(
GetCurrentSerialEventTarget(), __func__,
[&done](bool aResult) {
printf("[%zu] Sync promise value (InvokeAsync): %s\n", tid(),
aResult ? "true" : "false");
done = true;
},
[](nsresult aError) {
printf("[%zu] Error (InvokeAsync): 0x%x\n", tid(),
(unsigned)aError);
}); // Dispatch a runnable to the current even loop, for the sole purpose of // understanding ordering.
NS_DispatchToCurrentThread(NS_NewRunnableFunction("Final runnable", [] {
printf("[%zu] Dispatched after sync promise operation\n", tid());
}));
// The output will be as such (omitting the thread ids): // [...] SyncOperation(3) // [...] Dispatched before sync promise operation // [...] Sync promise value: true // [...] SyncOperation(4) // [...] Dispatched after sync promise operation // [...] Sync promise value (InvokeAsync): true
// Process all events and check that `done` was effectively set to true. This // is just for the purpose of this test.
MOZ_ALWAYS_TRUE(SpinEventLoopUntil( "xpcom:TEST(MozPromiseExamples, OneOff)"_ns, [&done]() { return done; }));
}
using IntPromise = MozPromise<int, nsresult, true>; using UintPromise = MozPromise<unsigned, nsresult, true>;
TEST(MozPromiseExamples, Chaining)
{ bool done = false;
SomethingSync s; // Do something that returns a bool, then chain it to a promise that returns // an int, then to a promise that returns an unsigned.
s.DoSomethingSync()
->Then(GetCurrentSerialEventTarget(), __func__,
[](GenericPromise::ResolveOrRejectValue&& aValue) { if (aValue.IsResolve()) { // Depending on the value of the bool, find the proper signed // integer value. return IntPromise::CreateAndResolve(
aValue.ResolveValue() ? 3 : 5, "Example IntPromise Resolver");
} return IntPromise::CreateAndReject(
aValue.RejectValue(), "Example IntPromise Rejecter");
})
->Then(GetCurrentSerialEventTarget(), __func__,
[&done](IntPromise::ResolveOrRejectValue&& aValue) { if (aValue.IsResolve()) { // Depending on the value of the signed integer, find the // proper unsigned integer value.
done = true; return UintPromise::CreateAndResolve( static_cast<unsigned>(aValue.ResolveValue()), "Example UintPromise Resolver");
} return UintPromise::CreateAndReject(
aValue.RejectValue(), "Example UintPromise Rejecter");
});
// Process all events and check that `done` was effectively set to true. This // is just for the purpose of this test.
MOZ_ALWAYS_TRUE(
SpinEventLoopUntil("xpcom:TEST(MozPromiseExamples, Chaining)"_ns,
[&done]() { return done; }));
}
// - converting an async legacy callback interface to a modern MozPromise // version with MozPromiseHolder.
¤ Dauer der Verarbeitung: 0.14 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.