| 
									
										
										
										
											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> | 
					
						
							| 
									
										
										
										
											2024-10-04 13:19:50 +02:00
										 |  |  |  * Copyright (c) 2021, Andreas Kling <andreas@ladybird.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>
 | 
					
						
							| 
									
										
										
										
											2023-08-06 18:09:39 +02:00
										 |  |  | #include <LibCore/EventReceiver.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
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-05-17 17:14:06 -07:00
										 |  |  |     static void enqueue_work(ESCAPING Function<void()>); | 
					
						
							| 
									
										
										
										
											2019-08-25 18:55:56 +03:00
										 |  |  |     static Thread& background_thread(); | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | template<typename Result> | 
					
						
							| 
									
										
										
										
											2024-05-17 17:14:06 -07:00
										 |  |  | class BackgroundAction final | 
					
						
							|  |  |  |     : public Core::EventReceiver | 
					
						
							| 
									
										
										
										
											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.
 | 
					
						
							| 
									
										
										
										
											2023-08-06 18:09:39 +02:00
										 |  |  |     using Promise = Core::Promise<NonnullRefPtr<Core::EventReceiver>>; | 
					
						
							| 
									
										
										
										
											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: | 
					
						
							| 
									
										
										
										
											2024-05-17 17:14:06 -07:00
										 |  |  |     BackgroundAction(ESCAPING Function<ErrorOr<Result>(BackgroundAction&)> action, ESCAPING Function<ErrorOr<void>(Result)> on_complete, ESCAPING Optional<Function<void(Error)>> on_error = {}) | 
					
						
							| 
									
										
										
										
											2025-02-09 13:00:10 -05:00
										 |  |  |         : m_action(move(action)) | 
					
						
							| 
									
										
										
										
											2019-08-25 18:55:56 +03:00
										 |  |  |         , m_on_complete(move(on_complete)) | 
					
						
							|  |  |  |     { | 
					
						
							| 
									
										
										
										
											2025-02-09 13:00:10 -05:00
										 |  |  |         auto promise = Promise::construct(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-29 14:57:00 +01:00
										 |  |  |         if (m_on_complete) { | 
					
						
							| 
									
										
										
										
											2025-02-09 13:00:10 -05:00
										 |  |  |             promise->on_resolution = [](NonnullRefPtr<Core::EventReceiver>& object) -> ErrorOr<void> { | 
					
						
							| 
									
										
										
										
											2022-12-29 14:57:00 +01:00
										 |  |  |                 auto self = static_ptr_cast<BackgroundAction<Result>>(object); | 
					
						
							|  |  |  |                 VERIFY(self->m_result.has_value()); | 
					
						
							| 
									
										
										
										
											2025-02-09 13:03:56 -05:00
										 |  |  |                 if (auto maybe_error = self->m_on_complete(self->m_result.release_value()); maybe_error.is_error()) | 
					
						
							| 
									
										
										
										
											2022-12-29 14:57:00 +01:00
										 |  |  |                     self->m_on_error(maybe_error.release_error()); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 return {}; | 
					
						
							|  |  |  |             }; | 
					
						
							| 
									
										
										
										
											2025-02-09 13:00:10 -05:00
										 |  |  |             Core::EventLoop::current().add_job(promise); | 
					
						
							| 
									
										
										
										
											2022-12-29 14:57:00 +01:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-12 23:34:28 +01:00
										 |  |  |         if (on_error.has_value()) | 
					
						
							|  |  |  |             m_on_error = on_error.release_value(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-02-09 13:00:10 -05:00
										 |  |  |         enqueue_work([self = NonnullRefPtr(*this), promise = move(promise), origin_event_loop = &Core::EventLoop::current()]() mutable { | 
					
						
							| 
									
										
										
										
											2023-08-06 01:21:49 -05:00
										 |  |  |             auto result = self->m_action(*self); | 
					
						
							| 
									
										
										
										
											2025-02-09 13:00:10 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-29 14:57:00 +01:00
										 |  |  |             // The event loop cancels the promise when it exits.
 | 
					
						
							| 
									
										
										
										
											2025-02-09 13:00:10 -05:00
										 |  |  |             self->m_canceled |= promise->is_rejected(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-29 14:57:00 +01:00
										 |  |  |             // All of our work was successful and we weren't cancelled; resolve the event loop's promise.
 | 
					
						
							| 
									
										
										
										
											2023-08-06 01:21:49 -05:00
										 |  |  |             if (!self->m_canceled && !result.is_error()) { | 
					
						
							|  |  |  |                 self->m_result = result.release_value(); | 
					
						
							| 
									
										
										
										
											2025-02-09 13:00:10 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-29 14:57:00 +01:00
										 |  |  |                 // If there is no completion callback, we don't rely on the user keeping around the event loop.
 | 
					
						
							| 
									
										
										
										
											2023-08-06 01:21:49 -05:00
										 |  |  |                 if (self->m_on_complete) { | 
					
						
							| 
									
										
										
										
											2025-02-09 13:00:10 -05:00
										 |  |  |                     origin_event_loop->deferred_invoke([self, promise = move(promise)] { | 
					
						
							| 
									
										
										
										
											2022-12-29 14:57:00 +01:00
										 |  |  |                         // Our promise's resolution function will never error.
 | 
					
						
							| 
									
										
										
										
											2025-02-09 13:00:10 -05:00
										 |  |  |                         (void)promise->resolve(*self); | 
					
						
							| 
									
										
										
										
											2022-12-29 14:57:00 +01:00
										 |  |  |                     }); | 
					
						
							|  |  |  |                     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(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-02-09 13:00:10 -05:00
										 |  |  |                 promise->reject(Error::from_errno(ECANCELED)); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-06 01:21:49 -05:00
										 |  |  |                 if (!self->m_canceled && self->m_on_error) { | 
					
						
							|  |  |  |                     origin_event_loop->deferred_invoke([self, error = move(error)]() mutable { | 
					
						
							|  |  |  |                         self->m_on_error(move(error)); | 
					
						
							| 
									
										
										
										
											2023-03-18 16:05:46 -04:00
										 |  |  |                     }); | 
					
						
							|  |  |  |                     origin_event_loop->wake(); | 
					
						
							| 
									
										
										
										
											2023-08-06 01:21:49 -05:00
										 |  |  |                 } else if (self->m_on_error) { | 
					
						
							|  |  |  |                     self->m_on_error(move(error)); | 
					
						
							| 
									
										
										
										
											2023-03-18 16:05:46 -04:00
										 |  |  |                 } | 
					
						
							| 
									
										
										
										
											2023-03-23 17:37:39 +00:00
										 |  |  |             } | 
					
						
							| 
									
										
										
										
											2019-08-25 18:55:56 +03:00
										 |  |  |         }); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-29 14:57:00 +01:00
										 |  |  |     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
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-23 18:57:28 -06:00
										 |  |  | void quit_background_thread(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-25 18:55:56 +03:00
										 |  |  | } |