1use std::sync::Arc;
4
5use async_trait::async_trait;
6
7use crate::Result;
8
9#[async_trait]
14pub trait Lock: Send + Sync {
15 async fn acquire(&self, namespace: &str, resource_id: &str) -> Result<()>;
17
18 async fn try_acquire(&self, namespace: &str, resource_id: &str) -> Result<bool>;
22
23 async fn release(&self, namespace: &str, resource_id: &str) -> Result<bool>;
25
26 async fn acquire_with_timeout(
30 &self,
31 namespace: &str,
32 resource_id: &str,
33 timeout_ms: i32,
34 retry_interval_ms: u64,
35 ) -> Result<()>;
36}
37
38pub struct LockGuard {
43 lock: Arc<dyn Lock>,
44 namespace: String,
45 resource_id: String,
46 released: bool,
47}
48
49impl LockGuard {
50 pub async fn acquire(
52 lock: Arc<dyn Lock>,
53 namespace: &str,
54 resource_id: &str,
55 ) -> Result<Self> {
56 lock.acquire(namespace, resource_id).await?;
57 Ok(Self {
58 lock,
59 namespace: namespace.to_string(),
60 resource_id: resource_id.to_string(),
61 released: false,
62 })
63 }
64
65 pub async fn try_acquire(
67 lock: Arc<dyn Lock>,
68 namespace: &str,
69 resource_id: &str,
70 ) -> Result<Option<Self>> {
71 if lock.try_acquire(namespace, resource_id).await? {
72 Ok(Some(Self {
73 lock,
74 namespace: namespace.to_string(),
75 resource_id: resource_id.to_string(),
76 released: false,
77 }))
78 } else {
79 Ok(None)
80 }
81 }
82
83 pub async fn release(mut self) -> Result<bool> {
85 self.released = true;
86 self.lock.release(&self.namespace, &self.resource_id).await
87 }
88}
89
90impl Drop for LockGuard {
91 fn drop(&mut self) {
92 if !self.released {
93 let lock = self.lock.clone();
94 let namespace = self.namespace.clone();
95 let resource_id = self.resource_id.clone();
96
97 let Some(handle) = tokio::runtime::Handle::try_current().ok() else {
98 tracing::warn!(
99 namespace = %namespace,
100 resource_id = %resource_id,
101 "Cannot release lock: no tokio runtime in Drop"
102 );
103 return;
104 };
105 handle.spawn(async move {
106 if let Err(e) = lock.release(&namespace, &resource_id).await {
107 tracing::error!(
108 namespace = %namespace,
109 resource_id = %resource_id,
110 error = %e,
111 "failed to release lock on drop"
112 );
113 }
114 });
115 }
116 }
117}