use crate::core::detector;
use crate::core::locks::NEXT_LOCK_ID;
use crate::core::types::{LockId, ThreadId, get_current_thread_id};
#[cfg(feature = "logging-and-visualization")]
use crate::core::{Events, logger};
use parking_lot::{
RwLock as ParkingLotRwLock, RwLockReadGuard as ParkingLotReadGuard,
RwLockWriteGuard as ParkingLotWriteGuard,
};
use std::ops::{Deref, DerefMut};
use std::sync::atomic::{AtomicUsize, Ordering};
pub struct RwLock<T> {
id: LockId,
inner: ParkingLotRwLock<T>,
creator_thread_id: ThreadId,
writer_owner: AtomicUsize,
}
pub struct RwLockReadGuard<'a, T> {
thread_id: ThreadId,
lock_id: LockId,
guard: ParkingLotReadGuard<'a, T>,
}
pub struct RwLockWriteGuard<'a, T> {
thread_id: ThreadId,
lock_id: LockId,
guard: ParkingLotWriteGuard<'a, T>,
owner_atomic: &'a AtomicUsize,
tracked_globally: bool,
}
impl<T> RwLock<T> {
pub fn new(value: T) -> Self {
let id = NEXT_LOCK_ID.fetch_add(1, Ordering::SeqCst);
let creator_thread_id = get_current_thread_id();
detector::rwlock::create_rwlock(id, Some(creator_thread_id));
RwLock {
id,
inner: ParkingLotRwLock::new(value),
creator_thread_id,
writer_owner: AtomicUsize::new(0),
}
}
pub fn id(&self) -> LockId {
self.id
}
pub fn creator_thread_id(&self) -> ThreadId {
self.creator_thread_id
}
pub fn read(&self) -> RwLockReadGuard<'_, T> {
let thread_id = get_current_thread_id();
let guard = crate::core::detector::rwlock::attempt_read(thread_id, self.id, || {
self.inner.try_read()
});
let guard = match guard {
Some(g) => g,
None => {
let g = self.inner.read();
detector::rwlock::complete_read(thread_id, self.id);
g
}
};
RwLockReadGuard {
thread_id,
lock_id: self.id,
guard,
}
}
pub fn write(&self) -> RwLockWriteGuard<'_, T> {
let thread_id = get_current_thread_id();
let tid_usize = thread_id as usize;
#[cfg(not(feature = "stress-test"))]
if let Some(guard) = self.inner.try_write() {
self.writer_owner.store(tid_usize, Ordering::Release);
#[cfg(feature = "logging-and-visualization")]
{
if logger::LOGGING_ENABLED.load(Ordering::Relaxed) {
logger::log_interaction_event(thread_id, self.id, Events::RwWriteAttempt);
}
}
#[cfg(feature = "lock-order-graph")]
detector::rwlock::complete_write(thread_id, self.id);
#[cfg(feature = "logging-and-visualization")]
{
if logger::LOGGING_ENABLED.load(Ordering::Relaxed) {
logger::log_interaction_event(thread_id, self.id, Events::RwWriteAcquired);
}
}
return RwLockWriteGuard {
thread_id,
lock_id: self.id,
guard,
owner_atomic: &self.writer_owner,
tracked_globally: cfg!(feature = "lock-order-graph"),
};
}
let mut current_writer_val = self.writer_owner.load(Ordering::Acquire);
if current_writer_val == 0 && self.inner.is_locked_exclusive() {
let mut spin_count = 0;
while current_writer_val == 0 {
if spin_count < 100 {
std::hint::spin_loop();
} else {
std::thread::yield_now();
}
current_writer_val = self.writer_owner.load(Ordering::Relaxed);
spin_count += 1;
if spin_count % 16 == 0 && !self.inner.is_locked_exclusive() {
break;
}
}
std::sync::atomic::fence(Ordering::Acquire);
}
let current_writer = if current_writer_val == 0 {
None
} else {
Some(current_writer_val as ThreadId)
};
let deadlock_info =
detector::rwlock::acquire_write_slow(thread_id, self.id, current_writer);
if let Some(info) = deadlock_info {
let is_stale = if let Some(expected_writer) = current_writer {
let actual_writer = self.writer_owner.load(Ordering::Relaxed);
!detector::deadlock_handling::verify_deadlock_edges(
&info,
thread_id,
self.id,
expected_writer,
actual_writer,
)
} else {
false
};
if !is_stale {
detector::deadlock_handling::process_deadlock(info);
}
}
let guard = self.inner.write();
detector::rwlock::complete_write(thread_id, self.id);
self.writer_owner.store(tid_usize, Ordering::Release);
RwLockWriteGuard {
thread_id,
lock_id: self.id,
guard,
owner_atomic: &self.writer_owner,
tracked_globally: true,
}
}
pub fn try_read(&self) -> Option<RwLockReadGuard<'_, T>> {
let thread_id = get_current_thread_id();
let guard = detector::rwlock::attempt_read(thread_id, self.id, || self.inner.try_read());
guard.map(|g| RwLockReadGuard {
thread_id,
lock_id: self.id,
guard: g,
})
}
pub fn try_write(&self) -> Option<RwLockWriteGuard<'_, T>> {
let thread_id = get_current_thread_id();
if let Some(guard) = self.inner.try_write() {
self.writer_owner
.store(thread_id as usize, Ordering::Release);
#[cfg(feature = "logging-and-visualization")]
{
if logger::LOGGING_ENABLED.load(Ordering::Relaxed) {
logger::log_interaction_event(thread_id, self.id, Events::RwWriteAttempt);
}
}
#[cfg(feature = "lock-order-graph")]
detector::rwlock::complete_write(thread_id, self.id);
#[cfg(feature = "logging-and-visualization")]
{
if logger::LOGGING_ENABLED.load(Ordering::Relaxed) {
logger::log_interaction_event(thread_id, self.id, Events::RwWriteAcquired);
}
}
Some(RwLockWriteGuard {
thread_id,
lock_id: self.id,
guard,
owner_atomic: &self.writer_owner,
tracked_globally: cfg!(feature = "lock-order-graph"),
})
} else {
None
}
}
pub fn into_inner(self) -> T
where
T: Sized,
{
detector::rwlock::destroy_rwlock(self.id);
let rwlock = std::mem::ManuallyDrop::new(self);
unsafe { std::ptr::read(&rwlock.inner) }.into_inner()
}
pub fn get_mut(&mut self) -> &mut T {
self.inner.get_mut()
}
}
impl<T> Drop for RwLock<T> {
fn drop(&mut self) {
detector::rwlock::destroy_rwlock(self.id);
}
}
impl<'a, T> Deref for RwLockReadGuard<'a, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
self.guard.deref()
}
}
impl<'a, T> Drop for RwLockReadGuard<'a, T> {
fn drop(&mut self) {
detector::rwlock::release_read(self.thread_id, self.lock_id);
}
}
impl<'a, T> Deref for RwLockWriteGuard<'a, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
self.guard.deref()
}
}
impl<'a, T> DerefMut for RwLockWriteGuard<'a, T> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.guard.deref_mut()
}
}
impl<'a, T> Drop for RwLockWriteGuard<'a, T> {
fn drop(&mut self) {
self.owner_atomic.store(0, Ordering::Release);
if self.tracked_globally {
detector::rwlock::release_write(self.thread_id, self.lock_id);
} else {
#[cfg(feature = "logging-and-visualization")]
if logger::LOGGING_ENABLED.load(Ordering::Relaxed) {
logger::log_interaction_event(
self.thread_id,
self.lock_id,
Events::RwWriteReleased,
);
}
}
}
}
impl<T: Default> Default for RwLock<T> {
fn default() -> RwLock<T> {
RwLock::new(Default::default())
}
}
impl<T> From<T> for RwLock<T> {
fn from(t: T) -> Self {
RwLock::new(t)
}
}