libxr
Embedded C++ framework for bare-metal, RTOS and Linux
Loading...
Searching...
No Matches
write_port.cpp
1#include "write_port.hpp"
2
3#include <cstring>
4#include <new>
5
6using namespace LibXR;
7
8WritePort::WritePort(size_t queue_size, size_t buffer_size)
9 : queue_requests_(queue_size != 0U
10 ? new (std::align_val_t(LibXR::CONCURRENCY_ALIGNMENT))
11 SPSCQueue<Request>(ValidateQueueSize(queue_size))
12 : nullptr),
13 queue_data_(buffer_size != 0U ? new (std::align_val_t(LibXR::CONCURRENCY_ALIGNMENT))
14 SPSCQueue<uint8_t>(buffer_size)
15 : nullptr)
16{
17}
18
20{
21 return queue_data_ == nullptr ? 0U : queue_data_->EmptySize();
22}
23
24size_t WritePort::Size() const
25{
26 return queue_data_ == nullptr ? 0U : queue_data_->Size();
27}
28
29size_t WritePort::Capacity() const
30{
31 return queue_data_ == nullptr ? 0U : queue_data_->MaxSize();
32}
33
35{
36 return queue_data_ != nullptr && write_fun_ != nullptr;
37}
38
40{
41 write_fun_ = fun;
42 return *this;
43}
44
45bool WritePort::TryClaimProducer()
46{
47 uint32_t observed = state_.load(std::memory_order_acquire);
48 for (;;)
49 {
50 if (GetPhase(observed) != Phase::IDLE)
51 {
52 return false;
53 }
54
55 const uint32_t desired = WithPhase(observed, Phase::LOCKED);
56 if (state_.compare_exchange_weak(observed, desired, std::memory_order_acq_rel,
57 std::memory_order_acquire))
58 {
59 return true;
60 }
61 }
62}
63
64void WritePort::ReleaseProducer(Phase next, [[maybe_unused]] bool in_isr)
65{
66 uint32_t observed = state_.load(std::memory_order_acquire);
67 for (;;)
68 {
69 DEV_ASSERT_FROM_CALLBACK(GetPhase(observed) == Phase::LOCKED, in_isr);
70 const uint32_t desired = WithPhase(observed, next);
71 if (state_.compare_exchange_weak(observed, desired, std::memory_order_acq_rel,
72 std::memory_order_acquire))
73 {
74 return;
75 }
76 }
77}
78
79void WritePort::PublishQueuedRequest(Phase next, [[maybe_unused]] bool in_isr)
80{
81 uint32_t observed = state_.load(std::memory_order_acquire);
82 for (;;)
83 {
84 DEV_ASSERT_FROM_CALLBACK(GetPhase(observed) == Phase::LOCKED, in_isr);
85 const uint32_t released = GetReleasedCount(observed);
86 DEV_ASSERT_FROM_CALLBACK(released < MAX_RELEASED_REQUESTS, in_isr);
87 const uint32_t desired = MakeState(next, released + 1U);
88 if (state_.compare_exchange_weak(observed, desired, std::memory_order_acq_rel,
89 std::memory_order_acquire))
90 {
91 return;
92 }
93 }
94}
95
96void WritePort::DecrementReleasedRequest([[maybe_unused]] bool in_isr)
97{
98 uint32_t observed = state_.load(std::memory_order_acquire);
99 for (;;)
100 {
101 DEV_ASSERT_FROM_CALLBACK(GetReleasedCount(observed) != 0U, in_isr);
102 const uint32_t desired = observed - RELEASED_INCREMENT;
103 if (state_.compare_exchange_weak(observed, desired, std::memory_order_acq_rel,
104 std::memory_order_acquire))
105 {
106 return;
107 }
108 }
109}
110
111void WritePort::NotifyBackend(bool in_isr)
112{
113 if (write_fun_ != nullptr)
114 {
115 write_fun_(*this, in_isr);
116 }
117}
118
120{
121 ASSERT_FROM_CALLBACK(queue_requests_ != nullptr, in_isr);
122
123 if (GetReleasedCount(state_.load(std::memory_order_acquire)) == 0U)
124 {
125 DEV_ASSERT_FROM_CALLBACK(front_remaining_ == 0U, in_isr);
126 return WriteQueue(*this, in_isr, 0U);
127 }
128
129 Request request{};
130 [[maybe_unused]] const auto peek_result = queue_requests_->Peek(request);
131 DEV_ASSERT_FROM_CALLBACK(peek_result == ErrorCode::OK, in_isr);
132 if (front_remaining_ == 0U)
133 {
134 DEV_ASSERT_FROM_CALLBACK(request.size != 0U, in_isr);
135 front_remaining_ = request.size;
136 }
137 else
138 {
139 DEV_ASSERT_FROM_CALLBACK(front_remaining_ <= request.size, in_isr);
140 }
141
142 return WriteQueue(*this, in_isr, front_remaining_);
143}
144
145void WritePort::WriteQueue::PopAll(uint8_t* destination)
146{
147 BeginAction();
148 const size_t remaining = AvailableSize();
149 DEV_ASSERT_FROM_CALLBACK(remaining != 0U, in_isr_);
150 DEV_ASSERT_FROM_CALLBACK(destination != nullptr, in_isr_);
151
152 size_t offset = 0U;
153 const size_t accepted = port_.queue_data_->ConsumeWithReader(
154 remaining,
155 [destination, &offset](const uint8_t* first, size_t first_size,
156 const uint8_t* second, size_t second_size) -> size_t
157 {
158 if (first_size != 0U)
159 {
160 std::memcpy(destination + offset, first, first_size);
161 offset += first_size;
162 }
163 if (second_size != 0U)
164 {
165 std::memcpy(destination + offset, second, second_size);
166 offset += second_size;
167 }
168 return first_size + second_size;
169 });
170 DEV_ASSERT_FROM_CALLBACK(accepted == remaining, in_isr_);
171 popped_size_ += accepted;
172}
173
175{
176 BeginAction();
177 ASSERT_FROM_CALLBACK(reason != ErrorCode::OK, in_isr_);
178 DEV_ASSERT_FROM_CALLBACK(front_size_ != 0U, in_isr_);
179
180 const size_t remaining = AvailableSize();
181 DEV_ASSERT_FROM_CALLBACK(remaining != 0U, in_isr_);
182 const size_t accepted = port_.queue_data_->ConsumeWithReader(
183 remaining,
184 [](const uint8_t* first, size_t first_size, const uint8_t* second,
185 size_t second_size) -> size_t
186 {
187 UNUSED(first);
188 UNUSED(second);
189 return first_size + second_size;
190 });
191 DEV_ASSERT_FROM_CALLBACK(accepted == remaining, in_isr_);
192 popped_size_ += accepted;
193 settlement_result_ = reason;
194}
195
197{
198 port_.SettleWriteQueue(popped_size_, settlement_result_, in_isr_);
199}
200
201void WritePort::SettleWriteQueue(size_t accepted, ErrorCode result, bool in_isr) noexcept
202{
203 if (accepted == 0U)
204 {
205 return;
206 }
207
208 DEV_ASSERT_FROM_CALLBACK(queue_requests_ != nullptr, in_isr);
209 DEV_ASSERT_FROM_CALLBACK(accepted <= front_remaining_, in_isr);
210 front_remaining_ -= accepted;
211 if (front_remaining_ != 0U)
212 {
213 return;
214 }
215
216 Request completed{};
217 [[maybe_unused]] const auto pop_result = queue_requests_->Pop(completed);
218 DEV_ASSERT_FROM_CALLBACK(pop_result == ErrorCode::OK, in_isr);
219 DecrementReleasedRequest(in_isr);
220 CompleteRequest(completed, result, in_isr);
221}
222
223void WritePort::CompleteRequest(Request& request, ErrorCode result, bool in_isr)
224{
225 if (request.op.type != WriteOperation::OperationType::BLOCK)
226 {
227 request.op.UpdateStatus(in_isr, result);
228 return;
229 }
230
231 for (;;)
232 {
233 uint32_t observed = state_.load(std::memory_order_acquire);
234 const Phase phase = GetPhase(observed);
235 if (phase == Phase::BLOCK_WAITING)
236 {
237 const uint32_t desired = WithPhase(observed, Phase::BLOCK_CLAIMED);
238 if (!state_.compare_exchange_weak(observed, desired, std::memory_order_acq_rel,
239 std::memory_order_acquire))
240 {
241 continue;
242 }
243
244 block_result_ = result;
245 request.op.data.sem_info.sem->PostFromCallback(in_isr);
246 return;
247 }
248
249 if (phase == Phase::BLOCK_DETACHED)
250 {
251 const uint32_t desired = WithPhase(observed, Phase::IDLE);
252 if (!state_.compare_exchange_weak(observed, desired, std::memory_order_acq_rel,
253 std::memory_order_acquire))
254 {
255 continue;
256 }
257 return;
258 }
259
260 if (phase == Phase::BLOCK_RETIRE_WAITING)
261 {
262 const uint32_t desired = WithPhase(observed, Phase::LOCKED);
263 if (!state_.compare_exchange_weak(observed, desired, std::memory_order_acq_rel,
264 std::memory_order_acquire))
265 {
266 continue;
267 }
268
269 Semaphore* waiter = admission_waiter_;
270 admission_waiter_ = nullptr;
271 DEV_ASSERT_FROM_CALLBACK(waiter != nullptr, in_isr);
272 waiter->PostFromCallback(in_isr);
273 return;
274 }
275
276 DEV_ASSERT_FROM_CALLBACK(false, in_isr);
277 return;
278 }
279}
280
281ErrorCode WritePort::CommitQueued(size_t size, WriteOperation& op, bool in_isr)
282{
283 DEV_ASSERT_FROM_CALLBACK(queue_requests_ != nullptr, in_isr);
284 DEV_ASSERT_FROM_CALLBACK(queue_data_ != nullptr, in_isr);
285 DEV_ASSERT_FROM_CALLBACK(size != 0U, in_isr);
286
287 Request request{size, op};
288 [[maybe_unused]] const auto push_result = queue_requests_->Push(request);
289 DEV_ASSERT_FROM_CALLBACK(push_result == ErrorCode::OK, in_isr);
290 request.op.MarkAsRunning();
291
292 PublishQueuedRequest(op.type == WriteOperation::OperationType::BLOCK
293 ? Phase::BLOCK_WAITING
294 : Phase::IDLE,
295 in_isr);
296 NotifyBackend(in_isr);
297
298 return op.type == WriteOperation::OperationType::BLOCK ? WaitForBlock(op)
299 : ErrorCode::OK;
300}
301
302ErrorCode WritePort::CommitAdmission([[maybe_unused]] size_t size, WriteOperation& op,
303 bool in_isr)
304{
305 DEV_ASSERT_FROM_CALLBACK(IsAdmissionMode(), in_isr);
306 DEV_ASSERT_FROM_CALLBACK(size != 0U, in_isr);
307
308 WriteOperation completed = op;
309 completed.MarkAsRunning();
310 NotifyBackend(in_isr);
311 ReleaseProducer(Phase::IDLE, in_isr);
312
313 if (completed.type != WriteOperation::OperationType::BLOCK)
314 {
315 completed.UpdateStatus(in_isr, ErrorCode::OK);
316 }
317 return ErrorCode::OK;
318}
319
320bool WritePort::TryRegisterRetirementWait(WriteOperation& op)
321{
322 DEV_ASSERT(op.type == WriteOperation::OperationType::BLOCK);
323 admission_waiter_ = op.data.sem_info.sem;
324 uint32_t observed = state_.load(std::memory_order_acquire);
325 for (;;)
326 {
327 if (GetPhase(observed) != Phase::BLOCK_DETACHED)
328 {
329 admission_waiter_ = nullptr;
330 return false;
331 }
332
333 const uint32_t desired = WithPhase(observed, Phase::BLOCK_RETIRE_WAITING);
334 if (state_.compare_exchange_weak(observed, desired, std::memory_order_acq_rel,
335 std::memory_order_acquire))
336 {
337 return true;
338 }
339 }
340}
341
342ErrorCode WritePort::WaitForRetirement(WriteOperation& op)
343{
344 if (!TryRegisterRetirementWait(op))
345 {
346 if (LoadPhase() == Phase::IDLE && TryClaimProducer())
347 {
348 return ErrorCode::OK;
349 }
350 return ErrorCode::BUSY;
351 }
352
353 ErrorCode wait_result = op.data.sem_info.sem->Wait(op.data.sem_info.timeout);
354 if (wait_result == ErrorCode::OK)
355 {
356 DEV_ASSERT(LoadPhase() == Phase::LOCKED);
357 return ErrorCode::OK;
358 }
359
360 for (;;)
361 {
362 uint32_t observed = state_.load(std::memory_order_acquire);
363 const Phase phase = GetPhase(observed);
364 if (phase == Phase::BLOCK_RETIRE_WAITING)
365 {
366 const uint32_t desired = WithPhase(observed, Phase::BLOCK_DETACHED);
367 if (state_.compare_exchange_weak(observed, desired, std::memory_order_acq_rel,
368 std::memory_order_acquire))
369 {
370 admission_waiter_ = nullptr;
371 return ErrorCode::TIMEOUT;
372 }
373 continue;
374 }
375
376 DEV_ASSERT(phase == Phase::LOCKED);
377 break;
378 }
379
380 do
381 {
382 wait_result = op.data.sem_info.sem->Wait(UINT32_MAX);
383 } while (wait_result == ErrorCode::TIMEOUT);
384 REQUIRE(wait_result == ErrorCode::OK);
385 ReleaseProducer(Phase::IDLE, false);
386 return ErrorCode::TIMEOUT;
387}
388
389ErrorCode WritePort::WaitForBlock(WriteOperation& op)
390{
391 ErrorCode wait_result = op.data.sem_info.sem->Wait(op.data.sem_info.timeout);
392 if (wait_result == ErrorCode::OK)
393 {
394 DEV_ASSERT(LoadPhase() == Phase::BLOCK_CLAIMED);
395 const ErrorCode result = block_result_;
396 uint32_t observed = state_.load(std::memory_order_acquire);
397 for (;;)
398 {
399 DEV_ASSERT(GetPhase(observed) == Phase::BLOCK_CLAIMED);
400 const uint32_t desired = WithPhase(observed, Phase::IDLE);
401 if (state_.compare_exchange_weak(observed, desired, std::memory_order_acq_rel,
402 std::memory_order_acquire))
403 {
404 break;
405 }
406 }
407 return result;
408 }
409
410 for (;;)
411 {
412 uint32_t observed = state_.load(std::memory_order_acquire);
413 const Phase phase = GetPhase(observed);
414 if (phase == Phase::BLOCK_WAITING)
415 {
416 const uint32_t desired = WithPhase(observed, Phase::BLOCK_DETACHED);
417 if (state_.compare_exchange_weak(observed, desired, std::memory_order_acq_rel,
418 std::memory_order_acquire))
419 {
420 return ErrorCode::TIMEOUT;
421 }
422 continue;
423 }
424
425 DEV_ASSERT(phase == Phase::BLOCK_CLAIMED);
426 break;
427 }
428
429 do
430 {
431 wait_result = op.data.sem_info.sem->Wait(UINT32_MAX);
432 } while (wait_result == ErrorCode::TIMEOUT);
433 REQUIRE(wait_result == ErrorCode::OK);
434 const ErrorCode result = block_result_;
435 uint32_t observed = state_.load(std::memory_order_acquire);
436 for (;;)
437 {
438 DEV_ASSERT(GetPhase(observed) == Phase::BLOCK_CLAIMED);
439 const uint32_t desired = WithPhase(observed, Phase::IDLE);
440 if (state_.compare_exchange_weak(observed, desired, std::memory_order_acq_rel,
441 std::memory_order_acquire))
442 {
443 break;
444 }
445 }
446 return result;
447}
448
450{
451 if (!Writable())
452 {
454 }
455
456 if (data.size_ == 0U)
457 {
458 if (op.type != WriteOperation::OperationType::BLOCK)
459 {
460 op.UpdateStatus(in_isr, ErrorCode::OK);
461 }
462 return ErrorCode::OK;
463 }
464
465 if (!TryClaimProducer())
466 {
467 if (op.type == WriteOperation::OperationType::BLOCK &&
468 LoadPhase() == Phase::BLOCK_DETACHED && data.size_ <= Capacity())
469 {
470 const ErrorCode retirement = WaitForRetirement(op);
471 if (retirement != ErrorCode::OK)
472 {
473 return retirement;
474 }
475 }
476 else
477 {
478 return ErrorCode::BUSY;
479 }
480 }
481
482 ASSERT_FROM_CALLBACK(data.addr_ != nullptr, in_isr);
483
484 if (queue_data_->EmptySize() < data.size_ ||
485 (!IsAdmissionMode() && queue_requests_->EmptySize() < 1U))
486 {
487 ReleaseProducer(Phase::IDLE, in_isr);
488 return ErrorCode::FULL;
489 }
490
491 [[maybe_unused]] const auto push_batch_result =
492 queue_data_->PushBatch(reinterpret_cast<const uint8_t*>(data.addr_), data.size_);
493 DEV_ASSERT_FROM_CALLBACK(push_batch_result == ErrorCode::OK, in_isr);
494
495 return IsAdmissionMode() ? CommitAdmission(data.size_, op, in_isr)
496 : CommitQueued(data.size_, op, in_isr);
497}
只读原始数据视图 / Immutable raw data view
size_t size_
数据字节数 / Data size in bytes
const void * addr_
数据起始地址 / Data start address
uint32_t timeout
等待超时值 / Wait timeout.
void UpdateStatus(bool in_isr, Status &&status)
按通知方式报告完成结果 / Report completion using the selected mode.
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.
ErrorCode Push(const Data &item)
推入一个强类型元素 / Push one typed element.
ErrorCode Pop(Data &item)
弹出一个强类型元素 / Pop one typed element.
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.
ErrorCode Peek(Data &item)
查看一个队头 payload 但不出队 / Peek one front payload without dequeuing it.
size_t ConsumeWithReader(size_t limit, Reader &&reader)
通过一次双 span 回调消费 payload 前缀 / Consume a payload prefix through one two-span reader callback.
ErrorCode PushBatch(const Data *data, size_t size)
批量推入多个 payload / Push multiple payloads into 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
已提交队头的出队接口 / Consumer interface for one released front.
void FailFront(ErrorCode reason)
丢弃剩余数据并结束队头 / Discard the remainder and fail the front.
void PopAll(uint8_t *destination)
复制并取走队头剩余数据 / Copy and consume the entire front remainder.
~WriteQueue() noexcept
结算出队,使用获取时的上下文 / Settle progress using the captured context.
size_t AvailableSize() const
获取当前队头的剩余长度 / Get the current front remainder.
基于 SPSC 字节队列的写端口 / Write endpoint backed by an SPSC byte queue.
WriteQueue GetWriteQueue(bool in_isr=false)
获取已提交队头的出队接口 / Obtain a consumer interface for the released front.
bool Writable() const
检查端口是否支持写入 / Check whether the port supports writes.
size_t Size() const
获取字节队列当前数据量 / Get current byte queue occupancy.
WritePort & operator=(WriteFun fun)
绑定写入进度通知函数 / Bind the write progress notification function.
size_t Capacity() const
获取字节队列总容量 / Get total byte queue capacity.
WritePort(size_t queue_size=3U, size_t buffer_size=128U)
构造写端口并分配队列 / Construct a write port and allocate its queues.
Definition write_port.cpp:8
size_t EmptySize() const
获取字节队列当前空闲空间 / Get current byte queue free space.
ErrorCode operator()(ConstRawData data, WriteOperation &op, bool in_isr=false)
提交写请求 / Submit a write request.
LibXR 命名空间 / LibXR namespace.
Definition ch32_can.hpp:14
ErrorCode
定义错误码枚举 / Defines an enumeration for error codes
@ TIMEOUT
超时 / Timeout
@ BUSY
忙碌 / Busy
@ NOT_SUPPORT
不支持 / Not supported
@ FULL
已满 / Full
@ OK
操作成功 / Operation successful
constexpr size_t CONCURRENCY_ALIGNMENT
并发结构对齐粒度(用于降低多核伪共享) / Alignment policy used by concurrency-oriented structures
Definition libxr_def.hpp:61
void(* WriteFun)(WritePort &port, bool in_isr)
写入进度通知函数类型 / Write progress notification function type.