use crate::{AsyncRLock, RedissonError, RedissonResult};
use std::sync::Arc;
use tokio::sync::Mutex as TokioMutex;
pub struct AsyncRMultiLock {
locks: Vec<AsyncRLock>,
acquired_indices: Arc<TokioMutex<Vec<usize>>>,
}
impl AsyncRMultiLock {
pub fn new(locks: Vec<AsyncRLock>) -> Self {
Self {
locks,
acquired_indices: Arc::new(TokioMutex::new(Vec::new())),
}
}
pub async fn lock(&self) -> RedissonResult<()> {
let mut acquired_indices = Vec::new();
for (i, lock) in self.locks.iter().enumerate() {
match lock.try_lock().await {
Ok(true) => acquired_indices.push(i),
Ok(false) => {
for &idx in &acquired_indices {
let _ = self.locks[idx].unlock().await;
}
return Err(RedissonError::LockAcquisitionError);
}
Err(e) => {
for &idx in &acquired_indices {
let _ = self.locks[idx].unlock().await;
}
return Err(e);
}
}
}
*self.acquired_indices.lock().await = acquired_indices;
Ok(())
}
pub async fn unlock(&self) -> RedissonResult<()> {
let mut acquired_indices = self.acquired_indices.lock().await;
let mut errors = Vec::new();
for &idx in acquired_indices.iter() {
if idx < self.locks.len() {
if let Err(e) = self.locks[idx].unlock().await {
errors.push(e);
}
}
}
acquired_indices.clear();
if errors.is_empty() {
Ok(())
} else {
Err(RedissonError::LockReleaseError)
}
}
pub async fn try_lock(&self) -> RedissonResult<bool> {
let mut acquired_indices = Vec::new();
for (i, lock) in self.locks.iter().enumerate() {
if lock.try_lock().await? {
acquired_indices.push(i);
} else {
for &idx in &acquired_indices {
let _ = self.locks[idx].unlock().await;
}
return Ok(false);
}
}
*self.acquired_indices.lock().await = acquired_indices;
Ok(true)
}
}
impl Clone for AsyncRMultiLock {
fn clone(&self) -> Self {
Self {
locks: self.locks.clone(),
acquired_indices: self.acquired_indices.clone(),
}
}
}