| 
									
										
										
										
											2020-01-18 09:38:21 +01:00
										 |  |  | /*
 | 
					
						
							| 
									
										
										
										
											2020-01-24 16:45:29 +03:00
										 |  |  |  * Copyright (c) 2019-2020, Sergey Bugaev <bugaevc@serenityos.org> | 
					
						
							| 
									
										
										
										
											2021-07-04 18:01:01 +02:00
										 |  |  |  * Copyright (c) 2021, Andreas Kling <kling@serenityos.org> | 
					
						
							| 
									
										
										
										
											2022-12-29 14:57:00 +01:00
										 |  |  |  * Copyright (c) 2022-2023, the SerenityOS developers. | 
					
						
							| 
									
										
										
										
											2020-01-18 09:38:21 +01:00
										 |  |  |  * | 
					
						
							| 
									
										
										
										
											2021-04-22 01:24:48 -07:00
										 |  |  |  * SPDX-License-Identifier: BSD-2-Clause | 
					
						
							| 
									
										
										
										
											2020-01-18 09:38:21 +01:00
										 |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-25 18:55:56 +03:00
										 |  |  | #pragma once
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <AK/Function.h>
 | 
					
						
							|  |  |  | #include <AK/NonnullRefPtr.h>
 | 
					
						
							|  |  |  | #include <AK/Optional.h>
 | 
					
						
							|  |  |  | #include <AK/Queue.h>
 | 
					
						
							| 
									
										
										
										
											2020-02-14 22:29:06 +01:00
										 |  |  | #include <LibCore/Event.h>
 | 
					
						
							| 
									
										
										
										
											2020-02-06 15:04:03 +01:00
										 |  |  | #include <LibCore/EventLoop.h>
 | 
					
						
							|  |  |  | #include <LibCore/Object.h>
 | 
					
						
							| 
									
										
										
										
											2022-12-29 14:57:00 +01:00
										 |  |  | #include <LibCore/Promise.h>
 | 
					
						
							| 
									
										
										
										
											2021-05-22 18:47:42 +02:00
										 |  |  | #include <LibThreading/Thread.h>
 | 
					
						
							| 
									
										
										
										
											2019-08-25 18:55:56 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-22 18:47:42 +02:00
										 |  |  | namespace Threading { | 
					
						
							| 
									
										
										
										
											2019-08-25 18:55:56 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | template<typename Result> | 
					
						
							|  |  |  | class BackgroundAction; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class BackgroundActionBase { | 
					
						
							|  |  |  |     template<typename Result> | 
					
						
							|  |  |  |     friend class BackgroundAction; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | private: | 
					
						
							| 
									
										
										
										
											2022-03-04 13:26:44 -07:00
										 |  |  |     BackgroundActionBase() = default; | 
					
						
							| 
									
										
										
										
											2019-08-25 18:55:56 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-04 18:01:01 +02:00
										 |  |  |     static void enqueue_work(Function<void()>); | 
					
						
							| 
									
										
										
										
											2019-08-25 18:55:56 +03:00
										 |  |  |     static Thread& background_thread(); | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | template<typename Result> | 
					
						
							| 
									
										
										
										
											2020-02-02 12:34:39 +01:00
										 |  |  | class BackgroundAction final : public Core::Object | 
					
						
							| 
									
										
										
										
											2019-08-25 18:55:56 +03:00
										 |  |  |     , private BackgroundActionBase { | 
					
						
							|  |  |  |     C_OBJECT(BackgroundAction); | 
					
						
							| 
									
										
										
										
											2020-02-02 12:34:39 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-25 18:55:56 +03:00
										 |  |  | public: | 
					
						
							| 
									
										
										
										
											2022-12-29 14:57:00 +01:00
										 |  |  |     // 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<NonnullRefPtr<Core::Object>>; | 
					
						
							| 
									
										
										
										
											2021-07-02 10:34:19 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-03-04 13:26:44 -07:00
										 |  |  |     virtual ~BackgroundAction() = default; | 
					
						
							| 
									
										
										
										
											2019-08-25 18:55:56 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-29 14:57:00 +01:00
										 |  |  |     Optional<Result> const& result() const { return m_result; } | 
					
						
							|  |  |  |     Optional<Result>& 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; } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-25 18:55:56 +03:00
										 |  |  | private: | 
					
						
							| 
									
										
										
										
											2022-12-29 14:57:00 +01:00
										 |  |  |     BackgroundAction(Function<ErrorOr<Result>(BackgroundAction&)> action, Function<ErrorOr<void>(Result)> on_complete, Optional<Function<void(Error)>> on_error = {}) | 
					
						
							| 
									
										
										
										
											2020-02-02 12:34:39 +01:00
										 |  |  |         : Core::Object(&background_thread()) | 
					
						
							| 
									
										
										
										
											2022-12-29 14:57:00 +01:00
										 |  |  |         , m_promise(Promise::try_create().release_value_but_fixme_should_propagate_errors()) | 
					
						
							| 
									
										
										
										
											2019-08-25 18:55:56 +03:00
										 |  |  |         , m_action(move(action)) | 
					
						
							|  |  |  |         , m_on_complete(move(on_complete)) | 
					
						
							|  |  |  |     { | 
					
						
							| 
									
										
										
										
											2022-12-29 14:57:00 +01:00
										 |  |  |         if (m_on_complete) { | 
					
						
							|  |  |  |             m_promise->on_resolved = [](NonnullRefPtr<Core::Object>& object) -> ErrorOr<void> { | 
					
						
							|  |  |  |                 auto self = static_ptr_cast<BackgroundAction<Result>>(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); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-12 23:34:28 +01:00
										 |  |  |         if (on_error.has_value()) | 
					
						
							|  |  |  |             m_on_error = on_error.release_value(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-01-25 08:54:54 +01:00
										 |  |  |         enqueue_work([this, origin_event_loop = &Core::EventLoop::current()] { | 
					
						
							| 
									
										
										
										
											2022-12-29 14:57:00 +01:00
										 |  |  |             auto result = m_action(*this); | 
					
						
							|  |  |  |             // The event loop cancels the promise when it exits.
 | 
					
						
							|  |  |  |             m_canceled |= m_promise->is_canceled(); | 
					
						
							|  |  |  |             // All of our work was successful and we weren't cancelled; resolve the event loop's promise.
 | 
					
						
							|  |  |  |             if (!m_canceled && !result.is_error()) { | 
					
						
							|  |  |  |                 m_result = result.release_value(); | 
					
						
							|  |  |  |                 // If there is no completion callback, we don't rely on the user keeping around the event loop.
 | 
					
						
							|  |  |  |                 if (m_on_complete) { | 
					
						
							|  |  |  |                     origin_event_loop->deferred_invoke([this] { | 
					
						
							|  |  |  |                         // Our promise's resolution function will never error.
 | 
					
						
							|  |  |  |                         (void)m_promise->resolve(*this); | 
					
						
							|  |  |  |                         remove_from_parent(); | 
					
						
							|  |  |  |                     }); | 
					
						
							|  |  |  |                     origin_event_loop->wake(); | 
					
						
							|  |  |  |                 } | 
					
						
							| 
									
										
										
										
											2020-02-25 12:05:25 +03:00
										 |  |  |             } else { | 
					
						
							| 
									
										
										
										
											2022-12-29 14:57:00 +01:00
										 |  |  |                 // 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(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 m_promise->cancel(Error::from_errno(ECANCELED)); | 
					
						
							|  |  |  |                 if (m_on_error) | 
					
						
							|  |  |  |                     m_on_error(move(error)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 remove_from_parent(); | 
					
						
							| 
									
										
										
										
											2020-02-25 12:05:25 +03:00
										 |  |  |             } | 
					
						
							| 
									
										
										
										
											2019-08-25 18:55:56 +03:00
										 |  |  |         }); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-29 14:57:00 +01:00
										 |  |  |     NonnullRefPtr<Promise> m_promise; | 
					
						
							|  |  |  |     Function<ErrorOr<Result>(BackgroundAction&)> m_action; | 
					
						
							| 
									
										
										
										
											2022-12-12 23:34:28 +01:00
										 |  |  |     Function<ErrorOr<void>(Result)> m_on_complete; | 
					
						
							|  |  |  |     Function<void(Error)> m_on_error = [](Error error) { | 
					
						
							|  |  |  |         dbgln("Error occurred while running a BackgroundAction: {}", error); | 
					
						
							|  |  |  |     }; | 
					
						
							| 
									
										
										
										
											2019-08-25 18:55:56 +03:00
										 |  |  |     Optional<Result> m_result; | 
					
						
							| 
									
										
										
										
											2022-12-29 14:57:00 +01:00
										 |  |  |     bool m_canceled { false }; | 
					
						
							| 
									
										
										
										
											2019-08-25 18:55:56 +03:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | } |