mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-09-11 13:36:13 +03:00
Since the existing Promise class is designed with deferred tasks on the main thread only, we need a new class that will ensure we can handle promises that are resolved/rejected off the main thread. This new class ensures that the callbacks are only called on the same thread that the promise is fulfilled from. If the callbacks are not set before the thread tries to fulfill the promise, it will spin until they are so that they will run on that thread.
168 lines
4.1 KiB
C++
168 lines
4.1 KiB
C++
/*
|
|
* Copyright (c) 2023, Ali Mohammad Pur <mpfard@serenityos.org>
|
|
*
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
*/
|
|
|
|
#include <LibCore/EventLoop.h>
|
|
#include <LibCore/Promise.h>
|
|
#include <LibCore/ThreadedPromise.h>
|
|
#include <LibTest/TestSuite.h>
|
|
#include <LibThreading/Thread.h>
|
|
#include <unistd.h>
|
|
|
|
TEST_CASE(promise_await_async_event)
|
|
{
|
|
Core::EventLoop loop;
|
|
|
|
auto promise = MUST(Core::Promise<int>::try_create());
|
|
|
|
loop.deferred_invoke([=] {
|
|
promise->resolve(42);
|
|
});
|
|
|
|
auto result = promise->await();
|
|
EXPECT(!result.is_error());
|
|
EXPECT_EQ(result.value(), 42);
|
|
}
|
|
|
|
TEST_CASE(promise_await_async_event_rejection)
|
|
{
|
|
Core::EventLoop loop;
|
|
|
|
auto promise = MUST(Core::Promise<int>::try_create());
|
|
|
|
loop.deferred_invoke([=] {
|
|
promise->reject(AK::Error::from_string_literal("lol no"));
|
|
});
|
|
|
|
auto result = promise->await();
|
|
EXPECT(result.is_error());
|
|
EXPECT_EQ(result.error().string_literal(), "lol no"sv);
|
|
}
|
|
|
|
TEST_CASE(promise_chain_handlers)
|
|
{
|
|
Core::EventLoop loop;
|
|
|
|
bool resolved = false;
|
|
bool rejected = false;
|
|
|
|
NonnullRefPtr<Core::Promise<int>> promise = MUST(Core::Promise<int>::try_create())
|
|
->when_resolved([&](int&) -> ErrorOr<void> { resolved = true; return {}; })
|
|
.when_rejected([&](AK::Error const&) { rejected = true; });
|
|
|
|
loop.deferred_invoke([=] {
|
|
promise->resolve(42);
|
|
});
|
|
|
|
(void)promise->await();
|
|
EXPECT(resolved);
|
|
EXPECT(!rejected);
|
|
}
|
|
|
|
TEST_CASE(threaded_promise_instantly_resolved)
|
|
{
|
|
Core::EventLoop loop;
|
|
|
|
bool resolved = false;
|
|
bool rejected = true;
|
|
Optional<pthread_t> thread_id;
|
|
|
|
auto promise = Core::ThreadedPromise<int>::create();
|
|
|
|
auto thread = Threading::Thread::construct([&, promise] {
|
|
thread_id = pthread_self();
|
|
promise->resolve(42);
|
|
return 0;
|
|
});
|
|
thread->start();
|
|
|
|
promise
|
|
->when_resolved([&](int result) {
|
|
EXPECT(thread_id.has_value());
|
|
EXPECT(pthread_equal(thread_id.value(), pthread_self()));
|
|
resolved = true;
|
|
rejected = false;
|
|
EXPECT_EQ(result, 42);
|
|
})
|
|
.when_rejected([](Error&&) {
|
|
VERIFY_NOT_REACHED();
|
|
});
|
|
|
|
promise->await();
|
|
EXPECT(promise->has_completed());
|
|
EXPECT(resolved);
|
|
EXPECT(!rejected);
|
|
MUST(thread->join());
|
|
}
|
|
|
|
TEST_CASE(threaded_promise_resolved_later)
|
|
{
|
|
Core::EventLoop loop;
|
|
|
|
bool unblock_thread = false;
|
|
bool resolved = false;
|
|
bool rejected = true;
|
|
Optional<pthread_t> thread_id;
|
|
|
|
auto promise = Core::ThreadedPromise<int>::create();
|
|
|
|
auto thread = Threading::Thread::construct([&, promise] {
|
|
thread_id = pthread_self();
|
|
while (!unblock_thread)
|
|
usleep(500);
|
|
promise->resolve(42);
|
|
return 0;
|
|
});
|
|
thread->start();
|
|
|
|
promise
|
|
->when_resolved([&]() {
|
|
EXPECT(thread_id.has_value());
|
|
EXPECT(pthread_equal(thread_id.value(), pthread_self()));
|
|
EXPECT(unblock_thread);
|
|
resolved = true;
|
|
rejected = false;
|
|
})
|
|
.when_rejected([](Error&&) {
|
|
VERIFY_NOT_REACHED();
|
|
});
|
|
|
|
Core::EventLoop::current().deferred_invoke([&]() { unblock_thread = true; });
|
|
|
|
promise->await();
|
|
EXPECT(promise->has_completed());
|
|
EXPECT(unblock_thread);
|
|
EXPECT(resolved);
|
|
EXPECT(!rejected);
|
|
MUST(thread->join());
|
|
}
|
|
|
|
TEST_CASE(threaded_promise_synchronously_resolved)
|
|
{
|
|
Core::EventLoop loop;
|
|
|
|
bool resolved = false;
|
|
bool rejected = true;
|
|
auto thread_id = pthread_self();
|
|
|
|
auto promise = Core::ThreadedPromise<int>::create();
|
|
promise->resolve(1337);
|
|
|
|
promise
|
|
->when_resolved([&]() {
|
|
EXPECT(pthread_equal(thread_id, pthread_self()));
|
|
resolved = true;
|
|
rejected = false;
|
|
})
|
|
.when_rejected([](Error&&) {
|
|
VERIFY_NOT_REACHED();
|
|
});
|
|
|
|
promise->await();
|
|
EXPECT(promise->has_completed());
|
|
EXPECT(resolved);
|
|
EXPECT(!rejected);
|
|
}
|