use std::collections::HashSet;
use std::sync::Arc;
use std::time::Duration;
use fileloft_core::{
error::TusError,
info::UploadId,
lock::{SendLock, SendLocker},
};
use tokio::sync::Mutex;
type HeldSet = Arc<Mutex<HashSet<String>>>;
#[derive(Clone)]
pub struct MemoryLocker {
held: HeldSet,
pub timeout: Duration,
}
impl MemoryLocker {
pub fn new() -> Self {
Self {
held: Arc::new(Mutex::new(HashSet::new())),
timeout: Duration::from_secs(20),
}
}
pub fn with_timeout(mut self, timeout: Duration) -> Self {
self.timeout = timeout;
self
}
}
impl Default for MemoryLocker {
fn default() -> Self {
Self::new()
}
}
impl SendLocker for MemoryLocker {
type LockType = MemoryLock;
async fn acquire(&self, id: &UploadId) -> Result<MemoryLock, TusError> {
let deadline = tokio::time::Instant::now() + self.timeout;
loop {
{
let mut held = self.held.lock().await;
if held.insert(id.as_str().to_string()) {
return Ok(MemoryLock {
id: id.as_str().to_string(),
held: Arc::clone(&self.held),
released: false,
});
}
}
if tokio::time::Instant::now() >= deadline {
return Err(TusError::LockTimeout(id.to_string()));
}
tokio::time::sleep(Duration::from_millis(10)).await;
}
}
}
pub struct MemoryLock {
id: String,
held: HeldSet,
released: bool,
}
impl SendLock for MemoryLock {
async fn release(mut self) -> Result<(), TusError> {
self.held.lock().await.remove(&self.id);
self.released = true;
Ok(())
}
}
impl Drop for MemoryLock {
fn drop(&mut self) {
if !self.released {
if let Ok(mut held) = self.held.try_lock() {
held.remove(&self.id);
}
}
}
}