20
20
//! in-memory store.
21
21
22
22
use std:: {
23
+ error:: Error ,
23
24
fmt,
24
25
ops:: { Deref , DerefMut } ,
25
26
result:: Result as StdResult ,
@@ -50,11 +51,23 @@ pub use self::{
50
51
#[ derive( Clone ) ]
51
52
pub struct EventCacheStoreLock {
52
53
/// 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 > ,
54
64
55
65
/// The store itself.
56
66
///
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.
58
71
store : Arc < DynEventCacheStore > ,
59
72
}
60
73
@@ -75,13 +88,15 @@ impl EventCacheStoreLock {
75
88
S : IntoEventCacheStore ,
76
89
{
77
90
let store = store. into_event_cache_store ( ) ;
91
+ let lockable_store = Arc :: new ( LockableEventCacheStore :: new ( store. clone ( ) ) ) ;
78
92
79
93
Self {
80
94
cross_process_lock : CrossProcessStoreLock :: new (
81
- LockableEventCacheStore :: new ( store . clone ( ) ) ,
95
+ lockable_store . clone ( ) ,
82
96
"default" . to_owned ( ) ,
83
97
holder,
84
98
) ,
99
+ lockable_store,
85
100
store,
86
101
}
87
102
}
@@ -91,9 +106,38 @@ impl EventCacheStoreLock {
91
106
/// It doesn't check whether the lock has been poisoned or not.
92
107
/// A lock has been poisoned if it's been acquired from another holder.
93
108
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
+ > {
94
132
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 ( ) } ;
95
135
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
+ } )
97
141
}
98
142
}
99
143
@@ -124,12 +168,44 @@ impl<'a> Deref for EventCacheStoreLockGuard<'a> {
124
168
}
125
169
}
126
170
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
+
127
203
/// Event cache store specific error type.
128
204
#[ derive( Debug , thiserror:: Error ) ]
129
205
pub enum EventCacheStoreError {
130
206
/// An error happened in the underlying database backend.
131
207
#[ error( transparent) ]
132
- Backend ( Box < dyn std :: error :: Error + Send + Sync > ) ,
208
+ Backend ( Box < dyn Error + Send + Sync > ) ,
133
209
134
210
/// The store is locked with a passphrase and an incorrect passphrase
135
211
/// was given.
@@ -163,7 +239,7 @@ impl EventCacheStoreError {
163
239
#[ inline]
164
240
pub fn backend < E > ( error : E ) -> Self
165
241
where
166
- E : std :: error :: Error + Send + Sync + ' static ,
242
+ E : Error + Send + Sync + ' static ,
167
243
{
168
244
Self :: Backend ( Box :: new ( error) )
169
245
}
0 commit comments