Skip to content

Commit 77a89b6

Browse files
committed
Fix #12 and factor out shared state creation
1 parent 0506c16 commit 77a89b6

File tree

1 file changed

+37
-70
lines changed

1 file changed

+37
-70
lines changed

src/lib.rs

Lines changed: 37 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -125,11 +125,7 @@ impl<T: Send> TripleBuffer<T> {
125125
/// Construct a triple buffer, using a functor to generate initial values
126126
fn new_impl(mut generator: impl FnMut() -> T) -> Self {
127127
// Start with the shared state...
128-
let mut new_buffer = || CachePadded::new(UnsafeCell::new(generator()));
129-
let shared_state = Arc::new(SharedState {
130-
buffers: [new_buffer(), new_buffer(), new_buffer()],
131-
back_info: AtomicBackBufferInfo::new(0),
132-
});
128+
let shared_state = Arc::new(SharedState::new(|_i| generator(), 0));
133129

134130
// ...then construct the input and output structs
135131
TripleBuffer {
@@ -452,24 +448,32 @@ struct SharedState<T: Send> {
452448
buffers: [CachePadded<UnsafeCell<T>>; 3],
453449

454450
/// Information about the current back-buffer state
455-
back_info: AtomicBackBufferInfo,
451+
back_info: CachePadded<AtomicBackBufferInfo>,
452+
}
453+
//
454+
#[doc(hidden)]
455+
impl<T: Send> SharedState<T> {
456+
/// Given (a way to generate) buffer contents and the back info, build the shared state
457+
fn new(mut gen_buf_data: impl FnMut(usize) -> T, back_info: BackBufferInfo) -> Self {
458+
let mut make_buf = |i| -> CachePadded<UnsafeCell<T>> {
459+
CachePadded::new(UnsafeCell::new(gen_buf_data(i)))
460+
};
461+
Self {
462+
buffers: [make_buf(0), make_buf(1), make_buf(2)],
463+
back_info: CachePadded::new(AtomicBackBufferInfo::new(back_info)),
464+
}
465+
}
456466
}
457467
//
458468
#[doc(hidden)]
459469
impl<T: Clone + Send> SharedState<T> {
460470
/// Cloning the shared state is unsafe because you must ensure that no one
461471
/// is concurrently accessing it, since &self is enough for writing.
462472
unsafe fn clone(&self) -> Self {
463-
// The use of UnsafeCell makes buffers somewhat cumbersome to clone...
464-
let clone_buffer = |i: usize| -> CachePadded<UnsafeCell<T>> {
465-
CachePadded::new(UnsafeCell::new((*self.buffers[i].get()).clone()))
466-
};
467-
468-
// ...so better define some shortcuts before getting started:
469-
SharedState {
470-
buffers: [clone_buffer(0), clone_buffer(1), clone_buffer(2)],
471-
back_info: AtomicBackBufferInfo::new(self.back_info.load(Ordering::Relaxed)),
472-
}
473+
Self::new(
474+
|i| (*self.buffers[i].get()).clone(),
475+
self.back_info.load(Ordering::Relaxed),
476+
)
473477
}
474478
}
475479
//
@@ -504,6 +508,7 @@ unsafe impl<T: Send> Sync for SharedState<T> {}
504508
/// the back-buffer, and reset to 0 when the consumer fetches the update.
505509
///
506510
type BufferIndex = u8;
511+
type BackBufferInfo = BufferIndex;
507512
//
508513
type AtomicBackBufferInfo = AtomicU8;
509514
const BACK_INDEX_MASK: u8 = 0b11; // Mask used to extract back-buffer index
@@ -512,16 +517,9 @@ const BACK_DIRTY_BIT: u8 = 0b100; // Bit set by producer to signal updates
512517
/// Unit tests
513518
#[cfg(test)]
514519
mod tests {
515-
use super::{
516-
AtomicBackBufferInfo, BufferIndex, SharedState, TripleBuffer, BACK_DIRTY_BIT,
517-
BACK_INDEX_MASK,
518-
};
519-
520-
use crossbeam_utils::CachePadded;
520+
use super::{BufferIndex, SharedState, TripleBuffer, BACK_DIRTY_BIT, BACK_INDEX_MASK};
521521

522-
use std::{
523-
cell::UnsafeCell, fmt::Debug, ops::Deref, sync::atomic::Ordering, thread, time::Duration,
524-
};
522+
use std::{fmt::Debug, ops::Deref, sync::atomic::Ordering, thread, time::Duration};
525523

526524
use testbench::{
527525
self,
@@ -541,47 +539,24 @@ mod tests {
541539
#[test]
542540
fn partial_eq_shared() {
543541
// Let's create some dummy shared state
544-
let dummy_state = SharedState::<u16> {
545-
buffers: [new_buffer(111), new_buffer(222), new_buffer(333)],
546-
back_info: AtomicBackBufferInfo::new(0b10),
547-
};
542+
let dummy_state = SharedState::<u16>::new(|i| [111, 222, 333][i], 0b10);
548543

549544
// Check that the dummy state is equal to itself
550545
assert!(unsafe { dummy_state.eq(&dummy_state) });
551546

552547
// Check that it's not equal to a state where buffer contents differ
553-
assert!(unsafe {
554-
!dummy_state.eq(&SharedState::<u16> {
555-
buffers: [new_buffer(114), new_buffer(222), new_buffer(333)],
556-
back_info: AtomicBackBufferInfo::new(0b10),
557-
})
558-
});
559-
assert!(unsafe {
560-
!dummy_state.eq(&SharedState::<u16> {
561-
buffers: [new_buffer(111), new_buffer(225), new_buffer(333)],
562-
back_info: AtomicBackBufferInfo::new(0b10),
563-
})
564-
});
565-
assert!(unsafe {
566-
!dummy_state.eq(&SharedState::<u16> {
567-
buffers: [new_buffer(111), new_buffer(222), new_buffer(336)],
568-
back_info: AtomicBackBufferInfo::new(0b10),
569-
})
570-
});
548+
assert!(unsafe { !dummy_state.eq(&SharedState::<u16>::new(|i| [114, 222, 333][i], 0b10)) });
549+
assert!(unsafe { !dummy_state.eq(&SharedState::<u16>::new(|i| [111, 225, 333][i], 0b10)) });
550+
assert!(unsafe { !dummy_state.eq(&SharedState::<u16>::new(|i| [111, 222, 336][i], 0b10)) });
571551

572552
// Check that it's not equal to a state where the back info differs
573553
assert!(unsafe {
574-
!dummy_state.eq(&SharedState::<u16> {
575-
buffers: [new_buffer(111), new_buffer(222), new_buffer(333)],
576-
back_info: AtomicBackBufferInfo::new(BACK_DIRTY_BIT & 0b10),
577-
})
578-
});
579-
assert!(unsafe {
580-
!dummy_state.eq(&SharedState::<u16> {
581-
buffers: [new_buffer(111), new_buffer(222), new_buffer(333)],
582-
back_info: AtomicBackBufferInfo::new(0b01),
583-
})
554+
!dummy_state.eq(&SharedState::<u16>::new(
555+
|i| [111, 222, 333][i],
556+
BACK_DIRTY_BIT & 0b10,
557+
))
584558
});
559+
assert!(unsafe { !dummy_state.eq(&SharedState::<u16>::new(|i| [111, 222, 333][i], 0b01)) });
585560
}
586561

587562
/// Check that TripleBuffer's PartialEq impl works
@@ -619,20 +594,17 @@ mod tests {
619594
#[test]
620595
fn clone_shared() {
621596
// Let's create some dummy shared state
622-
let dummy_state = SharedState::<u8> {
623-
buffers: [new_buffer(123), new_buffer(231), new_buffer(132)],
624-
back_info: AtomicBackBufferInfo::new(BACK_DIRTY_BIT & 0b01),
625-
};
597+
let dummy_state = SharedState::<u8>::new(|i| [123, 231, 132][i], BACK_DIRTY_BIT & 0b01);
626598

627599
// Now, try to clone it
628600
let dummy_state_copy = unsafe { dummy_state.clone() };
629601

630602
// Check that the contents of the original state did not change
631603
assert!(unsafe {
632-
dummy_state.eq(&SharedState::<u8> {
633-
buffers: [new_buffer(123), new_buffer(231), new_buffer(132)],
634-
back_info: AtomicBackBufferInfo::new(BACK_DIRTY_BIT & 0b01),
635-
})
604+
dummy_state.eq(&SharedState::<u8>::new(
605+
|i| [123, 231, 132][i],
606+
BACK_DIRTY_BIT & 0b01,
607+
))
636608
});
637609

638610
// Check that the contents of the original and final state are identical
@@ -946,11 +918,6 @@ mod tests {
946918
);
947919
}
948920

949-
/// Build data storage for a buffer
950-
fn new_buffer<T>(contents: T) -> CachePadded<UnsafeCell<T>> {
951-
CachePadded::new(UnsafeCell::new(contents))
952-
}
953-
954921
/// Range check for triple buffer indexes
955922
#[allow(unused_comparisons)]
956923
fn index_in_range(idx: BufferIndex) -> bool {

0 commit comments

Comments
 (0)