use crate::{lockedranges::LockedRanges, util::get_bounds, vecparts::VecParts};
use std::{
cell::UnsafeCell,
marker::PhantomData,
mem::size_of,
ops::{Deref, DerefMut, Range, RangeBounds},
slice,
sync::{LockResult, Mutex, PoisonError, TryLockError, TryLockResult},
};
#[derive(Debug)]
pub struct VecRangeLock<T> {
ranges: Mutex<LockedRanges>,
data: UnsafeCell<VecParts<T>>,
}
unsafe impl<T> Sync for VecRangeLock<T> where T: Send {}
impl<'a, T> VecRangeLock<T> {
pub fn new(data: Vec<T>) -> VecRangeLock<T> {
VecRangeLock {
ranges: Mutex::new(LockedRanges::new()),
data: UnsafeCell::new(data.into()),
}
}
#[inline]
pub fn data_len(&self) -> usize {
unsafe { (*self.data.get()).len() }
}
#[inline]
pub fn into_inner(self) -> Vec<T> {
debug_assert!(self.ranges.lock().unwrap().is_empty());
self.data.into_inner().into()
}
pub fn try_lock(
&'a self,
range: impl RangeBounds<usize>,
) -> TryLockResult<VecRangeLockGuard<'a, T>> {
let data_len = self.data_len();
let (range_start, range_end) = get_bounds(&range, data_len);
if range_start >= data_len || range_end > data_len {
panic!("Range is out of bounds.");
}
if range_start > range_end {
panic!("Invalid range. Start is bigger than end.");
}
let range = range_start..range_end;
if range.is_empty() {
TryLockResult::Ok(VecRangeLockGuard::new(self, range))
} else if let LockResult::Ok(mut ranges) = self.ranges.lock() {
if ranges.insert(&range) {
TryLockResult::Ok(VecRangeLockGuard::new(self, range))
} else {
TryLockResult::Err(TryLockError::WouldBlock)
}
} else {
TryLockResult::Err(TryLockError::Poisoned(PoisonError::new(
VecRangeLockGuard::new(self, range),
)))
}
}
fn unlock(&self, range: &Range<usize>) {
if !range.is_empty() {
let mut ranges = self
.ranges
.lock()
.expect("VecRangeLock: Failed to take ranges mutex.");
ranges.remove(range);
}
}
#[inline]
unsafe fn get_slice(&self, range: &Range<usize>) -> &[T] {
let data = (*self.data.get()).ptr();
assert!(range.start <= isize::MAX as usize / size_of::<T>());
unsafe { slice::from_raw_parts(data.add(range.start) as _, range.end - range.start) }
}
#[inline]
#[allow(clippy::mut_from_ref)]
unsafe fn get_mut_slice(&self, range: &Range<usize>) -> &mut [T] {
let data = (*self.data.get()).ptr();
assert!(range.start <= isize::MAX as usize / size_of::<T>());
unsafe { slice::from_raw_parts_mut(data.add(range.start) as _, range.end - range.start) }
}
}
#[derive(Debug)]
pub struct VecRangeLockGuard<'a, T> {
lock: &'a VecRangeLock<T>,
range: Range<usize>,
_p: PhantomData<*mut T>,
}
impl<'a, T> VecRangeLockGuard<'a, T> {
#[inline]
fn new(lock: &'a VecRangeLock<T>, range: Range<usize>) -> VecRangeLockGuard<'a, T> {
VecRangeLockGuard {
lock,
range,
_p: PhantomData,
}
}
}
impl<T> Drop for VecRangeLockGuard<'_, T> {
#[inline]
fn drop(&mut self) {
self.lock.unlock(&self.range);
}
}
impl<T> Deref for VecRangeLockGuard<'_, T> {
type Target = [T];
#[inline]
fn deref(&self) -> &Self::Target {
unsafe { self.lock.get_slice(&self.range) }
}
}
impl<T> DerefMut for VecRangeLockGuard<'_, T> {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { self.lock.get_mut_slice(&self.range) }
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::cell::RefCell;
use std::sync::{Arc, Barrier};
use std::thread;
#[test]
fn test_base() {
{
let a = VecRangeLock::new(vec![1_i32, 2, 3, 4, 5, 6]);
{
let mut g = a.try_lock(2..4).unwrap();
assert!(!a.ranges.lock().unwrap().is_empty());
assert_eq!(g[0..2], [3, 4]);
g[1] = 10;
assert_eq!(g[0..2], [3, 10]);
}
assert!(a.ranges.lock().unwrap().is_empty());
}
{
let a = VecRangeLock::new(vec![1_i32, 2, 3, 4, 5, 6]);
let g = a.try_lock(2..=4).unwrap();
assert_eq!(g[0..3], [3, 4, 5]);
}
{
let a = VecRangeLock::new(vec![1_i32, 2, 3, 4, 5, 6]);
let g = a.try_lock(..4).unwrap();
assert_eq!(g[0..4], [1, 2, 3, 4]);
}
{
let a = VecRangeLock::new(vec![1_i32, 2, 3, 4, 5, 6]);
let g = a.try_lock(..=4).unwrap();
assert_eq!(g[0..5], [1, 2, 3, 4, 5]);
}
{
let a = VecRangeLock::new(vec![1_i32, 2, 3, 4, 5, 6]);
let g = a.try_lock(2..).unwrap();
assert_eq!(g[0..4], [3, 4, 5, 6]);
}
{
let a = VecRangeLock::new(vec![1_i32, 2, 3, 4, 5, 6]);
let g = a.try_lock(..).unwrap();
assert_eq!(g[0..6], [1, 2, 3, 4, 5, 6]);
}
}
#[test]
fn test_empty_range() {
let a = VecRangeLock::new(vec![1_i32, 2, 3, 4, 5, 6]);
let g0 = a.try_lock(2..2).unwrap();
assert!(a.ranges.lock().unwrap().is_empty());
assert_eq!(g0[0..0], []);
let g1 = a.try_lock(2..2).unwrap();
assert!(a.ranges.lock().unwrap().is_empty());
assert_eq!(g1[0..0], []);
}
#[test]
#[should_panic(expected = "index out of bounds")]
fn test_base_oob_read() {
let a = VecRangeLock::new(vec![1_i32, 2, 3, 4, 5, 6]);
let g = a.try_lock(2..4).unwrap();
let _ = g[2];
}
#[test]
#[should_panic(expected = "index out of bounds")]
fn test_base_oob_write() {
let a = VecRangeLock::new(vec![1_i32, 2, 3, 4, 5, 6]);
let mut g = a.try_lock(2..4).unwrap();
g[2] = 10;
}
#[test]
#[should_panic(expected = "guard 1 panicked")]
fn test_overlap0() {
let a = VecRangeLock::new(vec![1_i32, 2, 3, 4, 5, 6]);
let _g0 = a.try_lock(2..4).expect("guard 0 panicked");
let _g1 = a.try_lock(3..5).expect("guard 1 panicked");
}
#[test]
#[should_panic(expected = "guard 0 panicked")]
fn test_overlap1() {
let a = VecRangeLock::new(vec![1_i32, 2, 3, 4, 5, 6]);
let _g1 = a.try_lock(3..5).expect("guard 1 panicked");
let _g0 = a.try_lock(2..4).expect("guard 0 panicked");
}
#[test]
fn test_thread_no_overlap() {
let a = Arc::new(VecRangeLock::new(vec![1_i32, 2, 3, 4, 5, 6]));
let b = Arc::clone(&a);
let c = Arc::clone(&a);
let ba0 = Arc::new(Barrier::new(2));
let ba1 = Arc::clone(&ba0);
let j0 = thread::spawn(move || {
{
let mut g = b.try_lock(2..4).unwrap();
assert!(!b.ranges.lock().unwrap().is_empty());
assert_eq!(g[0..2], [3, 4]);
g[1] = 10;
assert_eq!(g[0..2], [3, 10]);
}
ba0.wait();
});
let j1 = thread::spawn(move || {
{
let g = c.try_lock(4..6).unwrap();
assert!(!c.ranges.lock().unwrap().is_empty());
assert_eq!(g[0..2], [5, 6]);
}
ba1.wait();
let g = c.try_lock(3..5).unwrap();
assert_eq!(g[0..2], [10, 5]);
});
j1.join().expect("Thread 1 panicked.");
j0.join().expect("Thread 0 panicked.");
assert!(a.ranges.lock().unwrap().is_empty());
}
#[allow(dead_code)]
struct NoSyncStruct(RefCell<u32>);
#[test]
fn test_nosync() {
let a = Arc::new(VecRangeLock::new(vec![
NoSyncStruct(RefCell::new(1)),
NoSyncStruct(RefCell::new(2)),
NoSyncStruct(RefCell::new(3)),
NoSyncStruct(RefCell::new(4)),
]));
let b = Arc::clone(&a);
let c = Arc::clone(&a);
let ba0 = Arc::new(Barrier::new(2));
let ba1 = Arc::clone(&ba0);
let j0 = thread::spawn(move || {
let _g = b.try_lock(0..1).unwrap();
assert!(!b.ranges.lock().unwrap().is_empty());
ba0.wait();
});
let j1 = thread::spawn(move || {
let _g = c.try_lock(1..2).unwrap();
assert!(!c.ranges.lock().unwrap().is_empty());
ba1.wait();
});
j1.join().expect("Thread 1 panicked.");
j0.join().expect("Thread 0 panicked.");
assert!(a.ranges.lock().unwrap().is_empty());
}
}