libcopp 2.3.2
Loading...
Searching...
No Matches
std_coroutine_common.cpp
Go to the documentation of this file.
1// Copyright 2026 owent
2
4
5#include <libcopp/utils/config/libcopp_build_features.h>
6
7// clang-format off
8#include <libcopp/utils/config/stl_include_prefix.h> // NOLINT(build/include_order)
9// clang-format on
10#include <assert.h>
11#include <stdint.h>
12// clang-format off
13#include <libcopp/utils/config/stl_include_suffix.h> // NOLINT(build/include_order)
14// clang-format on
15
16#if defined(LIBCOPP_MACRO_ENABLE_STD_COROUTINE) && LIBCOPP_MACRO_ENABLE_STD_COROUTINE
17
18LIBCOPP_COPP_NAMESPACE_BEGIN
19
20LIBCOPP_COPP_API promise_caller_manager::promise_caller_manager()
21 :
22# if defined(LIBCOPP_MACRO_ENABLE_STD_VARIANT) && LIBCOPP_MACRO_ENABLE_STD_VARIANT
23 callers_(handle_delegate{nullptr})
24# else
25 unique_caller_(nullptr)
26# endif
27{
28}
29
30LIBCOPP_COPP_API promise_caller_manager::~promise_caller_manager() {}
31
32LIBCOPP_COPP_API void promise_caller_manager::add_caller(handle_delegate delegate) noexcept {
33 if (!delegate.handle || delegate.handle.done()) {
34 return;
35 }
36
37# if defined(LIBCOPP_MACRO_ENABLE_STD_VARIANT) && LIBCOPP_MACRO_ENABLE_STD_VARIANT
38 if (std::holds_alternative<multi_caller_container>(callers_)) {
39 std::get<multi_caller_container>(callers_).add(delegate);
40 return;
41 }
42
43 if (!std::get<handle_delegate>(callers_)) {
44 std::get<handle_delegate>(callers_) = delegate;
45 return;
46 }
47
48 if (std::get<handle_delegate>(callers_) == delegate) {
49 return;
50 }
51
52 // convert to multiple callers and keep the registration order
53 multi_caller_container callers;
54 callers.add(std::get<handle_delegate>(callers_));
55 callers.add(delegate);
56 callers_.emplace<multi_caller_container>(std::move(callers));
57# else
58 // The single-caller slot is resumed first, so only reuse it when no callers are queued.
59 if (!unique_caller_.handle && (!multiple_callers_ || multiple_callers_->size() == 0)) {
60 unique_caller_ = delegate;
61 return;
62 }
63
64 if (unique_caller_ == delegate) {
65 return;
66 }
67
68 if (!multiple_callers_) {
69 multiple_callers_.reset(new multi_caller_container());
70 }
71 multiple_callers_->add(delegate);
72# endif
73}
74
75LIBCOPP_COPP_API bool promise_caller_manager::remove_caller(handle_delegate delegate) noexcept {
76 bool has_caller = false;
77 do {
78# if defined(LIBCOPP_MACRO_ENABLE_STD_VARIANT) && LIBCOPP_MACRO_ENABLE_STD_VARIANT
79 if (std::holds_alternative<multi_caller_container>(callers_)) {
80 has_caller = std::get<multi_caller_container>(callers_).remove(delegate);
81 break;
82 }
83
84 if (std::get<handle_delegate>(callers_).handle == delegate.handle) {
85 std::get<handle_delegate>(callers_) = nullptr;
86 has_caller = true;
87 }
88# else
89 if (unique_caller_.handle == delegate.handle) {
90 unique_caller_ = nullptr;
91 has_caller = true;
92 break;
93 }
94
95 if (multiple_callers_) {
96 has_caller = multiple_callers_->remove(delegate);
97 }
98# endif
99 } while (false);
100
101 return has_caller;
102}
103
104LIBCOPP_COPP_API size_t promise_caller_manager::resume_callers() {
105 size_t resume_count = 0;
106# if defined(LIBCOPP_MACRO_ENABLE_STD_VARIANT) && LIBCOPP_MACRO_ENABLE_STD_VARIANT
107 if (std::holds_alternative<handle_delegate>(callers_)) {
108 auto caller = std::get<handle_delegate>(callers_);
109 std::get<handle_delegate>(callers_) = nullptr;
110 if (caller.handle && !caller.handle.done() &&
111 (nullptr == caller.promise || !caller.promise->check_flag(promise_flag::kDestroying))) {
112 caller.handle.resume();
113 ++resume_count;
114 }
115 } else if (std::holds_alternative<multi_caller_container>(callers_)) {
116 multi_caller_container callers;
117 callers.swap(std::get<multi_caller_container>(callers_));
118 for (auto &caller : callers.callers) {
119 if (caller.handle && !caller.handle.done() &&
120 (nullptr == caller.promise || !caller.promise->check_flag(promise_flag::kDestroying))) {
121 type_erased_handle_type handle = caller.handle;
122 handle.resume();
123 ++resume_count;
124 }
125 }
126 }
127# else
128 auto unique_caller = unique_caller_;
129 unique_caller_ = nullptr;
130 std::unique_ptr<multi_caller_container> multiple_callers;
131 multiple_callers.swap(multiple_callers_);
132
133 // The promise object may be destroyed after first caller.resume()
134 if (unique_caller.handle && !unique_caller.handle.done() &&
135 (nullptr == unique_caller.promise || !unique_caller.promise->check_flag(promise_flag::kDestroying))) {
136 unique_caller.handle.resume();
137 ++resume_count;
138 }
139
140 if (multiple_callers) {
141 for (auto &caller : multiple_callers->callers) {
142 if (caller.handle && !caller.handle.done() &&
143 (nullptr == caller.promise || !caller.promise->check_flag(promise_flag::kDestroying))) {
144 type_erased_handle_type handle = caller.handle;
145 handle.resume();
146 ++resume_count;
147 }
148 }
149 }
150# endif
151 return resume_count;
152}
153
154LIBCOPP_COPP_API bool promise_caller_manager::has_multiple_callers() const noexcept {
155# if defined(LIBCOPP_MACRO_ENABLE_STD_VARIANT) && LIBCOPP_MACRO_ENABLE_STD_VARIANT
156 if (std::holds_alternative<handle_delegate>(callers_)) {
157 return false;
158 } else if (std::holds_alternative<multi_caller_container>(callers_)) {
159 return std::get<multi_caller_container>(callers_).size() > 1;
160 }
161 return false;
162# else
163 size_t count = 0;
164 if (unique_caller_.handle && !unique_caller_.handle.done() &&
165 (nullptr == unique_caller_.promise || !unique_caller_.promise->check_flag(promise_flag::kDestroying))) {
166 ++count;
167 }
168
169 if (multiple_callers_) {
170 count += multiple_callers_->size();
171 }
172 return count > 1;
173# endif
174}
175
176LIBCOPP_COPP_API promise_base_type::pick_promise_status_awaitable::pick_promise_status_awaitable() noexcept
177 : data(promise_status::kInvalid) {}
178
179LIBCOPP_COPP_API promise_base_type::pick_promise_status_awaitable::pick_promise_status_awaitable(
180 promise_status status) noexcept
181 : data(status) {}
182
183LIBCOPP_COPP_API promise_base_type::pick_promise_status_awaitable::pick_promise_status_awaitable(
184 pick_promise_status_awaitable &&other) noexcept
185 : data(other.data) {}
186
187LIBCOPP_COPP_API promise_base_type::pick_promise_status_awaitable &
188promise_base_type::pick_promise_status_awaitable::operator=(pick_promise_status_awaitable &&other) noexcept {
189 data = other.data;
190 return *this;
191}
192
193LIBCOPP_COPP_API promise_base_type::pick_promise_status_awaitable::~pick_promise_status_awaitable() {}
194
195LIBCOPP_COPP_API promise_base_type::promise_base_type()
196 : flags_(0), status_{promise_status::kCreated}, current_waiting_{nullptr} {}
197
198LIBCOPP_COPP_API promise_base_type::~promise_base_type() {}
199
200LIBCOPP_COPP_API bool promise_base_type::is_waiting() const noexcept {
201 return current_waiting_ || check_flag(promise_flag::kInternalWaitting);
202}
203
204LIBCOPP_COPP_API void promise_base_type::set_waiting_handle(std::nullptr_t) noexcept { current_waiting_ = nullptr; }
205
206LIBCOPP_COPP_API void promise_base_type::set_waiting_handle(handle_delegate handle) { current_waiting_ = handle; }
207
208LIBCOPP_COPP_API void promise_base_type::resume_waiting(handle_delegate current_delegate, bool inherit_status) {
209 // Atfer resume(), this object maybe destroyed.
210 auto waiting_delegate = current_waiting_;
211
212 // Resume the waiting promise.
213 if (waiting_delegate.handle && !waiting_delegate.handle.done()) {
214 current_waiting_ = nullptr;
215 // Prevent the waiting coroutine remuse this again.
216 if (nullptr != waiting_delegate.promise) {
217 waiting_delegate.promise->remove_caller(current_delegate, inherit_status);
218 }
219 waiting_delegate.handle.resume();
220 } else if (current_delegate.handle && !current_delegate.handle.done() &&
221 check_flag(promise_flag::kInternalWaitting)) {
222 // If we are waiting for a internal awaitable object, we also allow to resume it.
223 current_delegate.handle.resume();
224 }
225}
226
227LIBCOPP_COPP_API promise_base_type::pick_promise_status_awaitable promise_base_type::yield_value(
228 pick_promise_status_awaitable &&args) const noexcept {
229 args.data = get_status();
230 return pick_promise_status_awaitable{args.data};
231}
232
233LIBCOPP_COPP_API void promise_base_type::add_caller(handle_delegate delegate) noexcept {
234 caller_manager_.add_caller(delegate);
235}
236
237LIBCOPP_COPP_API void promise_base_type::remove_caller(handle_delegate delegate, bool inherit_status) noexcept {
238 bool remove_caller_success = caller_manager_.remove_caller(delegate);
239 if (remove_caller_success && inherit_status && nullptr != delegate.promise && get_status() < promise_status::kDone &&
240 delegate.promise->get_status() > promise_status::kDone) {
241 set_status(delegate.promise->get_status());
242 }
243}
244
245LIBCOPP_COPP_API void promise_base_type::resume_callers() { caller_manager_.resume_callers(); }
246
247LIBCOPP_COPP_API awaitable_base_type::awaitable_base_type() : caller_{nullptr} {}
248LIBCOPP_COPP_API awaitable_base_type::~awaitable_base_type() {}
249
250LIBCOPP_COPP_API promise_base_type::handle_delegate awaitable_base_type::get_caller() const noexcept { return caller_; }
251
252LIBCOPP_COPP_API void awaitable_base_type::set_caller(promise_base_type::handle_delegate caller) noexcept {
253 caller_ = caller;
254}
255
256LIBCOPP_COPP_API void awaitable_base_type::set_caller(std::nullptr_t) noexcept { caller_ = nullptr; }
257
258LIBCOPP_COPP_NAMESPACE_END
259
260#endif
constexpr auto data(TCONTAINER &&container) -> decltype(container.data())
Definition span.h:54