/* * Copyright (c) 2019-2020, Sergey Bugaev * Copyright (c) 2021, Andreas Kling * Copyright (c) 2022-2023, the SerenityOS developers. * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once #include #include #include #include #include #include #include #include #include namespace Threading { template class BackgroundAction; class BackgroundActionBase { template friend class BackgroundAction; private: BackgroundActionBase() = default; static void enqueue_work(ESCAPING Function); static Thread& background_thread(); }; template class BackgroundAction final : public Core::EventReceiver , private BackgroundActionBase { C_OBJECT(BackgroundAction); public: // Promise is an implementation detail of BackgroundAction in order to communicate with EventLoop. // All of the promise's callbacks and state are either managed by us or by EventLoop. using Promise = Core::Promise>; virtual ~BackgroundAction() = default; Optional const& result() const { return m_result; } Optional& result() { return m_result; } void cancel() { m_canceled = true; } // If your action is long-running, you should periodically check the cancel state and possibly return early. bool is_canceled() const { return m_canceled; } private: BackgroundAction(ESCAPING Function(BackgroundAction&)> action, ESCAPING Function(Result)> on_complete, ESCAPING Optional> on_error = {}) : m_promise(Promise::try_create().release_value_but_fixme_should_propagate_errors()) , m_action(move(action)) , m_on_complete(move(on_complete)) { if (m_on_complete) { m_promise->on_resolution = [](NonnullRefPtr& object) -> ErrorOr { auto self = static_ptr_cast>(object); VERIFY(self->m_result.has_value()); if (auto maybe_error = self->m_on_complete(self->m_result.value()); maybe_error.is_error()) self->m_on_error(maybe_error.release_error()); return {}; }; Core::EventLoop::current().add_job(m_promise); } if (on_error.has_value()) m_on_error = on_error.release_value(); enqueue_work([self = NonnullRefPtr(*this), origin_event_loop = &Core::EventLoop::current()]() { auto result = self->m_action(*self); // The event loop cancels the promise when it exits. self->m_canceled |= self->m_promise->is_rejected(); // All of our work was successful and we weren't cancelled; resolve the event loop's promise. if (!self->m_canceled && !result.is_error()) { self->m_result = result.release_value(); // If there is no completion callback, we don't rely on the user keeping around the event loop. if (self->m_on_complete) { origin_event_loop->deferred_invoke([self] { // Our promise's resolution function will never error. (void)self->m_promise->resolve(*self); }); origin_event_loop->wake(); } } else { // We were either unsuccessful or cancelled (in which case there is no error). auto error = Error::from_errno(ECANCELED); if (result.is_error()) error = result.release_error(); self->m_promise->reject(Error::from_errno(ECANCELED)); if (!self->m_canceled && self->m_on_error) { origin_event_loop->deferred_invoke([self, error = move(error)]() mutable { self->m_on_error(move(error)); }); origin_event_loop->wake(); } else if (self->m_on_error) { self->m_on_error(move(error)); } } }); } NonnullRefPtr m_promise; Function(BackgroundAction&)> m_action; Function(Result)> m_on_complete; Function m_on_error = [](Error error) { dbgln("Error occurred while running a BackgroundAction: {}", error); }; Optional m_result; bool m_canceled { false }; }; void quit_background_thread(); }