Corium 1.1.0
High-Performance Zero-Heap C++20 MPSC Application Runtime
Loading...
Searching...
No Matches
Channel.hpp
Go to the documentation of this file.
1
7#pragma once
8
9#include <array>
10#include <atomic>
11#include <coroutine>
12#include <cstddef>
13#include <optional>
14#include <utility>
15
16namespace corium::async {
17
22template <typename T, size_t Capacity = 16>
23class Channel {
24 static_assert(Capacity > 0, "Channel capacity must be greater than zero.");
25
26public:
27 using ValueType = T;
28
29 constexpr Channel() noexcept = default;
30
32 close();
33 }
34
35 Channel(const Channel&) = delete;
36 Channel& operator=(const Channel&) = delete;
37
41 bool tryPush(T value) noexcept {
42 if (m_closed.load(std::memory_order_acquire)) {
43 return false;
44 }
45
46 size_t count = m_count.load(std::memory_order_relaxed);
47 if (count >= Capacity) {
48 return false;
49 }
50
51 size_t tail = m_tail.load(std::memory_order_relaxed);
52 m_buffer[tail] = std::move(value);
53 m_tail.store((tail + 1) % Capacity, std::memory_order_relaxed);
54 m_count.fetch_add(1, std::memory_order_release);
55
56 // Resume any waiting consumer
57 auto h = m_recvWaiter.exchange(nullptr, std::memory_order_acq_rel);
58 if (h && !h.done()) {
59 h.resume();
60 }
61 return true;
62 }
63
67 bool tryPop(T& out) noexcept {
68 size_t count = m_count.load(std::memory_order_relaxed);
69 if (count == 0) {
70 return false;
71 }
72
73 size_t head = m_head.load(std::memory_order_relaxed);
74 out = std::move(m_buffer[head]);
75 m_head.store((head + 1) % Capacity, std::memory_order_relaxed);
76 m_count.fetch_sub(1, std::memory_order_release);
77
78 // Resume any waiting producer
79 auto h = m_sendWaiter.exchange(nullptr, std::memory_order_acq_rel);
80 if (h && !h.done()) {
81 h.resume();
82 }
83 return true;
84 }
85
87 void close() noexcept {
88 m_closed.store(true, std::memory_order_release);
89 auto hr = m_recvWaiter.exchange(nullptr, std::memory_order_acq_rel);
90 if (hr && !hr.done()) {
91 hr.resume();
92 }
93 auto hs = m_sendWaiter.exchange(nullptr, std::memory_order_acq_rel);
94 if (hs && !hs.done()) {
95 hs.resume();
96 }
97 }
98
100 [[nodiscard]] bool isClosed() const noexcept {
101 return m_closed.load(std::memory_order_acquire);
102 }
103
105 [[nodiscard]] size_t size() const noexcept {
106 return m_count.load(std::memory_order_relaxed);
107 }
108
110 [[nodiscard]] bool empty() const noexcept {
111 return size() == 0;
112 }
113
115 [[nodiscard]] bool full() const noexcept {
116 return size() >= Capacity;
117 }
118
120 struct PushAwaiter {
123 bool done{false};
124
125 [[nodiscard]] bool await_ready() noexcept {
126 if (chan.isClosed()) {
127 done = true;
128 return true;
129 }
130 if (chan.tryPush(std::move(val))) {
131 done = true;
132 return true;
133 }
134 return false;
135 }
136
137 bool await_suspend(std::coroutine_handle<> h) noexcept {
138 chan.m_pendingPushVal = std::move(val);
139 chan.m_sendWaiter.store(h, std::memory_order_release);
140 return true;
141 }
142
143 bool await_resume() noexcept {
144 if (done) return !chan.isClosed();
145 if (chan.m_pendingPushVal.has_value()) {
146 bool pushed = chan.tryPush(std::move(*chan.m_pendingPushVal));
147 chan.m_pendingPushVal.reset();
148 return pushed;
149 }
150 return false;
151 }
152 };
153
157 [[nodiscard]] PushAwaiter push(T val) noexcept {
158 return PushAwaiter{*this, std::move(val), false};
159 }
160
162 struct PopAwaiter {
164 std::optional<T> result{};
165
166 [[nodiscard]] bool await_ready() noexcept {
167 T item{};
168 if (chan.tryPop(item)) {
169 result = std::move(item);
170 return true;
171 }
172 if (chan.isClosed()) {
173 result = std::nullopt;
174 return true;
175 }
176 return false;
177 }
178
179 bool await_suspend(std::coroutine_handle<> h) noexcept {
180 T item{};
181 if (chan.tryPop(item)) {
182 result = std::move(item);
183 return false;
184 }
185 if (chan.isClosed()) {
186 result = std::nullopt;
187 return false;
188 }
189 chan.m_recvWaiter.store(h, std::memory_order_release);
190 return true;
191 }
192
193 std::optional<T> await_resume() noexcept {
194 if (result.has_value()) {
195 return result;
196 }
197 T item{};
198 if (chan.tryPop(item)) {
199 return item;
200 }
201 return std::nullopt;
202 }
203 };
204
207 [[nodiscard]] PopAwaiter pop() noexcept {
208 return PopAwaiter{*this};
209 }
210
211private:
212 std::array<T, Capacity> m_buffer{};
213 std::atomic<size_t> m_head{0};
214 std::atomic<size_t> m_tail{0};
215 std::atomic<size_t> m_count{0};
216 std::atomic<bool> m_closed{false};
217
218 std::atomic<std::coroutine_handle<>> m_recvWaiter{nullptr};
219 std::atomic<std::coroutine_handle<>> m_sendWaiter{nullptr};
220 std::optional<T> m_pendingPushVal{};
221};
222
223} // namespace corium::async
Statically allocated bounded asynchronous channel for typed producer-consumer coroutines.
Definition Channel.hpp:23
Channel & operator=(const Channel &)=delete
bool full() const noexcept
Check if channel is full.
Definition Channel.hpp:115
void close() noexcept
Close the channel. No more pushes will succeed. Remaining elements can still be popped.
Definition Channel.hpp:87
PushAwaiter push(T val) noexcept
Push an item into the channel asynchronously with backpressure suspension.
Definition Channel.hpp:157
constexpr Channel() noexcept=default
size_t size() const noexcept
Number of elements currently in the channel.
Definition Channel.hpp:105
bool isClosed() const noexcept
Check if channel is closed.
Definition Channel.hpp:100
bool tryPop(T &out) noexcept
Non-blocking attempt to pop an item from the channel.
Definition Channel.hpp:67
T ValueType
Definition Channel.hpp:27
Channel(const Channel &)=delete
bool empty() const noexcept
Check if channel is empty.
Definition Channel.hpp:110
PopAwaiter pop() noexcept
Pop an item from the channel asynchronously.
Definition Channel.hpp:207
bool tryPush(T value) noexcept
Non-blocking attempt to push an item into the channel.
Definition Channel.hpp:41
Definition AsyncEvent.hpp:14
Awaiter for popping an item asynchronously (suspends if channel is empty).
Definition Channel.hpp:162
Channel & chan
Definition Channel.hpp:163
bool await_suspend(std::coroutine_handle<> h) noexcept
Definition Channel.hpp:179
bool await_ready() noexcept
Definition Channel.hpp:166
std::optional< T > await_resume() noexcept
Definition Channel.hpp:193
std::optional< T > result
Definition Channel.hpp:164
Awaiter for pushing an item asynchronously (suspends if channel is full).
Definition Channel.hpp:120
bool await_ready() noexcept
Definition Channel.hpp:125
T val
Definition Channel.hpp:122
Channel & chan
Definition Channel.hpp:121
bool await_resume() noexcept
Definition Channel.hpp:143
bool await_suspend(std::coroutine_handle<> h) noexcept
Definition Channel.hpp:137
bool done
Definition Channel.hpp:123