libcopp 2.3.1
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
stackful_channel.cpp
Go to the documentation of this file.
1// Copyright 2025 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_set>(callers_)) {
48 std::get<multi_caller_set>(callers_).insert(handle);
49 return;
50 }
51
52 if (!std::get<handle_delegate>(callers_)) {
53 std::get<handle_delegate>(callers_) = handle;
54 return;
55 }
56
57 // convert to multiple caller
58 multi_caller_set callers;
59 callers.insert(std::get<handle_delegate>(callers_));
60 callers.insert(handle);
61 callers_.emplace<multi_caller_set>(std::move(callers));
62#else
63 if (!unique_caller_) {
64 unique_caller_ = handle;
65 return;
66 }
67
68 if (!multiple_callers_) {
69 multiple_callers_.reset(new multi_caller_set());
70 }
71 multiple_callers_->insert(handle);
72#endif
73}
74
75LIBCOPP_COPP_API bool stackful_channel_context_base::remove_caller(handle_delegate handle) 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_set>(callers_)) {
80 has_caller = std::get<multi_caller_set>(callers_).erase(handle) > 0;
81 break;
82 }
83
84 if (std::get<handle_delegate>(callers_) == handle) {
85 std::get<handle_delegate>(callers_) = nullptr;
86 has_caller = true;
87 }
88#else
89 if (unique_caller_ == handle) {
90 unique_caller_ = nullptr;
91 has_caller = true;
92 break;
93 }
94
95 if (multiple_callers_) {
96 has_caller = multiple_callers_->erase(handle) > 0;
97 }
98#endif
99 } while (false);
100
101 return has_caller;
102}
103
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_data && caller.resume_handle) {
111 caller.resume_handle(caller.handle_data, this);
112 ++resume_count;
113 }
114 } else if (std::holds_alternative<multi_caller_set>(callers_)) {
115 multi_caller_set callers;
116 callers.swap(std::get<multi_caller_set>(callers_));
117 for (auto &caller : callers) {
118 if (caller.handle_data && caller.resume_handle) {
119 caller.resume_handle(caller.handle_data, this);
120 ++resume_count;
121 }
122 }
123 }
124#else
125 auto unique_caller = unique_caller_;
126 unique_caller_ = nullptr;
127 std::unique_ptr<multi_caller_set> multiple_callers;
128 multiple_callers.swap(multiple_callers_);
129
130 // The promise object may be destroyed after first caller.resume()
131 if (unique_caller.handle_data && unique_caller.resume_handle) {
132 unique_caller.resume_handle(unique_caller.handle_data, this);
133 ++resume_count;
134 }
135
136 if (multiple_callers) {
137 for (auto &caller : *multiple_callers) {
138 if (caller.handle_data && caller.resume_handle) {
139 caller.resume_handle(caller.handle_data, this);
140 ++resume_count;
141 }
142 }
143 }
144#endif
145 return resume_count;
146}
147
148LIBCOPP_COPP_API bool stackful_channel_context_base::has_multiple_callers() const noexcept {
149#if defined(LIBCOPP_MACRO_ENABLE_STD_VARIANT) && LIBCOPP_MACRO_ENABLE_STD_VARIANT
150 if (std::holds_alternative<handle_delegate>(callers_)) {
151 return false;
152 } else if (std::holds_alternative<multi_caller_set>(callers_)) {
153 return std::get<multi_caller_set>(callers_).size() > 1;
154 }
155 return false;
156#else
157 size_t count = 0;
159 ++count;
160 }
161
162 if (multiple_callers_) {
163 count += multiple_callers_->size();
164 }
165 return count > 1;
166#endif
167}
168
169LIBCOPP_COPP_API void stackful_channel_context_base::wake() {}
170
171LIBCOPP_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
std::unordered_set< handle_delegate, handle_delegate_hash > multi_caller_set
LIBCOPP_COPP_API void add_caller(handle_delegate handle) noexcept
std::unique_ptr< multi_caller_set > multiple_callers_
LIBCOPP_COPP_API ~stackful_channel_context_base()
LIBCOPP_COPP_API size_t resume_callers()
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)