use std::sync::Arc;
use crossbeam_epoch::{Collector, Guard, LocalHandle};
pub struct EpochManager {
collector: Collector,
}
impl EpochManager {
pub fn new() -> Self {
Self {
collector: Collector::new(),
}
}
pub fn register(&self) -> EpochHandle {
EpochHandle {
handle: self.collector.register(),
}
}
pub fn pin(&self) -> Guard {
self.collector.register().pin()
}
pub fn flush(&self) {
let guard = self.pin();
drop(guard);
}
}
impl Default for EpochManager {
fn default() -> Self {
Self::new()
}
}
pub struct EpochHandle {
handle: LocalHandle,
}
impl EpochHandle {
pub fn pin(&mut self) -> Guard {
self.handle.pin()
}
pub fn protect(&mut self) -> Guard {
self.pin()
}
pub fn defer<F>(&mut self, f: F)
where
F: FnOnce() + Send + 'static,
{
let guard = self.pin();
guard.defer(f);
}
pub unsafe fn defer_destroy<T>(&mut self, ptr: *mut T)
where
T: Send + 'static,
{
let ptr_addr = ptr as usize;
self.defer(move || {
let ptr = ptr_addr as *mut T;
if !ptr.is_null() {
unsafe {
drop(Box::from_raw(ptr));
}
}
});
}
pub fn flush(&mut self) {
let _guard = self.pin();
}
}
pub struct EpochPtr<T> {
ptr: *mut T,
}
impl<T> EpochPtr<T> {
pub fn new(ptr: *mut T) -> Self {
Self { ptr }
}
pub fn null() -> Self {
Self {
ptr: std::ptr::null_mut(),
}
}
pub fn is_null(&self) -> bool {
self.ptr.is_null()
}
pub unsafe fn as_ptr(&self) -> *mut T {
self.ptr
}
pub unsafe fn as_ref(&self) -> Option<&T> {
if self.ptr.is_null() {
None
} else {
unsafe { Some(&*self.ptr) }
}
}
pub unsafe fn as_mut(&mut self) -> Option<&mut T> {
if self.ptr.is_null() {
None
} else {
unsafe { Some(&mut *self.ptr) }
}
}
}
unsafe impl<T: Send> Send for EpochPtr<T> {}
unsafe impl<T: Sync> Sync for EpochPtr<T> {}
impl<T> Clone for EpochPtr<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T> Copy for EpochPtr<T> {}
pub trait EpochProtected {
fn with_epoch<F, R>(&self, f: F) -> R
where
F: FnOnce(&Guard) -> R;
}
impl EpochProtected for EpochManager {
fn with_epoch<F, R>(&self, f: F) -> R
where
F: FnOnce(&Guard) -> R,
{
let guard = self.pin();
f(&guard)
}
}
pub type SharedEpochManager = Arc<EpochManager>;
#[cfg(test)]
mod tests {
use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::thread;
use super::*;
#[test]
fn test_epoch_manager_creation() {
let epoch_manager = EpochManager::new();
let _handle = epoch_manager.register();
}
#[test]
fn test_epoch_guard() {
let epoch_manager = EpochManager::new();
let _guard = epoch_manager.pin();
}
#[test]
fn test_defer_destruction() {
let epoch_manager = EpochManager::new();
let mut handle = epoch_manager.register();
let counter = Arc::new(AtomicUsize::new(0));
let counter_clone = counter.clone();
handle.defer(move || {
counter_clone.fetch_add(1, Ordering::SeqCst);
});
handle.flush();
thread::sleep(std::time::Duration::from_millis(10));
}
#[test]
fn test_epoch_ptr() {
let value = Box::into_raw(Box::new(42i32));
let epoch_ptr = EpochPtr::new(value);
assert!(!epoch_ptr.is_null());
unsafe {
assert_eq!(*epoch_ptr.as_ptr(), 42);
if let Some(val_ref) = epoch_ptr.as_ref() {
assert_eq!(*val_ref, 42);
}
drop(Box::from_raw(value));
}
}
#[test]
fn test_null_epoch_ptr() {
let epoch_ptr: EpochPtr<i32> = EpochPtr::null();
assert!(epoch_ptr.is_null());
unsafe {
assert!(epoch_ptr.as_ref().is_none());
}
}
#[test]
fn test_with_epoch() {
let epoch_manager = EpochManager::new();
let result = epoch_manager.with_epoch(|_guard| 42);
assert_eq!(result, 42);
}
}