mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-05 12:57:42 +00:00
179 lines
5.2 KiB
Rust
179 lines
5.2 KiB
Rust
use std::time::{Duration, Instant};
|
|
|
|
use rand::Rng;
|
|
use tokio::{sync::RwLock, time::sleep};
|
|
use tracing::info;
|
|
|
|
#[derive(Debug, Default)]
|
|
pub struct LRWMutex {
|
|
id: RwLock<String>,
|
|
source: RwLock<String>,
|
|
is_write: RwLock<bool>,
|
|
refrence: RwLock<usize>,
|
|
}
|
|
|
|
impl LRWMutex {
|
|
pub async fn lock(&self) -> bool {
|
|
let is_write = true;
|
|
let id = self.id.read().await.clone();
|
|
let source = self.source.read().await.clone();
|
|
let timeout = Duration::from_secs(10000);
|
|
self.look_loop(
|
|
&id, &source, &timeout, // big enough
|
|
is_write,
|
|
)
|
|
.await
|
|
}
|
|
|
|
pub async fn get_lock(&self, id: &str, source: &str, timeout: &Duration) -> bool {
|
|
let is_write = true;
|
|
self.look_loop(id, source, timeout, is_write).await
|
|
}
|
|
|
|
pub async fn r_lock(&self) -> bool {
|
|
let is_write: bool = false;
|
|
let id = self.id.read().await.clone();
|
|
let source = self.source.read().await.clone();
|
|
let timeout = Duration::from_secs(10000);
|
|
self.look_loop(
|
|
&id, &source, &timeout, // big enough
|
|
is_write,
|
|
)
|
|
.await
|
|
}
|
|
|
|
pub async fn get_r_lock(&self, id: &str, source: &str, timeout: &Duration) -> bool {
|
|
let is_write = false;
|
|
self.look_loop(id, source, timeout, is_write).await
|
|
}
|
|
|
|
async fn inner_lock(&self, id: &str, source: &str, is_write: bool) -> bool {
|
|
*self.id.write().await = id.to_string();
|
|
*self.source.write().await = source.to_string();
|
|
|
|
let mut locked = false;
|
|
if is_write {
|
|
if *self.refrence.read().await == 0 && !*self.is_write.read().await {
|
|
*self.refrence.write().await = 1;
|
|
*self.is_write.write().await = true;
|
|
locked = true;
|
|
}
|
|
} else if !*self.is_write.read().await {
|
|
*self.refrence.write().await += 1;
|
|
locked = true;
|
|
}
|
|
|
|
locked
|
|
}
|
|
|
|
async fn look_loop(&self, id: &str, source: &str, timeout: &Duration, is_write: bool) -> bool {
|
|
let start = Instant::now();
|
|
loop {
|
|
if self.inner_lock(id, source, is_write).await {
|
|
return true;
|
|
} else {
|
|
if Instant::now().duration_since(start) > *timeout {
|
|
return false;
|
|
}
|
|
let sleep_time: u64;
|
|
{
|
|
let mut rng = rand::thread_rng();
|
|
sleep_time = rng.gen_range(10..=50);
|
|
}
|
|
sleep(Duration::from_millis(sleep_time)).await;
|
|
}
|
|
}
|
|
}
|
|
|
|
pub async fn un_lock(&self) {
|
|
let is_write = true;
|
|
if !self.unlock(is_write).await {
|
|
info!("Trying to un_lock() while no Lock() is active")
|
|
}
|
|
}
|
|
|
|
pub async fn un_r_lock(&self) {
|
|
let is_write = false;
|
|
if !self.unlock(is_write).await {
|
|
info!("Trying to un_r_lock() while no Lock() is active")
|
|
}
|
|
}
|
|
|
|
async fn unlock(&self, is_write: bool) -> bool {
|
|
let mut unlocked = false;
|
|
if is_write {
|
|
if *self.is_write.read().await && *self.refrence.read().await == 1 {
|
|
*self.refrence.write().await = 0;
|
|
*self.is_write.write().await = false;
|
|
unlocked = true;
|
|
}
|
|
} else if !*self.is_write.read().await && *self.refrence.read().await > 0 {
|
|
*self.refrence.write().await -= 1;
|
|
unlocked = true;
|
|
}
|
|
|
|
unlocked
|
|
}
|
|
|
|
pub async fn force_un_lock(&self) {
|
|
*self.refrence.write().await = 0;
|
|
*self.is_write.write().await = false;
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod test {
|
|
use std::{sync::Arc, time::Duration};
|
|
|
|
use common::error::Result;
|
|
use tokio::time::sleep;
|
|
|
|
use crate::lrwmutex::LRWMutex;
|
|
|
|
#[tokio::test]
|
|
async fn test_lock_unlock() -> Result<()> {
|
|
let l_rw_lock = LRWMutex::default();
|
|
let id = "foo";
|
|
let source = "dandan";
|
|
let timeout = Duration::from_secs(5);
|
|
assert!(l_rw_lock.get_lock(id, source, &timeout).await);
|
|
l_rw_lock.un_lock().await;
|
|
|
|
l_rw_lock.lock().await;
|
|
|
|
assert!(!l_rw_lock.get_r_lock(id, source, &timeout).await);
|
|
l_rw_lock.un_lock().await;
|
|
assert!(l_rw_lock.get_r_lock(id, source, &timeout).await);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn multi_thread_test() -> Result<()> {
|
|
let l_rw_lock = Arc::new(LRWMutex::default());
|
|
let id = "foo";
|
|
let source = "dandan";
|
|
|
|
let one_fn = async {
|
|
let one = Arc::clone(&l_rw_lock);
|
|
let timeout = Duration::from_secs(1);
|
|
assert!(one.get_lock(id, source, &timeout).await);
|
|
sleep(Duration::from_secs(5)).await;
|
|
l_rw_lock.un_lock().await;
|
|
};
|
|
|
|
let two_fn = async {
|
|
let two = Arc::clone(&l_rw_lock);
|
|
let timeout = Duration::from_secs(2);
|
|
assert!(!two.get_r_lock(id, source, &timeout).await);
|
|
sleep(Duration::from_secs(5)).await;
|
|
assert!(two.get_r_lock(id, source, &timeout).await);
|
|
two.un_r_lock().await;
|
|
};
|
|
|
|
tokio::join!(one_fn, two_fn);
|
|
|
|
Ok(())
|
|
}
|
|
}
|