Async++ unknown
Async (co_await/co_return) code for C++
Loading...
Searching...
No Matches
fire_and_forget.h
1#pragma once
2#include <asyncpp/detail/concepts.h>
3#include <asyncpp/detail/promise_allocator_base.h>
5#include <asyncpp/policy.h>
6#include <atomic>
7#include <utility>
8
9namespace asyncpp {
10 namespace detail {
21 template<bool Eager = false, ByteAllocator Allocator = default_allocator_type>
22 struct fire_and_forget_task_impl {
23 // Promise type of this task
24 class promise_type : public promise_allocator_base<Allocator> {
25 std::atomic<size_t> m_ref_count{1};
26 std::function<void()> m_exception_handler{};
27
28 public:
29 constexpr promise_type() noexcept = default;
30 promise_type(const promise_type&) = delete;
31 promise_type(promise_type&&) = delete;
32
33 auto get_return_object() noexcept { return coroutine_handle<promise_type>::from_promise(*this); }
34 constexpr auto initial_suspend() noexcept {
35 struct awaiter {
36 promise_type* self;
37
38 [[nodiscard]] constexpr bool await_ready() const noexcept { return Eager; }
39 constexpr void await_suspend(coroutine_handle<>) const noexcept {}
40 constexpr void await_resume() const noexcept { self->ref(); }
41 };
42 return awaiter{this};
43 }
44 auto final_suspend() noexcept {
45 struct awaiter {
46 promise_type* self;
47 [[nodiscard]] constexpr bool await_ready() const noexcept {
48 return self->m_ref_count.fetch_sub(1) == 1;
49 }
50 void await_suspend(coroutine_handle<>) const noexcept {}
51 constexpr void await_resume() const noexcept {}
52 };
53 return awaiter{this};
54 }
55 constexpr void return_void() noexcept {}
56 void unhandled_exception() noexcept {
57 if (m_exception_handler)
58 m_exception_handler();
59 else
60 std::terminate();
61 }
62
63 auto await_transform(exception_policy policy) {
64 m_exception_handler = std::move(policy.handler);
65 return suspend_never{};
66 }
67 template<typename U>
68 constexpr U&& await_transform(U&& awaitable) noexcept {
69 return static_cast<U&&>(awaitable);
70 }
71
72 void unref() noexcept {
73 if (m_ref_count.fetch_sub(1) == 1) coroutine_handle<promise_type>::from_promise(*this).destroy();
74 }
75 void ref() noexcept { m_ref_count.fetch_add(1); }
76 };
77
79 //NOLINTNEXTLINE(google-explicit-constructor)
80 fire_and_forget_task_impl(coroutine_handle<promise_type> hndl) noexcept : m_coro(hndl) {}
81
83 fire_and_forget_task_impl(fire_and_forget_task_impl&& other) noexcept
84 : m_coro(std::exchange(other.m_coro, {})) {}
85
87 fire_and_forget_task_impl& operator=(fire_and_forget_task_impl&& other) noexcept {
88 m_coro = std::exchange(other.m_coro, m_coro);
89 return *this;
90 }
91
92 fire_and_forget_task_impl(const fire_and_forget_task_impl& other) : m_coro{other.m_coro} {
93 if (m_coro) m_coro.promise().ref();
94 }
95
96 fire_and_forget_task_impl& operator=(const fire_and_forget_task_impl& other) {
97 if (&other != this) {
98 if (m_coro) m_coro.promise().unref();
99 m_coro = other.m_coro;
100 if (m_coro) m_coro.promise().ref();
101 }
102 return *this;
103 }
104
105 ~fire_and_forget_task_impl() {
106 if (m_coro) m_coro.promise().unref();
107 }
108
110 void start() noexcept
111 requires(!Eager)
112 {
113 if (m_coro && !m_coro.done()) {
114 m_coro.resume();
115 // Make sure it is only started once
116 m_coro.promise().unref();
117 m_coro = nullptr;
118 }
119 }
120
121 private:
122 coroutine_handle<promise_type> m_coro;
123 };
124 } // namespace detail
125
127 template<class Allocator = default_allocator_type>
128 using eager_fire_and_forget_task = detail::fire_and_forget_task_impl<true, Allocator>;
130 template<class Allocator = default_allocator_type>
131 using fire_and_forget_task = detail::fire_and_forget_task_impl<false, Allocator>;
132} // namespace asyncpp
Provides a consistent import interface for coroutine, experimental/coroutine or a best effort fallbac...