use crate::read::ReadHandle;
use crate::Apply;
use crate::sync::{fence, Arc, AtomicUsize, MutexGuard, Ordering};
use std::collections::VecDeque;
use std::ptr::NonNull;
#[cfg(test)]
use std::sync::atomic::AtomicBool;
use std::{fmt, thread};
pub struct WriteHandle<O, T, A>
where
O: Apply<T, A>,
{
epochs: crate::Epochs,
w_handle: NonNull<T>,
oplog: VecDeque<O>,
swap_index: usize,
r_handle: ReadHandle<T>,
last_epochs: Vec<usize>,
auxiliary: A,
#[cfg(test)]
refreshes: usize,
#[cfg(test)]
is_waiting: Arc<AtomicBool>,
}
unsafe impl<O, T, A> Send for WriteHandle<O, T, A>
where
O: Apply<T, A>,
T: Send,
O: Send,
A: Send,
ReadHandle<T>: Send,
{
}
impl<O, T, A> fmt::Debug for WriteHandle<O, T, A>
where
O: Apply<T, A> + fmt::Debug,
O: fmt::Debug,
A: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("WriteHandle")
.field("epochs", &self.epochs)
.field("w_handle", &self.w_handle)
.field("oplog", &self.oplog)
.field("swap_index", &self.swap_index)
.field("r_handle", &self.r_handle)
.field("auxiliary", &self.auxiliary)
.finish()
}
}
impl<O, T, A> Drop for WriteHandle<O, T, A>
where
O: Apply<T, A>,
{
fn drop(&mut self) {
use std::ptr;
if self.swap_index != self.oplog.len() {
self.publish();
}
let r_handle = self.r_handle.inner.swap(ptr::null_mut(), Ordering::Release);
let epochs = Arc::clone(&self.epochs);
let mut epochs = epochs.lock().unwrap();
self.wait(&mut epochs);
fence(Ordering::SeqCst);
drop(unsafe { Box::from_raw(self.w_handle.as_ptr()) });
drop(unsafe { Box::from_raw(r_handle) });
}
}
impl<O, T, A> WriteHandle<O, T, A>
where
O: Apply<T, A>,
{
pub(crate) fn new(
w_handle: T,
epochs: crate::Epochs,
r_handle: ReadHandle<T>,
auxiliary: A,
) -> Self {
Self {
epochs,
w_handle: unsafe { NonNull::new_unchecked(Box::into_raw(Box::new(w_handle))) },
oplog: VecDeque::new(),
swap_index: 0,
r_handle,
last_epochs: Vec::new(),
auxiliary,
#[cfg(test)]
is_waiting: Arc::new(AtomicBool::new(false)),
#[cfg(test)]
refreshes: 0,
}
}
fn wait(&mut self, epochs: &mut MutexGuard<'_, slab::Slab<Arc<AtomicUsize>>>) {
let mut iter = 0;
let mut starti = 0;
#[cfg(test)]
{
self.is_waiting.store(true, Ordering::Relaxed);
}
self.last_epochs.resize(epochs.capacity(), 0);
'retry: loop {
for (ii, (ri, epoch)) in epochs.iter().enumerate().skip(starti) {
if self.last_epochs[ri] % 2 == 0 {
continue;
}
let now = epoch.load(Ordering::Acquire);
if now != self.last_epochs[ri] {
} else {
starti = ii;
if !cfg!(loom) {
if iter != 20 {
iter += 1;
} else {
thread::yield_now();
}
}
#[cfg(loom)]
loom::thread::yield_now();
continue 'retry;
}
}
break;
}
#[cfg(test)]
{
self.is_waiting.store(false, Ordering::Relaxed);
}
}
pub fn publish(&mut self) -> &mut Self {
let epochs = Arc::clone(&self.epochs);
let mut epochs = epochs.lock().unwrap();
self.wait(&mut epochs);
let w_handle = unsafe { self.w_handle.as_mut() };
let r_handle = unsafe {
self.r_handle
.inner
.load(Ordering::Acquire)
.as_ref()
.unwrap()
};
if self.swap_index != 0 {
for op in self.oplog.drain(0..self.swap_index) {
O::apply_second(op, r_handle, w_handle, &mut self.auxiliary);
}
}
for op in self.oplog.iter_mut() {
O::apply_first(op, w_handle, r_handle, &mut self.auxiliary);
}
self.swap_index = self.oplog.len();
let r_handle = self
.r_handle
.inner
.swap(self.w_handle.as_ptr(), Ordering::Release);
self.w_handle = unsafe { NonNull::new_unchecked(r_handle) };
fence(Ordering::SeqCst);
for (ri, epoch) in epochs.iter() {
self.last_epochs[ri] = epoch.load(Ordering::Acquire);
}
#[cfg(test)]
{
self.refreshes += 1;
}
self
}
pub fn flush(&mut self) {
if self.has_pending_operations() {
self.publish();
}
}
pub fn has_pending_operations(&self) -> bool {
self.swap_index < self.oplog.len()
}
pub fn append(&mut self, op: O) -> &mut Self {
self.extend(std::iter::once(op));
self
}
pub fn auxiliary(&self) -> &A {
&self.auxiliary
}
pub fn auxiliary_mut(&mut self) -> &mut A {
&mut self.auxiliary
}
pub fn take(self) -> Box<T> {
use std::mem;
use std::ptr;
let mut this = mem::ManuallyDrop::new(self);
if this.swap_index != this.oplog.len() {
this.publish();
}
let r_handle = this.r_handle.inner.swap(ptr::null_mut(), Ordering::Release);
{
let epochs = Arc::clone(&this.epochs);
let mut epochs = epochs.lock().unwrap();
this.wait(&mut epochs);
}
fence(Ordering::SeqCst);
drop(unsafe { Box::from_raw(this.w_handle.as_ptr()) });
let boxed_r_handle = unsafe { Box::from_raw(r_handle) };
unsafe { ptr::drop_in_place(&mut this.epochs) };
unsafe { ptr::drop_in_place(&mut this.oplog) };
unsafe { ptr::drop_in_place(&mut this.r_handle) };
unsafe { ptr::drop_in_place(&mut this.last_epochs) };
#[cfg(test)]
unsafe {
ptr::drop_in_place(&mut this.is_waiting)
};
boxed_r_handle
}
}
use std::ops::Deref;
impl<O, T, A> Deref for WriteHandle<O, T, A>
where
O: Apply<T, A>,
{
type Target = ReadHandle<T>;
fn deref(&self) -> &Self::Target {
&self.r_handle
}
}
impl<O, T, A> Extend<O> for WriteHandle<O, T, A>
where
O: Apply<T, A>,
{
fn extend<I>(&mut self, ops: I)
where
I: IntoIterator<Item = O>,
{
self.oplog.extend(ops);
}
}
#[allow(dead_code)]
struct CheckWriteHandleSend;
#[cfg(test)]
mod tests {
use crate::sync::{AtomicUsize, Mutex, Ordering};
use crate::Apply;
use slab::Slab;
include!("./utilities.rs");
#[test]
fn append_test() {
let mut w = crate::new::<CounterAddOp, _, _>(0, ());
w.append(CounterAddOp(1));
assert_eq!(w.oplog.len(), 1);
w.publish();
w.append(CounterAddOp(2));
w.append(CounterAddOp(3));
assert_eq!(w.oplog.len(), 3);
}
#[test]
fn take_test() {
let mut w = crate::new::<CounterAddOp, _, _>(2, ());
w.append(CounterAddOp(1));
w.publish();
w.append(CounterAddOp(1));
w.publish();
assert_eq!(*w.take(), 4);
let mut w = crate::new::<CounterAddOp, _, _>(2, ());
w.append(CounterAddOp(1));
w.publish();
w.append(CounterAddOp(1));
w.publish();
w.append(CounterAddOp(2));
assert_eq!(*w.take(), 6);
let mut w = crate::new::<CounterAddOp, _, _>(2, ());
w.append(CounterAddOp(1));
w.publish();
w.append(CounterAddOp(1));
assert_eq!(*w.take(), 4);
let mut w = crate::new::<CounterAddOp, _, _>(2, ());
w.append(CounterAddOp(1));
assert_eq!(*w.take(), 3);
let mut w = crate::new::<CounterAddOp, _, _>(2, ());
w.append(CounterAddOp(1));
w.publish();
assert_eq!(*w.take(), 3);
let w = crate::new::<CounterAddOp, _, _>(2, ());
assert_eq!(*w.take(), 2);
}
#[test]
fn wait_test() {
use std::sync::{Arc, Barrier};
use std::thread;
let mut w = crate::new::<CounterAddOp, _, _>(0, ());
let test_epochs: crate::Epochs = Default::default();
let mut test_epochs = test_epochs.lock().unwrap();
w.wait(&mut test_epochs);
let held_epoch = Arc::new(AtomicUsize::new(1));
w.last_epochs = vec![2, 2, 1];
let mut epochs_slab = Slab::new();
epochs_slab.insert(Arc::new(AtomicUsize::new(2)));
epochs_slab.insert(Arc::new(AtomicUsize::new(2)));
epochs_slab.insert(Arc::clone(&held_epoch));
let barrier = Arc::new(Barrier::new(2));
let is_waiting = Arc::clone(&w.is_waiting);
let is_waiting_v = is_waiting.load(Ordering::Relaxed);
assert_eq!(false, is_waiting_v);
let barrier2 = Arc::clone(&barrier);
let test_epochs = Arc::new(Mutex::new(epochs_slab));
let wait_handle = thread::spawn(move || {
barrier2.wait();
let mut test_epochs = test_epochs.lock().unwrap();
w.wait(&mut test_epochs);
});
barrier.wait();
while !is_waiting.load(Ordering::Relaxed) {
thread::yield_now();
}
held_epoch.fetch_add(1, Ordering::SeqCst);
let _ = wait_handle.join();
}
#[test]
fn flush_noblock() {
let mut w = crate::new::<CounterAddOp, _, _>(0, ());
let r = w.clone();
w.append(CounterAddOp(42));
w.publish();
assert_eq!(*r.enter().unwrap(), 42);
let _count = r.enter();
assert_eq!(w.oplog.iter().skip(w.swap_index).count(), 0);
assert!(!w.has_pending_operations());
}
#[test]
fn flush_no_refresh() {
let mut w = crate::new::<CounterAddOp, _, _>(0, ());
assert!(!w.has_pending_operations());
w.publish();
assert!(!w.has_pending_operations());
assert_eq!(w.refreshes, 1);
w.append(CounterAddOp(42));
assert!(w.has_pending_operations());
w.publish();
assert!(!w.has_pending_operations());
assert_eq!(w.refreshes, 2);
w.append(CounterAddOp(42));
assert!(w.has_pending_operations());
w.publish();
assert!(!w.has_pending_operations());
assert_eq!(w.refreshes, 3);
assert!(!w.has_pending_operations());
w.publish();
assert_eq!(w.refreshes, 4);
}
}