libxr
Embedded C++ framework for bare-metal, RTOS and Linux
Loading...
Searching...
No Matches
read_port.cpp
1#include "read_port.hpp"
2
3#include <new>
4
5using namespace LibXR;
6
7ReadPort::ReadPort(size_t buffer_size)
8 : queue_data_(buffer_size != 0U ? new (std::align_val_t(LibXR::CONCURRENCY_ALIGNMENT))
9 SPSCQueue<uint8_t>(buffer_size)
10 : nullptr)
11{
12}
13
15{
16 ASSERT_FROM_CALLBACK(queue_data_ != nullptr, in_isr);
17 return ReadQueue(*this, in_isr);
18}
19
20ReadPort::ReadQueue::~ReadQueue() { ASSERT_FROM_CALLBACK(finished_, in_isr_); }
21
22ErrorCode ReadPort::ReadQueue::PushBatch(const uint8_t* data, size_t size)
23{
24 ASSERT_FROM_CALLBACK(!finished_, in_isr_);
25 if (size != 0U)
26 {
27 ASSERT_FROM_CALLBACK(data != nullptr, in_isr_);
28 }
29
30 const ErrorCode result = port_.queue_data_->PushBatch(data, size);
31 if (result == ErrorCode::OK && size != 0U)
32 {
33 dirty_ = true;
34 }
35 return result;
36}
37
38size_t ReadPort::ReadQueue::EmptySize() const { return port_.queue_data_->EmptySize(); }
39
40size_t ReadPort::ReadQueue::Capacity() const { return port_.queue_data_->MaxSize(); }
41
43{
44 ASSERT_FROM_CALLBACK(!finished_, in_isr_);
45 finished_ = true;
46 if (dirty_)
47 {
48 port_.PublishProduced(in_isr_);
49 }
50}
51
52size_t ReadPort::EmptySize() const
53{
54 return queue_data_ == nullptr ? 0U : queue_data_->EmptySize();
55}
56
57size_t ReadPort::Size() const
58{
59 return queue_data_ == nullptr ? 0U : queue_data_->Size();
60}
61
62size_t ReadPort::Capacity() const
63{
64 return queue_data_ == nullptr ? 0U : queue_data_->MaxSize();
65}
66
67bool ReadPort::Readable() const { return queue_data_ != nullptr; }
68
69bool ReadPort::TryClaimIdle()
70{
71 uint32_t observed = state_.load(std::memory_order_acquire);
72 for (;;)
73 {
74 if (GetPhase(observed) != Phase::IDLE)
75 {
76 return false;
77 }
78
79 if (state_.compare_exchange_weak(observed, static_cast<uint32_t>(Phase::CLAIMED),
80 std::memory_order_acq_rel,
81 std::memory_order_acquire))
82 {
83 return true;
84 }
85 }
86}
87
88bool ReadPort::HasEnough(size_t available, size_t requested) const
89{
90 return requested == 0U ? available != 0U : available >= requested;
91}
92
93void ReadPort::ReleaseClaimed([[maybe_unused]] bool in_isr)
94{
95 uint32_t observed = state_.load(std::memory_order_acquire);
96 for (;;)
97 {
98 DEV_ASSERT_FROM_CALLBACK(GetPhase(observed) == Phase::CLAIMED, in_isr);
99 const uint32_t desired = WithPhase(observed, Phase::IDLE);
100 if (state_.compare_exchange_weak(observed, desired, std::memory_order_acq_rel,
101 std::memory_order_acquire))
102 {
103 return;
104 }
105 }
106}
107
108bool ReadPort::ClaimBlockCompletion()
109{
110 uint32_t observed = state_.load(std::memory_order_acquire);
111 for (;;)
112 {
113 const Phase phase = GetPhase(observed);
114 if (phase == Phase::BLOCK_CLAIMED)
115 {
116 return true;
117 }
118 if (phase != Phase::CLAIMED && phase != Phase::CLAIMED_WITH_WAITER)
119 {
120 return false;
121 }
122
123 const uint32_t desired = WithPhase(observed, Phase::BLOCK_CLAIMED);
124 if (state_.compare_exchange_weak(observed, desired, std::memory_order_acq_rel,
125 std::memory_order_acquire))
126 {
127 return true;
128 }
129 }
130}
131
132void ReadPort::ReleaseBlockCompletion([[maybe_unused]] bool in_isr)
133{
134 uint32_t observed = state_.load(std::memory_order_acquire);
135 for (;;)
136 {
137 DEV_ASSERT_FROM_CALLBACK(GetPhase(observed) == Phase::BLOCK_CLAIMED, in_isr);
138 const uint32_t desired = WithPhase(observed, Phase::IDLE);
139 if (state_.compare_exchange_weak(observed, desired, std::memory_order_acq_rel,
140 std::memory_order_acquire))
141 {
142 return;
143 }
144 }
145}
146
147void ReadPort::PublishProduced(bool in_isr)
148{
149 state_.fetch_or(EVENT_BIT, std::memory_order_acq_rel);
150 ProcessPendingReads(in_isr);
151}
152
153void ReadPort::NotifyDataAvailable(bool in_isr)
154{
155 state_.fetch_or(EVENT_BIT, std::memory_order_acq_rel);
156 ProcessPendingReads(in_isr);
157}
158
160{
161 if (!Readable())
162 {
164 }
165
166 if (!TryClaimIdle())
167 {
168 return ErrorCode::BUSY;
169 }
170
171 const bool is_block = op.type == ReadOperation::OperationType::BLOCK;
172
173 if (data.size_ != 0U)
174 {
175 ASSERT_FROM_CALLBACK(data.addr_ != nullptr, in_isr);
176 if (data.size_ > Capacity())
177 {
178 ReleaseClaimed(in_isr);
179 return ErrorCode::SIZE_ERR;
180 }
181 }
182
183 if (HasEnough(queue_data_->Size(), data.size_))
184 {
185 if (data.size_ != 0U)
186 {
187 [[maybe_unused]] const auto pop_batch_result =
188 queue_data_->PopBatch(reinterpret_cast<uint8_t*>(data.addr_), data.size_);
189 DEV_ASSERT_FROM_CALLBACK(pop_batch_result == ErrorCode::OK, in_isr);
190 }
191
192 Request completed{data, op};
193 ReleaseClaimed(in_isr);
194 if (data.size_ != 0U)
195 {
197 }
198 if (completed.op.type != ReadOperation::OperationType::BLOCK)
199 {
200 completed.op.UpdateStatus(in_isr, ErrorCode::OK);
201 }
202 return ErrorCode::OK;
203 }
204
205 info_ = Request{data, op};
206 info_.op.MarkAsRunning();
207
208 for (;;)
209 {
210 uint32_t observed = state_.load(std::memory_order_acquire);
211 DEV_ASSERT_FROM_CALLBACK(GetPhase(observed) == Phase::CLAIMED, in_isr);
212
213 if (HasEvent(observed))
214 {
215 if (!state_.compare_exchange_weak(observed, static_cast<uint32_t>(Phase::CLAIMED),
216 std::memory_order_acq_rel,
217 std::memory_order_acquire))
218 {
219 continue;
220 }
221
222 if (HasEnough(queue_data_->Size(), data.size_))
223 {
224 if (info_.op.type == ReadOperation::OperationType::BLOCK)
225 {
226 CompleteClaimedBlock(in_isr);
227 return WaitForBlock(op);
228 }
229 CompleteClaimedRead(in_isr);
230 return ErrorCode::OK;
231 }
232 continue;
233 }
234
235 uint32_t expected = observed;
236 if (state_.compare_exchange_weak(expected, static_cast<uint32_t>(Phase::PENDING),
237 std::memory_order_acq_rel,
238 std::memory_order_acquire))
239 {
240 break;
241 }
242 }
243
244 return is_block ? WaitForBlock(op) : ErrorCode::OK;
245}
246
247void ReadPort::ProcessPendingReads(bool in_isr)
248{
249 for (;;)
250 {
251 uint32_t observed = state_.load(std::memory_order_acquire);
252 if (GetPhase(observed) != Phase::PENDING)
253 {
254 return;
255 }
256
257 if (!state_.compare_exchange_weak(observed, static_cast<uint32_t>(Phase::CLAIMED),
258 std::memory_order_acq_rel,
259 std::memory_order_acquire))
260 {
261 continue;
262 }
263
264 if (!HasEnough(queue_data_->Size(), info_.data.size_))
265 {
266 for (;;)
267 {
268 uint32_t current = state_.load(std::memory_order_acquire);
269 const Phase current_phase = GetPhase(current);
270 if (current_phase == Phase::CLAIMED_WITH_WAITER)
271 {
272 Semaphore* const waiter = info_.op.data.sem_info.sem;
273 DEV_ASSERT_FROM_CALLBACK(waiter != nullptr, in_isr);
274 const uint32_t desired = WithPhase(current, Phase::IDLE);
275 if (state_.compare_exchange_weak(current, desired, std::memory_order_acq_rel,
276 std::memory_order_acquire))
277 {
278 waiter->PostFromCallback(in_isr);
279 return;
280 }
281 continue;
282 }
283
284 DEV_ASSERT_FROM_CALLBACK(current_phase == Phase::CLAIMED, in_isr);
285 const uint32_t desired = WithPhase(current, Phase::PENDING);
286 if (state_.compare_exchange_weak(current, desired, std::memory_order_acq_rel,
287 std::memory_order_acquire))
288 {
289 if (HasEvent(desired))
290 {
291 break;
292 }
293 return;
294 }
295 }
296 continue;
297 }
298
299 if (info_.op.type == ReadOperation::OperationType::BLOCK)
300 {
301 CompleteClaimedBlock(in_isr);
302 }
303 else
304 {
305 CompleteClaimedRead(in_isr);
306 }
307 return;
308 }
309}
310
311void ReadPort::CompleteClaimedRead(bool in_isr)
312{
313 Request completed = info_;
314 DEV_ASSERT_FROM_CALLBACK(completed.op.type != ReadOperation::OperationType::BLOCK,
315 in_isr);
316
317 if (completed.data.size_ != 0U)
318 {
319 [[maybe_unused]] const auto pop_batch_result = queue_data_->PopBatch(
320 reinterpret_cast<uint8_t*>(completed.data.addr_), completed.data.size_);
321 DEV_ASSERT_FROM_CALLBACK(pop_batch_result == ErrorCode::OK, in_isr);
322 }
323
324 ReleaseClaimed(in_isr);
325 if (completed.data.size_ != 0U)
326 {
328 }
329 completed.op.UpdateStatus(in_isr, ErrorCode::OK);
330}
331
332void ReadPort::CompleteClaimedBlock(bool in_isr)
333{
334 Request completed = info_;
335 DEV_ASSERT_FROM_CALLBACK(completed.op.type == ReadOperation::OperationType::BLOCK,
336 in_isr);
337 [[maybe_unused]] const auto claim_block_completion_result = ClaimBlockCompletion();
338 DEV_ASSERT_FROM_CALLBACK(claim_block_completion_result, in_isr);
339
340 if (completed.data.size_ != 0U)
341 {
342 [[maybe_unused]] const auto pop_batch_result = queue_data_->PopBatch(
343 reinterpret_cast<uint8_t*>(completed.data.addr_), completed.data.size_);
344 DEV_ASSERT_FROM_CALLBACK(pop_batch_result == ErrorCode::OK, in_isr);
346 }
347
348 block_result_ = ErrorCode::OK;
349 completed.op.data.sem_info.sem->PostFromCallback(in_isr);
350}
351
352ErrorCode ReadPort::WaitForBlock(ReadOperation& op)
353{
354 ErrorCode wait_result = op.data.sem_info.sem->Wait(op.data.sem_info.timeout);
355 if (wait_result == ErrorCode::OK)
356 {
357 DEV_ASSERT(GetPhase(state_.load(std::memory_order_acquire)) == Phase::BLOCK_CLAIMED);
358 const ErrorCode result = block_result_;
359 ReleaseBlockCompletion(false);
360 return result;
361 }
362
363 for (;;)
364 {
365 uint32_t observed = state_.load(std::memory_order_acquire);
366 const Phase phase = GetPhase(observed);
367 if (phase == Phase::PENDING)
368 {
369 const uint32_t desired = WithPhase(observed, Phase::IDLE);
370 if (state_.compare_exchange_weak(observed, desired, std::memory_order_acq_rel,
371 std::memory_order_acquire))
372 {
373 return ErrorCode::TIMEOUT;
374 }
375 continue;
376 }
377
378 if (phase == Phase::CLAIMED)
379 {
380 const uint32_t desired = WithPhase(observed, Phase::CLAIMED_WITH_WAITER);
381 if (state_.compare_exchange_weak(observed, desired, std::memory_order_acq_rel,
382 std::memory_order_acquire))
383 {
384 break;
385 }
386 continue;
387 }
388
389 DEV_ASSERT(phase == Phase::CLAIMED_WITH_WAITER || phase == Phase::BLOCK_CLAIMED);
390 break;
391 }
392
393 do
394 {
395 wait_result = op.data.sem_info.sem->Wait(UINT32_MAX);
396 } while (wait_result == ErrorCode::TIMEOUT);
397 REQUIRE(wait_result == ErrorCode::OK);
398 const Phase completed_phase = GetPhase(state_.load(std::memory_order_acquire));
399 if (completed_phase == Phase::IDLE)
400 {
401 return ErrorCode::TIMEOUT;
402 }
403
404 DEV_ASSERT(completed_phase == Phase::BLOCK_CLAIMED);
405 const ErrorCode result = block_result_;
406 ReleaseBlockCompletion(false);
407 return result;
408}
409
411{
412 if (!Readable())
413 {
415 }
416 if (!TryClaimIdle())
417 {
418 return ErrorCode::BUSY;
419 }
420
421 queue_data_->Reset();
422 ReleaseClaimed(in_isr);
424 return ErrorCode::OK;
425}
426
427void ReadPort::BindQueue(SPSCQueue<uint8_t>* queue)
428{
429 DEV_ASSERT(queue != nullptr);
430 DEV_ASSERT(queue_data_ == nullptr);
431 queue_data_ = queue;
432}
uint32_t timeout
等待超时值 / Wait timeout.
union LibXR::Operation::@6 data
完成通知对象,由 type 选择有效成员 / Notification target selected by type.
void MarkAsRunning()
标记轮询操作正在执行 / Mark a polling operation as running.
Semaphore * sem
借用的信号量 / Borrowed semaphore.
OperationType type
当前通知方式 / Current notification mode.
可写原始数据视图 / Mutable raw data view
size_t size_
数据字节数 / Data size in bytes
void * addr_
数据起始地址 / Data start address
接收队列的短期写入接口 / Short-lived producer interface for the RX queue.
void Publish()
结束本次写入并通知挂起读 / End production and notify a pending read.
Definition read_port.cpp:42
size_t EmptySize() const
获取接收队列当前空闲空间 / Get current RX queue free space.
Definition read_port.cpp:38
~ReadQueue()
开发期检查是否已 Publish / Check for Publish in development builds.
Definition read_port.cpp:20
ErrorCode PushBatch(const uint8_t *data, size_t size)
批量复制字节到接收队列 / Copy a batch of bytes into the RX queue.
Definition read_port.cpp:22
size_t Capacity() const
获取接收队列容量 / Get the RX queue capacity.
Definition read_port.cpp:40
virtual void OnReadQueueSpaceAvailable(bool)
接收队列空间可用通知 / RX queue space-available notification.
ReadPort(size_t buffer_size=128U)
构造读端口并分配接收队列 / Construct a read port and allocate its RX queue.
Definition read_port.cpp:7
size_t EmptySize() const
获取当前空闲空间 / Get current free space.
Definition read_port.cpp:52
ErrorCode ClearQueuedData(bool in_isr=false)
丢弃当前已排队的接收字节 / Discard currently queued RX bytes.
size_t Size() const
获取当前排队的数据量 / Get the current queued data size.
Definition read_port.cpp:57
bool Readable() const
检查端口是否支持读取 / Check whether the port supports reads.
Definition read_port.cpp:67
size_t Capacity() const
获取接收队列容量 / Get the RX queue capacity.
Definition read_port.cpp:62
ErrorCode operator()(RawData data, ReadOperation &op, bool in_isr=false)
提交读请求 / Submit a read request.
ReadQueue GetReadQueue(bool in_isr=false)
获取后端接收入队接口 / Obtain the backend RX producer interface.
Definition read_port.cpp:14
size_t MaxSize() const
获取队列最大容量 / Get the maximum queue capacity
size_t Size() const
获取当前已用元素数 / Get the current element count
size_t EmptySize() const
获取剩余空槽数 / Get the current free-slot count
单生产者单消费者无锁队列 / Single-producer single-consumer lock-free queue.
void Reset()
从 consumer 侧丢弃当前可见的全部 payload / Discard currently available payloads from the consumer side
ErrorCode PopBatch(Data *data, size_t size)
批量弹出多个 payload / Pop multiple payloads from the queue.
信号量类,实现线程同步机制 Semaphore class implementing thread synchronization
Definition semaphore.hpp:23
void PostFromCallback(bool in_isr)
从中断回调中释放(增加)信号量 Releases (increments) the semaphore from an ISR (Interrupt Service Routine)
Definition semaphore.cpp:96
ErrorCode Wait(uint32_t timeout=UINT32_MAX)
等待(减少)信号量 Waits (decrements) the semaphore
Definition semaphore.cpp:49
LibXR 命名空间 / LibXR namespace.
Definition ch32_can.hpp:14
ErrorCode
定义错误码枚举 / Defines an enumeration for error codes
@ TIMEOUT
超时 / Timeout
@ SIZE_ERR
尺寸错误 / Size error
@ BUSY
忙碌 / Busy
@ NOT_SUPPORT
不支持 / Not supported
@ OK
操作成功 / Operation successful
constexpr size_t CONCURRENCY_ALIGNMENT
并发结构对齐粒度(用于降低多核伪共享) / Alignment policy used by concurrency-oriented structures
Definition libxr_def.hpp:61