1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 02:17:35 +00:00

LibCore: Add a class for thread-safe promises

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.
This commit is contained in:
Zaggy1024 2023-07-11 20:48:56 -05:00 committed by Andrew Kaster
parent 8626404ddb
commit fe672989a9
7 changed files with 321 additions and 4 deletions

View file

@ -6,7 +6,10 @@
#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)
{
@ -57,3 +60,108 @@ TEST_CASE(promise_chain_handlers)
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);
}