/* * Copyright (c) 2021, Kyle Pereira * Copyright (c) 2022, kleines Filmröllchen * Copyright (c) 2021-2023, Ali Mohammad Pur * Copyright (c) 2023, Gregory Bertilson * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once #include #include #include #include #include namespace Core { template class ThreadedPromise : public AtomicRefCounted> { public: static NonnullRefPtr> create() { return adopt_ref(*new ThreadedPromise()); } using ResultType = Conditional, Empty, TResult>; using ErrorType = TError; void resolve(ResultType&& result) { when_error_handler_is_ready([self = NonnullRefPtr(*this), result = move(result)]() mutable { if (self->m_resolution_handler) { auto handler_result = self->m_resolution_handler(forward(result)); if (handler_result.is_error()) self->m_rejection_handler(handler_result.release_error()); self->m_has_completed = true; } }); } void resolve() requires IsSame { resolve(Empty()); } void reject(ErrorType&& error) { when_error_handler_is_ready([this, error = move(error)]() mutable { m_rejection_handler(forward(error)); m_has_completed = true; }); } void reject(ErrorType const& error) requires IsTriviallyCopyable { reject(ErrorType(error)); } bool has_completed() { Threading::MutexLocker locker { m_mutex }; return m_has_completed; } void await() { while (!has_completed()) Core::EventLoop::current().pump(EventLoop::WaitMode::PollForEvents); } // Set the callback to be called when the promise is resolved. A rejection callback // must also be provided before any callback will be called. template, ResultType&&> ResolvedHandler> ThreadedPromise& when_resolved(ResolvedHandler handler) { Threading::MutexLocker locker { m_mutex }; VERIFY(!m_resolution_handler); m_resolution_handler = move(handler); return *this; } template ResolvedHandler> ThreadedPromise& when_resolved(ResolvedHandler handler) { return when_resolved([handler = move(handler)](ResultType&& result) -> ErrorOr { handler(forward(result)); return {}; }); } template> ResolvedHandler> ThreadedPromise& when_resolved(ResolvedHandler handler) { return when_resolved([handler = move(handler)](ResultType&&) -> ErrorOr { return handler(); }); } template ResolvedHandler> ThreadedPromise& when_resolved(ResolvedHandler handler) { return when_resolved([handler = move(handler)](ResultType&&) -> ErrorOr { handler(); return {}; }); } // Set the callback to be called when the promise is rejected. Setting this callback // will cause the promise fulfillment to be ready to be handled. template RejectedHandler> ThreadedPromise& when_rejected(RejectedHandler when_rejected = [](ErrorType&) {}) { Threading::MutexLocker locker { m_mutex }; VERIFY(!m_rejection_handler); m_rejection_handler = move(when_rejected); return *this; } template>, ResultType&&> ChainedResolution> NonnullRefPtr> chain_promise(ChainedResolution chained_resolution) { auto new_promise = ThreadedPromise::create(); when_resolved([=, chained_resolution = move(chained_resolution)](ResultType&& result) mutable -> ErrorOr { chained_resolution(forward(result)) ->when_resolved([=](auto&& new_result) { new_promise->resolve(move(new_result)); }) .when_rejected([=](ErrorType&& error) { new_promise->reject(move(error)); }); return {}; }); when_rejected([=](ErrorType&& error) { new_promise->reject(move(error)); }); return new_promise; } template, ResultType&&> MappingFunction> NonnullRefPtr> map(MappingFunction mapping_function) { auto new_promise = ThreadedPromise::create(); when_resolved([=, mapping_function = move(mapping_function)](ResultType&& result) -> ErrorOr { new_promise->resolve(TRY(mapping_function(forward(result)))); return {}; }); when_rejected([=](ErrorType&& error) { new_promise->reject(move(error)); }); return new_promise; } private: template static void deferred_handler_check(NonnullRefPtr self, F&& function) { Threading::MutexLocker locker { self->m_mutex }; if (self->m_rejection_handler) { function(); return; } EventLoop::current().deferred_invoke([self, function = forward(function)]() mutable { deferred_handler_check(self, move(function)); }); } template void when_error_handler_is_ready(F function) { if (EventLoop::is_running()) { deferred_handler_check(NonnullRefPtr(*this), move(function)); } else { // NOTE: Handlers should always be set almost immediately, so we can expect this // to spin extremely briefly. Therefore, sleeping the thread should not be // necessary. while (true) { Threading::MutexLocker locker { m_mutex }; if (m_rejection_handler) break; } VERIFY(m_rejection_handler); function(); } } ThreadedPromise() = default; ThreadedPromise(EventReceiver* parent) : EventReceiver(parent) { } Function(ResultType&&)> m_resolution_handler; Function m_rejection_handler; Threading::Mutex m_mutex; bool m_has_completed; }; }