libcopp 2.3.2
Loading...
Searching...
No Matches
stackful_channel.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
11#include <assert.h>
12#include <stdint.h>
13
14// clang-format off
15#include <libcopp/utils/config/stl_include_suffix.h> // NOLINT(build/include_order)
16// clang-format on
17
20
21LIBCOPP_COPP_NAMESPACE_BEGIN
22
24 void *invoke_ctx, stackful_channel_context_base *priv_data) {
26 reinterpret_cast<coroutine_context_base *>(invoke_ctx), priv_data);
27}
28
29#if defined(LIBCOPP_MACRO_ENABLE_WIN_FIBER) && LIBCOPP_MACRO_ENABLE_WIN_FIBER
31 void *invoke_ctx, stackful_channel_context_base *priv_data) {
33 reinterpret_cast<coroutine_context_base *>(invoke_ctx), priv_data);
34}
35#endif
36
38
40
41LIBCOPP_COPP_API void stackful_channel_context_base::add_caller(handle_delegate handle) noexcept {
42 if (!handle) {
43 return;
44 }
45
46#if defined(LIBCOPP_MACRO_ENABLE_STD_VARIANT) && LIBCOPP_MACRO_ENABLE_STD_VARIANT
47 if (std::holds_alternative<multi_caller_container>(callers_)) {
48 std::get<multi_caller_container>(callers_).add(handle);
49 return;
50 }
51
52 if (!std::get<handle_delegate>(callers_)) {
53 std::get<handle_delegate>(callers_) = handle;
54 return;
55 }
56
57 if (std::get<handle_delegate>(callers_) == handle) {
58 return;
59 }
60
61 // convert to multiple callers and keep the registration order
63 callers.add(std::get<handle_delegate>(callers_));
64 callers.add(handle);
65 callers_.emplace<multi_caller_container>(std::move(callers));
66#else
67 // The single-caller slot is resumed first, so only reuse it when no callers are queued.
68 if (!unique_caller_ && (!multiple_callers_ || multiple_callers_->size() == 0)) {
69 unique_caller_ = handle;
70 return;
71 }
72
73 if (unique_caller_ == handle) {
74 return;
75 }
76
77 if (!multiple_callers_) {
78 multiple_callers_.reset(new multi_caller_container());
79 }
80 multiple_callers_->add(handle);
81#endif
82}
83
84LIBCOPP_COPP_API bool stackful_channel_context_base::remove_caller(handle_delegate handle) noexcept {
85 bool has_caller = false;
86 do {
87#if defined(LIBCOPP_MACRO_ENABLE_STD_VARIANT) && LIBCOPP_MACRO_ENABLE_STD_VARIANT
88 if (std::holds_alternative<multi_caller_container>(callers_)) {
89 has_caller = std::get<multi_caller_container>(callers_).remove(handle);
90 break;
91 }
92
93 if (std::get<handle_delegate>(callers_) == handle) {
94 std::get<handle_delegate>(callers_) = nullptr;
95 has_caller = true;
96 }
97#else
98 if (unique_caller_ == handle) {
99 unique_caller_ = nullptr;
100 has_caller = true;
101 break;
102 }
103
104 if (multiple_callers_) {
105 has_caller = multiple_callers_->remove(handle);
106 }
107#endif
108 } while (false);
109
110 return has_caller;
111}
112
114 size_t resume_count = 0;
115#if defined(LIBCOPP_MACRO_ENABLE_STD_VARIANT) && LIBCOPP_MACRO_ENABLE_STD_VARIANT
116 if (std::holds_alternative<handle_delegate>(callers_)) {
117 auto caller = std::get<handle_delegate>(callers_);
118 std::get<handle_delegate>(callers_) = nullptr;
119 if (caller.handle_data && caller.resume_handle) {
120 caller.resume_handle(caller.handle_data, this);
121 ++resume_count;
122 }
123 } else if (std::holds_alternative<multi_caller_container>(callers_)) {
125 callers.swap(std::get<multi_caller_container>(callers_));
126 for (auto &caller : callers.callers) {
127 if (caller.handle_data && caller.resume_handle) {
128 caller.resume_handle(caller.handle_data, this);
129 ++resume_count;
130 }
131 }
132 }
133#else
134 auto unique_caller = unique_caller_;
135 unique_caller_ = nullptr;
136 std::unique_ptr<multi_caller_container> multiple_callers;
137 multiple_callers.swap(multiple_callers_);
138
139 // The promise object may be destroyed after first caller.resume()
140 if (unique_caller.handle_data && unique_caller.resume_handle) {
141 unique_caller.resume_handle(unique_caller.handle_data, this);
142 ++resume_count;
143 }
144
145 if (multiple_callers) {
146 for (auto &caller : multiple_callers->callers) {
147 if (caller.handle_data && caller.resume_handle) {
148 caller.resume_handle(caller.handle_data, this);
149 ++resume_count;
150 }
151 }
152 }
153#endif
154 return resume_count;
155}
156
157LIBCOPP_COPP_API bool stackful_channel_context_base::has_multiple_callers() const noexcept {
158#if defined(LIBCOPP_MACRO_ENABLE_STD_VARIANT) && LIBCOPP_MACRO_ENABLE_STD_VARIANT
159 if (std::holds_alternative<handle_delegate>(callers_)) {
160 return false;
161 } else if (std::holds_alternative<multi_caller_container>(callers_)) {
162 return std::get<multi_caller_container>(callers_).size() > 1;
163 }
164 return false;
165#else
166 size_t count = 0;
168 ++count;
169 }
170
171 if (multiple_callers_) {
172 count += multiple_callers_->size();
173 }
174 return count > 1;
175#endif
176}
177
178LIBCOPP_COPP_API void stackful_channel_context_base::wake() {}
179
180LIBCOPP_COPP_NAMESPACE_END
base type of all coroutine context
LIBCOPP_COPP_API bool remove_caller(handle_delegate handle) noexcept
LIBCOPP_COPP_API stackful_channel_context_base() noexcept
LIBCOPP_COPP_API bool has_multiple_callers() const noexcept
LIBCOPP_COPP_API void add_caller(handle_delegate handle) noexcept
std::unique_ptr< multi_caller_container > multiple_callers_
LIBCOPP_COPP_API ~stackful_channel_context_base()
LIBCOPP_COPP_API size_t resume_callers()
void swap(multi_caller_container &other) noexcept
int(* resume_handle)(void *, stackful_channel_context_base *priv_data)
static LIBCOPP_COPP_API_HEAD_ONLY int resume(void *invoke_ctx, stackful_channel_context_base *priv_data)
static LIBCOPP_UTIL_FORCEINLINE int resume(coroutine_context_base *invoke_ctx, stackful_channel_context_base *priv_data)