use std::cell::Cell;
use crate::handle_unwind::handle_unwind;
use crate::lockable::{Lockable, RawLock, Sharable};
use crate::Keyable;
#[must_use]
pub fn get_locks<L: Lockable>(data: &L) -> Vec<&dyn RawLock> {
let mut locks = get_locks_unsorted(data);
locks.sort_by_key(|lock| &raw const **lock);
locks
}
#[must_use]
pub fn get_locks_unsorted<L: Lockable>(data: &L) -> Vec<&dyn RawLock> {
let mut locks = Vec::new();
data.get_ptrs(&mut locks);
locks
}
#[must_use]
pub fn ordered_contains_duplicates(l: &[&dyn RawLock]) -> bool {
if l.is_empty() {
return false;
}
l.windows(2)
.any(|window| std::ptr::addr_eq(window[0], window[1]))
}
pub unsafe fn ordered_write(locks: &[&dyn RawLock]) {
let locked = Cell::new(0);
handle_unwind(
|| {
for lock in locks {
lock.raw_write();
locked.set(locked.get() + 1);
}
},
|| attempt_to_recover_writes_from_panic(&locks[0..locked.get()]),
)
}
pub unsafe fn ordered_read(locks: &[&dyn RawLock]) {
let locked = Cell::new(0);
handle_unwind(
|| {
for lock in locks {
lock.raw_read();
locked.set(locked.get() + 1);
}
},
|| attempt_to_recover_reads_from_panic(&locks[0..locked.get()]),
)
}
pub unsafe fn ordered_try_write(locks: &[&dyn RawLock]) -> bool {
let locked = Cell::new(0);
handle_unwind(
|| unsafe {
for (i, lock) in locks.iter().enumerate() {
if lock.raw_try_write() {
locked.set(locked.get() + 1);
} else {
for lock in &locks[0..i] {
lock.raw_unlock_write();
}
return false;
}
}
true
},
||
attempt_to_recover_writes_from_panic(&locks[0..locked.get()]),
)
}
pub unsafe fn ordered_try_read(locks: &[&dyn RawLock]) -> bool {
let locked = Cell::new(0);
handle_unwind(
|| unsafe {
for (i, lock) in locks.iter().enumerate() {
if lock.raw_try_read() {
locked.set(locked.get() + 1);
} else {
for lock in &locks[0..i] {
lock.raw_unlock_read();
}
return false;
}
}
true
},
||
attempt_to_recover_reads_from_panic(&locks[0..locked.get()]),
)
}
pub fn scoped_write<'a, L: RawLock + Lockable + ?Sized, R>(
collection: &'a L,
key: impl Keyable,
f: impl FnOnce(L::DataMut<'a>) -> R,
) -> R {
unsafe {
collection.raw_write();
let r = handle_unwind(
|| f(collection.data_mut()),
|| collection.raw_unlock_write(),
);
drop(key);
collection.raw_unlock_write();
r
}
}
pub fn scoped_try_write<'a, L: RawLock + Lockable + ?Sized, Key: Keyable, R>(
collection: &'a L,
key: Key,
f: impl FnOnce(L::DataMut<'a>) -> R,
) -> Result<R, Key> {
unsafe {
if !collection.raw_try_write() {
return Err(key);
}
let r = handle_unwind(
|| f(collection.data_mut()),
|| collection.raw_unlock_write(),
);
drop(key);
collection.raw_unlock_write();
Ok(r)
}
}
pub fn scoped_read<'a, L: RawLock + Sharable + ?Sized, R>(
collection: &'a L,
key: impl Keyable,
f: impl FnOnce(L::DataRef<'a>) -> R,
) -> R {
unsafe {
collection.raw_read();
let r = handle_unwind(|| f(collection.data_ref()), || collection.raw_unlock_read());
drop(key);
collection.raw_unlock_read();
r
}
}
pub fn scoped_try_read<'a, L: RawLock + Sharable + ?Sized, Key: Keyable, R>(
collection: &'a L,
key: Key,
f: impl FnOnce(L::DataRef<'a>) -> R,
) -> Result<R, Key> {
unsafe {
if !collection.raw_try_read() {
return Err(key);
}
let r = handle_unwind(|| f(collection.data_ref()), || collection.raw_unlock_read());
drop(key);
collection.raw_unlock_read();
Ok(r)
}
}
pub unsafe fn attempt_to_recover_writes_from_panic(locks: &[&dyn RawLock]) {
handle_unwind(
|| {
for lock in locks {
lock.raw_unlock_write();
}
},
|| locks.iter().for_each(|l| l.poison()),
)
}
pub unsafe fn attempt_to_recover_reads_from_panic(locked: &[&dyn RawLock]) {
handle_unwind(
|| {
for lock in locked {
lock.raw_unlock_read();
}
},
|| locked.iter().for_each(|l| l.poison()),
)
}
#[cfg(test)]
mod tests {
use crate::collection::utils::ordered_contains_duplicates;
#[test]
fn empty_array_does_not_contain_duplicates() {
assert!(!ordered_contains_duplicates(&[]))
}
}