aboutsummaryrefslogtreecommitdiff
path: root/embassy-hal-common/src/atomic_ring_buffer.rs
diff options
context:
space:
mode:
authorDario Nieuwenhuis <[email protected]>2023-03-02 00:57:54 +0100
committerDario Nieuwenhuis <[email protected]>2023-03-02 01:01:19 +0100
commitf95aafc90e365bbdd243c0ed5069ff23abfa8be4 (patch)
tree03e268b48cc9aec840783345ed7a86d9f58bbece /embassy-hal-common/src/atomic_ring_buffer.rs
parentc4f4aa10f9af2fafe4b3c01a0b0358883cf96b14 (diff)
common: allow atomic ringbuf to fill up to N instead of just N-1.
This allows the ringbuf to be filled up to `N` instead of just `N-1`, using some fun tricks on the indices. The advantage is better performance: Before, the first write would fill N-1 bytes, The second would write just the 1 byte left before wrapping, then N-2. Then 2, then N-3, and so on. This would result in more smaller chunks, so worse perf. This problem is gone now.
Diffstat (limited to 'embassy-hal-common/src/atomic_ring_buffer.rs')
-rw-r--r--embassy-hal-common/src/atomic_ring_buffer.rs98
1 files changed, 69 insertions, 29 deletions
diff --git a/embassy-hal-common/src/atomic_ring_buffer.rs b/embassy-hal-common/src/atomic_ring_buffer.rs
index 4c944d763..ccbc37362 100644
--- a/embassy-hal-common/src/atomic_ring_buffer.rs
+++ b/embassy-hal-common/src/atomic_ring_buffer.rs
@@ -14,10 +14,18 @@ use core::sync::atomic::{AtomicPtr, AtomicUsize, Ordering};
14/// One concurrent writer and one concurrent reader are supported, even at 14/// One concurrent writer and one concurrent reader are supported, even at
15/// different execution priorities (like main and irq). 15/// different execution priorities (like main and irq).
16pub struct RingBuffer { 16pub struct RingBuffer {
17 buf: AtomicPtr<u8>, 17 pub buf: AtomicPtr<u8>,
18 len: AtomicUsize, 18 pub len: AtomicUsize,
19 start: AtomicUsize, 19
20 end: AtomicUsize, 20 // start and end wrap at len*2, not at len.
21 // This allows distinguishing "full" and "empty".
22 // full is when start+len == end (modulo len*2)
23 // empty is when start == end
24 //
25 // This avoids having to consider the ringbuffer "full" at len-1 instead of len.
26 // The usual solution is adding a "full" flag, but that can't be made atomic
27 pub start: AtomicUsize,
28 pub end: AtomicUsize,
21} 29}
22 30
23pub struct Reader<'a>(&'a RingBuffer); 31pub struct Reader<'a>(&'a RingBuffer);
@@ -90,7 +98,7 @@ impl RingBuffer {
90 let start = self.start.load(Ordering::Relaxed); 98 let start = self.start.load(Ordering::Relaxed);
91 let end = self.end.load(Ordering::Relaxed); 99 let end = self.end.load(Ordering::Relaxed);
92 100
93 len == 0 || self.wrap(end + 1) == start 101 self.wrap(start + len) == end
94 } 102 }
95 103
96 pub fn is_empty(&self) -> bool { 104 pub fn is_empty(&self) -> bool {
@@ -100,15 +108,13 @@ impl RingBuffer {
100 start == end 108 start == end
101 } 109 }
102 110
103 fn wrap(&self, n: usize) -> usize { 111 fn wrap(&self, mut n: usize) -> usize {
104 let len = self.len.load(Ordering::Relaxed); 112 let len = self.len.load(Ordering::Relaxed);
105 113
106 assert!(n <= len); 114 if n >= len * 2 {
107 if n == len { 115 n -= len * 2
108 0
109 } else {
110 n
111 } 116 }
117 n
112 } 118 }
113} 119}
114 120
@@ -161,16 +167,25 @@ impl<'a> Writer<'a> {
161 pub fn push_buf(&mut self) -> (*mut u8, usize) { 167 pub fn push_buf(&mut self) -> (*mut u8, usize) {
162 // Ordering: popping writes `start` last, so we read `start` first. 168 // Ordering: popping writes `start` last, so we read `start` first.
163 // Read it with Acquire ordering, so that the next accesses can't be reordered up past it. 169 // Read it with Acquire ordering, so that the next accesses can't be reordered up past it.
164 let start = self.0.start.load(Ordering::Acquire); 170 let mut start = self.0.start.load(Ordering::Acquire);
165 let buf = self.0.buf.load(Ordering::Relaxed); 171 let buf = self.0.buf.load(Ordering::Relaxed);
166 let len = self.0.len.load(Ordering::Relaxed); 172 let len = self.0.len.load(Ordering::Relaxed);
167 let end = self.0.end.load(Ordering::Relaxed); 173 let mut end = self.0.end.load(Ordering::Relaxed);
174
175 let empty = start == end;
176
177 if start >= len {
178 start -= len
179 }
180 if end >= len {
181 end -= len
182 }
168 183
169 let n = if start <= end { 184 if start == end && !empty {
170 len - end - (start == 0 && len != 0) as usize 185 // full
171 } else { 186 return (buf, 0);
172 start - end - 1 187 }
173 }; 188 let n = if start > end { start - end } else { len - end };
174 189
175 trace!(" ringbuf: push_buf {:?}..{:?}", end, end + n); 190 trace!(" ringbuf: push_buf {:?}..{:?}", end, end + n);
176 (unsafe { buf.add(end) }, n) 191 (unsafe { buf.add(end) }, n)
@@ -239,12 +254,23 @@ impl<'a> Reader<'a> {
239 // Ordering: pushing writes `end` last, so we read `end` first. 254 // Ordering: pushing writes `end` last, so we read `end` first.
240 // Read it with Acquire ordering, so that the next accesses can't be reordered up past it. 255 // Read it with Acquire ordering, so that the next accesses can't be reordered up past it.
241 // This is needed to guarantee we "see" the data written by the writer. 256 // This is needed to guarantee we "see" the data written by the writer.
242 let end = self.0.end.load(Ordering::Acquire); 257 let mut end = self.0.end.load(Ordering::Acquire);
243 let buf = self.0.buf.load(Ordering::Relaxed); 258 let buf = self.0.buf.load(Ordering::Relaxed);
244 let len = self.0.len.load(Ordering::Relaxed); 259 let len = self.0.len.load(Ordering::Relaxed);
245 let start = self.0.start.load(Ordering::Relaxed); 260 let mut start = self.0.start.load(Ordering::Relaxed);
246 261
247 let n = if end < start { len - start } else { end - start }; 262 if start == end {
263 return (buf, 0);
264 }
265
266 if start >= len {
267 start -= len
268 }
269 if end >= len {
270 end -= len
271 }
272
273 let n = if end > start { end - start } else { len - start };
248 274
249 trace!(" ringbuf: pop_buf {:?}..{:?}", start, start + n); 275 trace!(" ringbuf: pop_buf {:?}..{:?}", start, start + n);
250 (unsafe { buf.add(start) }, n) 276 (unsafe { buf.add(start) }, n)
@@ -280,12 +306,12 @@ mod tests {
280 assert_eq!(rb.is_full(), false); 306 assert_eq!(rb.is_full(), false);
281 307
282 rb.writer().push(|buf| { 308 rb.writer().push(|buf| {
283 // If capacity is 4, we can fill it up to 3. 309 assert_eq!(4, buf.len());
284 assert_eq!(3, buf.len());
285 buf[0] = 1; 310 buf[0] = 1;
286 buf[1] = 2; 311 buf[1] = 2;
287 buf[2] = 3; 312 buf[2] = 3;
288 3 313 buf[3] = 4;
314 4
289 }); 315 });
290 316
291 assert_eq!(rb.is_empty(), false); 317 assert_eq!(rb.is_empty(), false);
@@ -301,7 +327,7 @@ mod tests {
301 assert_eq!(rb.is_full(), true); 327 assert_eq!(rb.is_full(), true);
302 328
303 rb.reader().pop(|buf| { 329 rb.reader().pop(|buf| {
304 assert_eq!(3, buf.len()); 330 assert_eq!(4, buf.len());
305 assert_eq!(1, buf[0]); 331 assert_eq!(1, buf[0]);
306 1 332 1
307 }); 333 });
@@ -310,7 +336,7 @@ mod tests {
310 assert_eq!(rb.is_full(), false); 336 assert_eq!(rb.is_full(), false);
311 337
312 rb.reader().pop(|buf| { 338 rb.reader().pop(|buf| {
313 assert_eq!(2, buf.len()); 339 assert_eq!(3, buf.len());
314 0 340 0
315 }); 341 });
316 342
@@ -318,11 +344,16 @@ mod tests {
318 assert_eq!(rb.is_full(), false); 344 assert_eq!(rb.is_full(), false);
319 345
320 rb.reader().pop(|buf| { 346 rb.reader().pop(|buf| {
321 assert_eq!(2, buf.len()); 347 assert_eq!(3, buf.len());
322 assert_eq!(2, buf[0]); 348 assert_eq!(2, buf[0]);
323 assert_eq!(3, buf[1]); 349 assert_eq!(3, buf[1]);
324 2 350 2
325 }); 351 });
352 rb.reader().pop(|buf| {
353 assert_eq!(1, buf.len());
354 assert_eq!(4, buf[0]);
355 1
356 });
326 357
327 assert_eq!(rb.is_empty(), true); 358 assert_eq!(rb.is_empty(), true);
328 assert_eq!(rb.is_full(), false); 359 assert_eq!(rb.is_full(), false);
@@ -333,19 +364,28 @@ mod tests {
333 }); 364 });
334 365
335 rb.writer().push(|buf| { 366 rb.writer().push(|buf| {
336 assert_eq!(1, buf.len()); 367 assert_eq!(4, buf.len());
337 buf[0] = 10; 368 buf[0] = 10;
338 1 369 1
339 }); 370 });
340 371
341 rb.writer().push(|buf| { 372 rb.writer().push(|buf| {
342 assert_eq!(2, buf.len()); 373 assert_eq!(3, buf.len());
343 buf[0] = 11; 374 buf[0] = 11;
344 buf[1] = 12; 375 buf[1] = 12;
345 2 376 2
346 }); 377 });
347 378
348 assert_eq!(rb.is_empty(), false); 379 assert_eq!(rb.is_empty(), false);
380 assert_eq!(rb.is_full(), false);
381
382 rb.writer().push(|buf| {
383 assert_eq!(1, buf.len());
384 buf[0] = 13;
385 1
386 });
387
388 assert_eq!(rb.is_empty(), false);
349 assert_eq!(rb.is_full(), true); 389 assert_eq!(rb.is_full(), true);
350 } 390 }
351 } 391 }