/* * 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 #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: virtual ~BackgroundAction() = default; Optional const& result() const { return m_result; } Optional& result() { return m_result; } // Cancellation is a best-effort cross-thread signal. No other state is protected by this flag. // It is not used to synchronize access to any other state (m_result), so relaxed atomics are fine. void cancel() { m_canceled.store(true, AK::MemoryOrder::memory_order_relaxed); } // If your action is long-running, you should periodically check the cancel state and possibly return early. bool is_canceled() const { return m_canceled.load(AK::MemoryOrder::memory_order_relaxed); } private: BackgroundAction(ESCAPING Function(BackgroundAction&)> action, ESCAPING Function on_complete, ESCAPING Function on_error = {}) : m_action(move(action)) , m_on_complete(move(on_complete)) , m_on_error(move(on_error)) { enqueue_work([self = NonnullRefPtr(*this), origin_event_loop = Core::EventLoop::current_weak()]() mutable { auto result = self->m_action(*self); auto event_loop = origin_event_loop->take(); if (!event_loop) { dbgln("BackgroundAction {:p} was dropped, the origin loop is gone.", self.ptr()); return; } event_loop->deferred_invoke([self = move(self), result = move(result)]() mutable { auto const canceled = self->m_canceled.load(AK::MemoryOrder::memory_order_relaxed); if (canceled) return; if (result.is_error()) { if (self->m_on_error) self->m_on_error(result.release_error()); return; } if (self->m_on_complete) self->m_on_complete(result.release_value()); }); }); } Function(BackgroundAction&)> m_action; Function m_on_complete; Function m_on_error; Optional m_result; Atomic m_canceled { false }; }; void quit_background_thread(); }