libxr
Embedded C++ framework for bare-metal, RTOS and Linux
Loading...
Searching...
No Matches
slot_pool.cpp
1#include "slot_pool.hpp"
2
3#include <algorithm>
4#include <limits>
5
6namespace LibXR
7{
8SlotPoolBase::SlotPoolBase(size_t slot_size, size_t slot_align, size_t slot_count)
9 : slot_size_(slot_size),
10 slot_count_(slot_count),
11 slot_stride_(AlignUp(slot_size_, slot_align)),
12 alloc_align_(std::max(alignof(std::max_align_t), slot_align)),
13 states_(nullptr),
14 seqs_(nullptr),
15 slots_(nullptr),
16 publish_seq_(0),
17 release_count_(0)
18{
19 ASSERT(slot_size_ > 0);
20 ASSERT(slot_count_ > 0);
21 ASSERT(slot_align > 0);
22 ASSERT(slot_count_ <= std::numeric_limits<size_t>::max() / slot_stride_);
23 ASSERT(slot_stride_ <= std::numeric_limits<size_t>::max() / slot_align);
24
25 states_ = new (std::align_val_t(LibXR::CONCURRENCY_ALIGNMENT))
26 std::atomic<uint32_t>[slot_count_];
27 seqs_ = new (std::align_val_t(LibXR::CONCURRENCY_ALIGNMENT))
28 std::atomic<uint32_t>[slot_count_];
29
30 slots_ = static_cast<std::byte*>(
31 ::operator new[](slot_stride_ * slot_count_, std::align_val_t(alloc_align_)));
32
33 for (size_t index = 0; index < slot_count_; ++index)
34 {
35 states_[index].store(SLOT_FREE, std::memory_order_relaxed);
36 seqs_[index].store(0, std::memory_order_relaxed);
37 }
38}
39
41{
42 ::operator delete[](slots_, std::align_val_t(alloc_align_));
43 // 与构造时显式对齐的 new[] 配对 / Pairs with the explicitly aligned new[] of
44 // the constructor.
45 ::operator delete[](states_, std::align_val_t(LibXR::CONCURRENCY_ALIGNMENT));
46 ::operator delete[](seqs_, std::align_val_t(LibXR::CONCURRENCY_ALIGNMENT));
47}
48
50{
51 // 释放计数作为重扫的变化检测:扫描期间有槽被释放时,FULL 才允许重扫一次。
52 // The release counter detects changes across the scan: a FULL result may only
53 // trigger one rescan when some slot was released meanwhile.
54 uint32_t release_begin = release_count_.load(std::memory_order_acquire);
55 bool rescanned = false;
56
57 for (;;)
58 {
59 for (size_t i = 0; i < slot_count_; ++i)
60 {
61 if (states_[i].load(std::memory_order_acquire) != SLOT_FREE)
62 {
63 continue;
64 }
65
66 uint32_t expected = SLOT_FREE;
67 if (states_[i].compare_exchange_strong(expected, SLOT_WRITING,
68 std::memory_order_acq_rel,
69 std::memory_order_acquire))
70 {
71 index = i;
72 return ErrorCode::OK;
73 }
74 // 该槽刚被其他上下文认领:继续扫剩余槽位。
75 // The slot was just claimed by another context: keep scanning the rest.
76 }
77
78 const uint32_t release_end = release_count_.load(std::memory_order_acquire);
79 if (release_end == release_begin || rescanned)
80 {
81 return ErrorCode::FULL;
82 }
83 release_begin = release_end;
84 rescanned = true;
85 }
86}
87
88void SlotPoolBase::Publish(size_t index)
89{
90 ASSERT(index < slot_count_);
91
92 // 先取序号再置就绪:重扫的发布计数变化先于就绪状态可见。
93 // Take the sequence first, then publish: the counter change that drives the
94 // rescan becomes visible before the ready state itself.
95 const uint32_t seq = publish_seq_.fetch_add(1, std::memory_order_release);
96 seqs_[index].store(seq, std::memory_order_relaxed);
97
98 uint32_t expected = SLOT_WRITING;
99 const bool ok = states_[index].compare_exchange_strong(
100 expected, SLOT_READY, std::memory_order_release, std::memory_order_acquire);
101 ASSERT(ok);
102}
103
105{
106 bool rescanned = false;
107
108 for (;;)
109 {
110 // 本轮开始时的发布计数:本轮开始前就绪的条目,序号都早于它。
111 // Publish count at the start of this pass: every item that was ready before
112 // the pass began has a sequence before it.
113 const uint32_t pass_begin = publish_seq_.load(std::memory_order_acquire);
114 size_t best = INVALID_INDEX;
115 uint32_t best_seq = 0;
116
117 for (size_t i = 0; i < slot_count_; ++i)
118 {
119 if (states_[i].load(std::memory_order_acquire) != SLOT_READY)
120 {
121 continue;
122 }
123
124 const uint32_t seq = seqs_[i].load(std::memory_order_relaxed);
125 if (best == INVALID_INDEX || SeqBefore(seq, best_seq))
126 {
127 best = i;
128 best_seq = seq;
129 }
130 }
131
132 if (best != INVALID_INDEX)
133 {
134 // 选中的条目在本轮开始后才取得序号时,同一发布者更早的条目可能在本轮
135 // 扫过它的槽之后才就绪而被漏掉:重新扫描,直到选中的条目早于本轮开始。
136 // 单核上中断里的扫描不会被低优先级上下文的发布插入,不会走到这里。
137 // When the chosen item took its sequence after this pass began, an earlier
138 // item of the same publisher may have become ready only after the pass had
139 // scanned its slot: rescan until the chosen item predates the pass. On a
140 // single core, a scan inside an interrupt cannot be interleaved with
141 // publications from lower-priority contexts, so it never gets here.
142 if (!SeqBefore(best_seq, pass_begin))
143 {
144 continue;
145 }
146
147 uint32_t expected = SLOT_READY;
148 if (states_[best].compare_exchange_strong(expected, SLOT_READING,
149 std::memory_order_acq_rel,
150 std::memory_order_acquire))
151 {
152 // CAS 成功后复读序号:扫描到 CAS 之间该槽若被取走、释放并重新发布,
153 // CAS 赢到的是新条目。撤销本次认领(读取中 → 就绪),把新条目留在
154 // 池里,重新扫描,保证选中的仍是最早发布项。
155 // Re-read the sequence after a winning CAS: if the slot was taken,
156 // released and re-published between the scan and the CAS, the CAS won
157 // the NEW item. Undo this claim (reading -> ready) to leave the new
158 // item in the pool, then rescan so the oldest item still wins.
159 if (seqs_[best].load(std::memory_order_relaxed) == best_seq)
160 {
161 index = best;
162 return ErrorCode::OK;
163 }
164 uint32_t reading = SLOT_READING;
165 const bool undone = states_[best].compare_exchange_strong(
166 reading, SLOT_READY, std::memory_order_release, std::memory_order_acquire);
167 ASSERT(undone);
168 continue;
169 }
170 // 该槽刚被其他消费者取走:重新扫描。
171 // Another consumer just claimed the slot: rescan.
172 continue;
173 }
174
175 const uint32_t publish_end = publish_seq_.load(std::memory_order_acquire);
176 if (publish_end == pass_begin || rescanned)
177 {
178 return ErrorCode::EMPTY;
179 }
180 rescanned = true;
181 }
182}
183
184void SlotPoolBase::Release(size_t index)
185{
186 ASSERT(index < slot_count_);
187
188 // 释放计数先于状态置回空闲:已经扫过该槽的上下文能通过计数变化发现它。
189 // Bump the release counter before the state returns to free: a context that
190 // already scanned past this slot can detect it through the counter change.
191 release_count_.fetch_add(1, std::memory_order_release);
192
193 uint32_t expected = states_[index].load(std::memory_order_relaxed);
194 // 只有持有者(写入中或读取中)才能释放 / Only the holder (writing or reading)
195 // may release.
196 ASSERT(expected == SLOT_WRITING || expected == SLOT_READING);
197 const bool ok = states_[index].compare_exchange_strong(
198 expected, SLOT_FREE, std::memory_order_release, std::memory_order_acquire);
199 ASSERT(ok);
200}
201
202void* SlotPoolBase::SlotPtr(size_t index)
203{
204 ASSERT(index < slot_count_);
205 return slots_ + index * slot_stride_;
206}
207
208const void* SlotPoolBase::SlotPtr(size_t index) const
209{
210 ASSERT(index < slot_count_);
211 return slots_ + index * slot_stride_;
212}
213
214size_t SlotPoolBase::AlignUp(size_t value, size_t align)
215{
216 ASSERT(value <= std::numeric_limits<size_t>::max() - (align - 1));
217 return ((value + align - 1) / align) * align;
218}
219} // namespace LibXR
~SlotPoolBase()
析构槽池内核 / Destroy the slot-pool core.
Definition slot_pool.cpp:40
ErrorCode Take(size_t &index)
认领序号最小的就绪槽(就绪 → 读取中) / Claim the oldest ready slot (ready -> reading).
void * SlotPtr(size_t index)
获取指定槽位的存储地址 / Get the storage address of one slot.
const size_t alloc_align_
std::byte * slots_
槽存储缓冲区 / Byte buffer storing slots.
const size_t slot_count_
槽总数 / Total number of slots.
std::atomic< uint32_t > publish_seq_
std::atomic< uint32_t > * states_
每槽状态字 / Per-slot atomic state word.
std::atomic< uint32_t > release_count_
const size_t slot_stride_
相邻槽位之间的步长 / Byte stride between slots.
const size_t slot_size_
单个槽的字节数 / Byte size of one slot.
std::atomic< uint32_t > * seqs_
每槽发布序号 / Per-slot publish sequence.
static constexpr size_t INVALID_INDEX
无效槽位下标 / Sentinel for "no slot chosen".
ErrorCode Acquire(size_t &index)
认领一个空闲槽(空闲 → 写入中) / Claim one free slot (free -> writing).
Definition slot_pool.cpp:49
SlotPoolBase(size_t slot_size, size_t slot_align, size_t slot_count)
构造槽池内核 / Construct the slot-pool core.
Definition slot_pool.cpp:8
@ SLOT_READING
读取中,已被消费者持有 / Reading, owned by a consumer
@ SLOT_READY
就绪,等待 Take / Ready, waiting for Take
@ SLOT_FREE
空闲,可被 Acquire 认领 / Free, claimable by Acquire
@ SLOT_WRITING
写入中,已被生产者持有 / Writing, owned by a producer
void Release(size_t index)
释放自己持有的槽(写入中或读取中 → 空闲) / Release an owned slot (writing or reading -> free).
void Publish(size_t index)
发布已写好的槽(写入中 → 就绪),并取得发布序号 / Publish a fully written slot (writing -> ready) and take a publish sequen...
Definition slot_pool.cpp:88
LibXR 命名空间 / LibXR namespace.
Definition ch32_can.hpp:14
ErrorCode
定义错误码枚举 / Defines an enumeration for error codes
@ EMPTY
为空 / Empty
@ FULL
已满 / Full
@ OK
操作成功 / Operation successful
constexpr size_t CONCURRENCY_ALIGNMENT
并发结构对齐粒度(用于降低多核伪共享) / Alignment policy used by concurrency-oriented structures
Definition libxr_def.hpp:61
constexpr auto max(LeftType a, RightType b) -> std::common_type_t< LeftType, RightType >
计算两个数的最大值 / Computes the maximum of two numbers
定长槽池:任务与中断共享的小容量消息槽 / Fixed-size slot pool for small task/interrupt shared message slots.