@@ -131,9 +131,11 @@ impl<T> RingBuffer<T> {
131
131
let p = Producer {
132
132
buffer : buffer. clone ( ) ,
133
133
cached_head : Cell :: new ( 0 ) ,
134
+ cached_tail : Cell :: new ( 0 ) ,
134
135
} ;
135
136
let c = Consumer {
136
137
buffer,
138
+ cached_head : Cell :: new ( 0 ) ,
137
139
cached_tail : Cell :: new ( 0 ) ,
138
140
} ;
139
141
( p, c)
@@ -283,6 +285,11 @@ pub struct Producer<T> {
283
285
///
284
286
/// This value can be stale and sometimes needs to be resynchronized with `buffer.head`.
285
287
cached_head : Cell < usize > ,
288
+
289
+ /// A copy of `buffer.tail` for quick access.
290
+ ///
291
+ /// This value is always in sync with `buffer.tail`.
292
+ cached_tail : Cell < usize > ,
286
293
}
287
294
288
295
// SAFETY: After moving a Producer to another thread, there is still only a single thread
@@ -315,6 +322,7 @@ impl<T> Producer<T> {
315
322
unsafe { self . buffer . slot_ptr ( tail) . write ( value) } ;
316
323
let tail = self . buffer . increment1 ( tail) ;
317
324
self . buffer . tail . store ( tail, Ordering :: Release ) ;
325
+ self . cached_tail . set ( tail) ;
318
326
Ok ( ( ) )
319
327
} else {
320
328
Err ( PushError :: Full ( value) )
@@ -342,9 +350,7 @@ impl<T> Producer<T> {
342
350
pub fn slots ( & self ) -> usize {
343
351
let head = self . buffer . head . load ( Ordering :: Acquire ) ;
344
352
self . cached_head . set ( head) ;
345
- // "tail" is only ever written by the producer thread, "Relaxed" is enough
346
- let tail = self . buffer . tail . load ( Ordering :: Relaxed ) ;
347
- self . buffer . capacity - self . buffer . distance ( head, tail)
353
+ self . buffer . capacity - self . buffer . distance ( head, self . cached_tail . get ( ) )
348
354
}
349
355
350
356
/// Returns `true` if there are currently no slots available for writing.
@@ -445,8 +451,7 @@ impl<T> Producer<T> {
445
451
/// This is a strict subset of the functionality implemented in `write_chunk_uninit()`.
446
452
/// For performance, this special case is immplemented separately.
447
453
fn next_tail ( & self ) -> Option < usize > {
448
- // "tail" is only ever written by the producer thread, "Relaxed" is enough
449
- let tail = self . buffer . tail . load ( Ordering :: Relaxed ) ;
454
+ let tail = self . cached_tail . get ( ) ;
450
455
451
456
// Check if the queue is *possibly* full.
452
457
if self . buffer . distance ( self . cached_head . get ( ) , tail) == self . buffer . capacity {
@@ -488,6 +493,11 @@ pub struct Consumer<T> {
488
493
/// A reference to the ring buffer.
489
494
buffer : Arc < RingBuffer < T > > ,
490
495
496
+ /// A copy of `buffer.head` for quick access.
497
+ ///
498
+ /// This value is always in sync with `buffer.head`.
499
+ cached_head : Cell < usize > ,
500
+
491
501
/// A copy of `buffer.tail` for quick access.
492
502
///
493
503
/// This value can be stale and sometimes needs to be resynchronized with `buffer.tail`.
@@ -534,6 +544,7 @@ impl<T> Consumer<T> {
534
544
let value = unsafe { self . buffer . slot_ptr ( head) . read ( ) } ;
535
545
let head = self . buffer . increment1 ( head) ;
536
546
self . buffer . head . store ( head, Ordering :: Release ) ;
547
+ self . cached_head . set ( head) ;
537
548
Ok ( value)
538
549
} else {
539
550
Err ( PopError :: Empty )
@@ -588,9 +599,7 @@ impl<T> Consumer<T> {
588
599
pub fn slots ( & self ) -> usize {
589
600
let tail = self . buffer . tail . load ( Ordering :: Acquire ) ;
590
601
self . cached_tail . set ( tail) ;
591
- // "head" is only ever written by the consumer thread, "Relaxed" is enough
592
- let head = self . buffer . head . load ( Ordering :: Relaxed ) ;
593
- self . buffer . distance ( head, tail)
602
+ self . buffer . distance ( self . cached_head . get ( ) , tail)
594
603
}
595
604
596
605
/// Returns `true` if there are currently no slots available for reading.
@@ -690,8 +699,7 @@ impl<T> Consumer<T> {
690
699
/// This is a strict subset of the functionality implemented in `read_chunk()`.
691
700
/// For performance, this special case is immplemented separately.
692
701
fn next_head ( & self ) -> Option < usize > {
693
- // "head" is only ever written by the consumer thread, "Relaxed" is enough
694
- let head = self . buffer . head . load ( Ordering :: Relaxed ) ;
702
+ let head = self . cached_head . get ( ) ;
695
703
696
704
// Check if the queue is *possibly* empty.
697
705
if head == self . cached_tail . get ( ) {
0 commit comments