BackgroundAction.h 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. /*
  2. * Copyright (c) 2019-2020, Sergey Bugaev <bugaevc@serenityos.org>
  3. * Copyright (c) 2021, Andreas Kling <kling@serenityos.org>
  4. * Copyright (c) 2022-2023, the SerenityOS developers.
  5. *
  6. * SPDX-License-Identifier: BSD-2-Clause
  7. */
  8. #pragma once
  9. #include <AK/Function.h>
  10. #include <AK/NonnullRefPtr.h>
  11. #include <AK/Optional.h>
  12. #include <AK/Queue.h>
  13. #include <LibCore/Event.h>
  14. #include <LibCore/EventLoop.h>
  15. #include <LibCore/Object.h>
  16. #include <LibCore/Promise.h>
  17. #include <LibThreading/Thread.h>
  18. namespace Threading {
  19. template<typename Result>
  20. class BackgroundAction;
  21. class BackgroundActionBase {
  22. template<typename Result>
  23. friend class BackgroundAction;
  24. private:
  25. BackgroundActionBase() = default;
  26. static void enqueue_work(Function<void()>);
  27. static Thread& background_thread();
  28. };
  29. template<typename Result>
  30. class BackgroundAction final : public Core::Object
  31. , private BackgroundActionBase {
  32. C_OBJECT(BackgroundAction);
  33. public:
  34. // Promise is an implementation detail of BackgroundAction in order to communicate with EventLoop.
  35. // All of the promise's callbacks and state are either managed by us or by EventLoop.
  36. using Promise = Core::Promise<NonnullRefPtr<Core::Object>>;
  37. virtual ~BackgroundAction() = default;
  38. Optional<Result> const& result() const { return m_result; }
  39. Optional<Result>& result() { return m_result; }
  40. void cancel() { m_canceled = true; }
  41. // If your action is long-running, you should periodically check the cancel state and possibly return early.
  42. bool is_canceled() const { return m_canceled; }
  43. private:
  44. BackgroundAction(Function<ErrorOr<Result>(BackgroundAction&)> action, Function<ErrorOr<void>(Result)> on_complete, Optional<Function<void(Error)>> on_error = {})
  45. : Core::Object(&background_thread())
  46. , m_promise(Promise::try_create().release_value_but_fixme_should_propagate_errors())
  47. , m_action(move(action))
  48. , m_on_complete(move(on_complete))
  49. {
  50. if (m_on_complete) {
  51. m_promise->on_resolved = [](NonnullRefPtr<Core::Object>& object) -> ErrorOr<void> {
  52. auto self = static_ptr_cast<BackgroundAction<Result>>(object);
  53. VERIFY(self->m_result.has_value());
  54. if (auto maybe_error = self->m_on_complete(self->m_result.value()); maybe_error.is_error())
  55. self->m_on_error(maybe_error.release_error());
  56. return {};
  57. };
  58. Core::EventLoop::current().add_job(m_promise);
  59. }
  60. if (on_error.has_value())
  61. m_on_error = on_error.release_value();
  62. enqueue_work([this, origin_event_loop = &Core::EventLoop::current()] {
  63. auto result = m_action(*this);
  64. // The event loop cancels the promise when it exits.
  65. m_canceled |= m_promise->is_canceled();
  66. auto callback_scheduled = false;
  67. // All of our work was successful and we weren't cancelled; resolve the event loop's promise.
  68. if (!m_canceled && !result.is_error()) {
  69. m_result = result.release_value();
  70. // If there is no completion callback, we don't rely on the user keeping around the event loop.
  71. if (m_on_complete) {
  72. callback_scheduled = true;
  73. origin_event_loop->deferred_invoke([this] {
  74. // Our promise's resolution function will never error.
  75. (void)m_promise->resolve(*this);
  76. remove_from_parent();
  77. });
  78. origin_event_loop->wake();
  79. }
  80. } else {
  81. // We were either unsuccessful or cancelled (in which case there is no error).
  82. auto error = Error::from_errno(ECANCELED);
  83. if (result.is_error())
  84. error = result.release_error();
  85. m_promise->cancel(Error::from_errno(ECANCELED));
  86. if (m_on_error) {
  87. callback_scheduled = true;
  88. origin_event_loop->deferred_invoke([this, error = move(error)]() mutable {
  89. m_on_error(move(error));
  90. remove_from_parent();
  91. });
  92. origin_event_loop->wake();
  93. }
  94. }
  95. if (!callback_scheduled)
  96. remove_from_parent();
  97. });
  98. }
  99. NonnullRefPtr<Promise> m_promise;
  100. Function<ErrorOr<Result>(BackgroundAction&)> m_action;
  101. Function<ErrorOr<void>(Result)> m_on_complete;
  102. Function<void(Error)> m_on_error = [](Error error) {
  103. dbgln("Error occurred while running a BackgroundAction: {}", error);
  104. };
  105. Optional<Result> m_result;
  106. bool m_canceled { false };
  107. };
  108. }