mirror of
https://github.com/lightningdevkit/rust-lightning.git
synced 2025-02-25 15:20:24 +01:00
Because we handle messages (which can take some time, persisting things to disk or validating cryptographic signatures) with the top-level read lock, but require the top-level write lock to connect new peers or handle disconnection, we are particularly sensitive to writer starvation issues. Rust's libstd RwLock does not provide any fairness guarantees, using whatever the OS provides as-is. On Linux, pthreads defaults to starving writers, which Rust's RwLock exposes to us (without any configurability). Here we work around that issue by blocking readers if there are pending writers, optimizing for readable code over perfectly-optimized blocking.
117 lines
2.4 KiB
Rust
117 lines
2.4 KiB
Rust
pub use ::alloc::sync::Arc;
|
|
use core::ops::{Deref, DerefMut};
|
|
use core::time::Duration;
|
|
use core::cell::{RefCell, Ref, RefMut};
|
|
|
|
pub type LockResult<Guard> = Result<Guard, ()>;
|
|
|
|
pub struct Condvar {}
|
|
|
|
impl Condvar {
|
|
pub fn new() -> Condvar {
|
|
Condvar { }
|
|
}
|
|
|
|
pub fn wait<'a, T>(&'a self, guard: MutexGuard<'a, T>) -> LockResult<MutexGuard<'a, T>> {
|
|
Ok(guard)
|
|
}
|
|
|
|
#[allow(unused)]
|
|
pub fn wait_timeout<'a, T>(&'a self, guard: MutexGuard<'a, T>, _dur: Duration) -> LockResult<(MutexGuard<'a, T>, ())> {
|
|
Ok((guard, ()))
|
|
}
|
|
|
|
pub fn notify_all(&self) {}
|
|
}
|
|
|
|
pub struct Mutex<T: ?Sized> {
|
|
inner: RefCell<T>
|
|
}
|
|
|
|
#[must_use = "if unused the Mutex will immediately unlock"]
|
|
pub struct MutexGuard<'a, T: ?Sized + 'a> {
|
|
lock: RefMut<'a, T>,
|
|
}
|
|
|
|
impl<T: ?Sized> Deref for MutexGuard<'_, T> {
|
|
type Target = T;
|
|
|
|
fn deref(&self) -> &T {
|
|
&self.lock.deref()
|
|
}
|
|
}
|
|
|
|
impl<T: ?Sized> DerefMut for MutexGuard<'_, T> {
|
|
fn deref_mut(&mut self) -> &mut T {
|
|
self.lock.deref_mut()
|
|
}
|
|
}
|
|
|
|
impl<T> Mutex<T> {
|
|
pub fn new(inner: T) -> Mutex<T> {
|
|
Mutex { inner: RefCell::new(inner) }
|
|
}
|
|
|
|
pub fn lock<'a>(&'a self) -> LockResult<MutexGuard<'a, T>> {
|
|
Ok(MutexGuard { lock: self.inner.borrow_mut() })
|
|
}
|
|
|
|
pub fn try_lock<'a>(&'a self) -> LockResult<MutexGuard<'a, T>> {
|
|
Ok(MutexGuard { lock: self.inner.borrow_mut() })
|
|
}
|
|
}
|
|
|
|
pub struct RwLock<T: ?Sized> {
|
|
inner: RefCell<T>
|
|
}
|
|
|
|
pub struct RwLockReadGuard<'a, T: ?Sized + 'a> {
|
|
lock: Ref<'a, T>,
|
|
}
|
|
|
|
pub struct RwLockWriteGuard<'a, T: ?Sized + 'a> {
|
|
lock: RefMut<'a, T>,
|
|
}
|
|
|
|
impl<T: ?Sized> Deref for RwLockReadGuard<'_, T> {
|
|
type Target = T;
|
|
|
|
fn deref(&self) -> &T {
|
|
&self.lock.deref()
|
|
}
|
|
}
|
|
|
|
impl<T: ?Sized> Deref for RwLockWriteGuard<'_, T> {
|
|
type Target = T;
|
|
|
|
fn deref(&self) -> &T {
|
|
&self.lock.deref()
|
|
}
|
|
}
|
|
|
|
impl<T: ?Sized> DerefMut for RwLockWriteGuard<'_, T> {
|
|
fn deref_mut(&mut self) -> &mut T {
|
|
self.lock.deref_mut()
|
|
}
|
|
}
|
|
|
|
impl<T> RwLock<T> {
|
|
pub fn new(inner: T) -> RwLock<T> {
|
|
RwLock { inner: RefCell::new(inner) }
|
|
}
|
|
|
|
pub fn read<'a>(&'a self) -> LockResult<RwLockReadGuard<'a, T>> {
|
|
Ok(RwLockReadGuard { lock: self.inner.borrow() })
|
|
}
|
|
|
|
pub fn write<'a>(&'a self) -> LockResult<RwLockWriteGuard<'a, T>> {
|
|
Ok(RwLockWriteGuard { lock: self.inner.borrow_mut() })
|
|
}
|
|
|
|
pub fn try_write<'a>(&'a self) -> LockResult<RwLockWriteGuard<'a, T>> {
|
|
// There is no try, grasshopper - only used for tests and expected to fail
|
|
Err(())
|
|
}
|
|
}
|
|
|
|
pub type FairRwLock<T> = RwLock<T>;
|