Skip to content

Commit fa18ed4

Browse files
committed
feat(base): Implement EventCacheStoreLock::lock.
This patch implements `EventCacheStoreLock::lock`, which is similar to `lock_unchecked` but it returns a `Result<EventCacheStoreLockGuard, EventCacheStoreLockPoisonError<…>>` to handle the _poisoned_ case.
1 parent eff9827 commit fa18ed4

File tree

1 file changed

+82
-6
lines changed
  • crates/matrix-sdk-base/src/event_cache/store

1 file changed

+82
-6
lines changed

crates/matrix-sdk-base/src/event_cache/store/mod.rs

Lines changed: 82 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
//! in-memory store.
2121
2222
use std::{
23+
error::Error,
2324
fmt,
2425
ops::{Deref, DerefMut},
2526
result::Result as StdResult,
@@ -50,11 +51,23 @@ pub use self::{
5051
#[derive(Clone)]
5152
pub struct EventCacheStoreLock {
5253
/// The inner cross process lock that is used to lock the `EventCacheStore`.
53-
cross_process_lock: CrossProcessStoreLock<LockableEventCacheStore>,
54+
cross_process_lock: CrossProcessStoreLock<Arc<LockableEventCacheStore>>,
55+
56+
/// A reference to the `LockableEventCacheStore`.
57+
///
58+
/// This is used to get access to extra API on `LockableEventCacheStore`,
59+
/// not restricted to the `BackingStore` trait.
60+
///
61+
/// This is necessary because `CrossProcessStoreLock` doesn't provide a way
62+
/// to get a reference to the inner backing store. And that's okay.
63+
lockable_store: Arc<LockableEventCacheStore>,
5464

5565
/// The store itself.
5666
///
57-
/// That's the only place where the store exists.
67+
/// The store lives here, and inside `Self::lockable_store`.
68+
///
69+
/// This is necessary because the lock methods return a guard that contains
70+
/// a reference to the store.
5871
store: Arc<DynEventCacheStore>,
5972
}
6073

@@ -75,13 +88,15 @@ impl EventCacheStoreLock {
7588
S: IntoEventCacheStore,
7689
{
7790
let store = store.into_event_cache_store();
91+
let lockable_store = Arc::new(LockableEventCacheStore::new(store.clone()));
7892

7993
Self {
8094
cross_process_lock: CrossProcessStoreLock::new(
81-
LockableEventCacheStore::new(store.clone()),
95+
lockable_store.clone(),
8296
"default".to_owned(),
8397
holder,
8498
),
99+
lockable_store,
85100
store,
86101
}
87102
}
@@ -91,9 +106,38 @@ impl EventCacheStoreLock {
91106
/// It doesn't check whether the lock has been poisoned or not.
92107
/// A lock has been poisoned if it's been acquired from another holder.
93108
pub async fn lock_unchecked(&self) -> Result<EventCacheStoreLockGuard<'_>, LockStoreError> {
109+
match self.lock().await? {
110+
Ok(guard) => Ok(guard),
111+
Err(poison_error) => Ok(poison_error.into_inner()),
112+
}
113+
}
114+
115+
/// Acquire a spin lock (see [`CrossProcessStoreLock::spin_lock`]).
116+
///
117+
/// It **does** check whether the lock has been poisoned or not. Use
118+
/// [`EventCacheStoreLock::lock_unchecked`] if you don't want to check. A
119+
/// lock has been poisoned if it's been acquired from another holder.
120+
///
121+
/// This method returns a first `Result` to handle the locking error. The
122+
/// `Ok` variant contains another `Result` to handle the locking poison.
123+
pub async fn lock(
124+
&self,
125+
) -> Result<
126+
Result<
127+
EventCacheStoreLockGuard<'_>,
128+
EventCacheStoreLockPoisonError<EventCacheStoreLockGuard<'_>>,
129+
>,
130+
LockStoreError,
131+
> {
94132
let cross_process_lock_guard = self.cross_process_lock.spin_lock(None).await?;
133+
let event_cache_store_lock_guard =
134+
EventCacheStoreLockGuard { cross_process_lock_guard, store: self.store.deref() };
95135

96-
Ok(EventCacheStoreLockGuard { cross_process_lock_guard, store: self.store.deref() })
136+
Ok(if self.lockable_store.is_poisoned() {
137+
Err(EventCacheStoreLockPoisonError(event_cache_store_lock_guard))
138+
} else {
139+
Ok(event_cache_store_lock_guard)
140+
})
97141
}
98142
}
99143

@@ -124,12 +168,44 @@ impl<'a> Deref for EventCacheStoreLockGuard<'a> {
124168
}
125169
}
126170

171+
/// A type of error which can be returned whenever a cross-process lock is
172+
/// acquired.
173+
///
174+
/// [`EventCacheStoreLock`] is poisoned whenever the lock is acquired from
175+
/// another holder than the current holder, i.e. if the previous lock was held
176+
/// by another process basically.
177+
pub struct EventCacheStoreLockPoisonError<T>(T);
178+
179+
impl<T> EventCacheStoreLockPoisonError<T> {
180+
fn into_inner(self) -> T {
181+
self.0
182+
}
183+
}
184+
185+
impl<T> fmt::Debug for EventCacheStoreLockPoisonError<T> {
186+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
187+
f.debug_struct("EventCacheStoreLockPoisonError").finish_non_exhaustive()
188+
}
189+
}
190+
191+
impl<T> fmt::Display for EventCacheStoreLockPoisonError<T> {
192+
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
193+
"Poisoned lock: lock has been acquired from another process".fmt(formatter)
194+
}
195+
}
196+
197+
impl<T> Error for EventCacheStoreLockPoisonError<T> {
198+
fn description(&self) -> &str {
199+
"Poisoned lock: lock has been acquired from another process"
200+
}
201+
}
202+
127203
/// Event cache store specific error type.
128204
#[derive(Debug, thiserror::Error)]
129205
pub enum EventCacheStoreError {
130206
/// An error happened in the underlying database backend.
131207
#[error(transparent)]
132-
Backend(Box<dyn std::error::Error + Send + Sync>),
208+
Backend(Box<dyn Error + Send + Sync>),
133209

134210
/// The store is locked with a passphrase and an incorrect passphrase
135211
/// was given.
@@ -163,7 +239,7 @@ impl EventCacheStoreError {
163239
#[inline]
164240
pub fn backend<E>(error: E) -> Self
165241
where
166-
E: std::error::Error + Send + Sync + 'static,
242+
E: Error + Send + Sync + 'static,
167243
{
168244
Self::Backend(Box::new(error))
169245
}

0 commit comments

Comments
 (0)