4
4
#![ allow( missing_docs) ]
5
5
6
6
use std:: {
7
- collections:: BTreeMap ,
7
+ collections:: { BTreeMap , BTreeSet } ,
8
8
fmt:: Debug ,
9
+ ops:: DerefMut ,
9
10
sync:: { Arc , OnceLock } ,
10
11
} ;
11
12
12
- use anyhow:: { anyhow, Result } ;
13
+ use anyhow:: { anyhow, bail , Result } ;
13
14
use futures_lite:: future:: Boxed as BoxedFuture ;
15
+ use futures_util:: future:: BoxFuture ;
14
16
use iroh_base:: hash:: { BlobFormat , Hash } ;
15
17
use iroh_net:: { endpoint:: Connecting , Endpoint , NodeAddr } ;
16
18
use iroh_router:: ProtocolHandler ;
@@ -24,27 +26,32 @@ use crate::{
24
26
Stats ,
25
27
} ,
26
28
provider:: EventSender ,
29
+ store:: GcConfig ,
27
30
util:: {
28
- local_pool:: LocalPoolHandle ,
31
+ local_pool:: { self , LocalPoolHandle } ,
29
32
progress:: { AsyncChannelProgressSender , ProgressSender } ,
30
33
SetTagOption ,
31
34
} ,
32
35
HashAndFormat , TempTag ,
33
36
} ;
34
37
35
- // pub type ProtectCb = Box<dyn Fn(&mut BTreeSet<Hash>) -> BoxFuture<()> + Send + Sync>;
36
- //
37
- // #[derive(derive_more::Debug)]
38
- // enum GcState {
39
- // Initial(#[debug(skip)] Vec<ProtectCb>),
40
- // Started(#[allow(dead_code)] Option<local_pool::Run<()>>),
41
- // }
42
- //
43
- // impl Default for GcState {
44
- // fn default() -> Self {
45
- // Self::Initial(Vec::new())
46
- // }
47
- // }
38
+ /// A callback that blobs can ask about a set of hashes that should not be garbage collected.
39
+ pub type ProtectCb = Box < dyn Fn ( & mut BTreeSet < Hash > ) -> BoxFuture < ( ) > + Send + Sync > ;
40
+
41
+ /// The state of the gc loop.
42
+ #[ derive( derive_more:: Debug ) ]
43
+ enum GcState {
44
+ // Gc loop is not yet running. Other protcols can add protect callbacks
45
+ Initial ( #[ debug( skip) ] Vec < ProtectCb > ) ,
46
+ // Gc loop is running. No more protect callbacks can be added.
47
+ Started ( #[ allow( dead_code) ] Option < local_pool:: Run < ( ) > > ) ,
48
+ }
49
+
50
+ impl Default for GcState {
51
+ fn default ( ) -> Self {
52
+ Self :: Initial ( Vec :: new ( ) )
53
+ }
54
+ }
48
55
49
56
#[ derive( Debug ) ]
50
57
pub struct Blobs < S > {
@@ -54,6 +61,7 @@ pub struct Blobs<S> {
54
61
downloader : Downloader ,
55
62
batches : tokio:: sync:: Mutex < BlobBatches > ,
56
63
endpoint : Endpoint ,
64
+ gc_state : Arc < std:: sync:: Mutex < GcState > > ,
57
65
#[ cfg( feature = "rpc" ) ]
58
66
pub ( crate ) rpc_handler : Arc < OnceLock < crate :: rpc:: RpcHandler > > ,
59
67
}
@@ -185,6 +193,7 @@ impl<S: crate::store::Store> Blobs<S> {
185
193
downloader,
186
194
endpoint,
187
195
batches : Default :: default ( ) ,
196
+ gc_state : Default :: default ( ) ,
188
197
#[ cfg( feature = "rpc" ) ]
189
198
rpc_handler : Arc :: new ( OnceLock :: new ( ) ) ,
190
199
}
@@ -206,43 +215,47 @@ impl<S: crate::store::Store> Blobs<S> {
206
215
& self . endpoint
207
216
}
208
217
209
- // pub fn add_protected(&self, cb: ProtectCb) -> Result<()> {
210
- // let mut state = self.gc_state.lock().unwrap();
211
- // match &mut *state {
212
- // GcState::Initial(cbs) => {
213
- // cbs.push(cb);
214
- // }
215
- // GcState::Started(_) => {
216
- // anyhow::bail!("cannot add protected blobs after gc has started");
217
- // }
218
- // }
219
- // Ok(())
220
- // }
221
- //
222
- // pub fn start_gc(&self, config: GcConfig) -> Result<()> {
223
- // let mut state = self.gc_state.lock().unwrap();
224
- // let protected = match state.deref_mut() {
225
- // GcState::Initial(items) => std::mem::take(items),
226
- // GcState::Started(_) => anyhow::bail!("gc already started"),
227
- // };
228
- // let protected = Arc::new(protected);
229
- // let protected_cb = move || {
230
- // let protected = protected.clone();
231
- // async move {
232
- // let mut set = BTreeSet::new();
233
- // for cb in protected.iter() {
234
- // cb(&mut set).await;
235
- // }
236
- // set
237
- // }
238
- // };
239
- // let store = self.store.clone();
240
- // let run = self
241
- // .rt
242
- // .spawn(move || async move { store.gc_run(config, protected_cb).await });
243
- // *state = GcState::Started(Some(run));
244
- // Ok(())
245
- // }
218
+ /// Add a callback that will be called before the garbage collector runs.
219
+ ///
220
+ /// This can only be called before the garbage collector has started, otherwise it will return an error.
221
+ pub fn add_protected ( & self , cb : ProtectCb ) -> Result < ( ) > {
222
+ let mut state = self . gc_state . lock ( ) . unwrap ( ) ;
223
+ match & mut * state {
224
+ GcState :: Initial ( cbs) => {
225
+ cbs. push ( cb) ;
226
+ }
227
+ GcState :: Started ( _) => {
228
+ anyhow:: bail!( "cannot add protected blobs after gc has started" ) ;
229
+ }
230
+ }
231
+ Ok ( ( ) )
232
+ }
233
+
234
+ /// Start garbage collection with the given settings.
235
+ pub fn start_gc ( & self , config : GcConfig ) -> Result < ( ) > {
236
+ let mut state = self . gc_state . lock ( ) . unwrap ( ) ;
237
+ let protected = match state. deref_mut ( ) {
238
+ GcState :: Initial ( items) => std:: mem:: take ( items) ,
239
+ GcState :: Started ( _) => bail ! ( "gc already started" ) ,
240
+ } ;
241
+ let protected = Arc :: new ( protected) ;
242
+ let protected_cb = move || {
243
+ let protected = protected. clone ( ) ;
244
+ async move {
245
+ let mut set = BTreeSet :: new ( ) ;
246
+ for cb in protected. iter ( ) {
247
+ cb ( & mut set) . await ;
248
+ }
249
+ set
250
+ }
251
+ } ;
252
+ let store = self . store . clone ( ) ;
253
+ let run = self
254
+ . rt
255
+ . spawn ( move || async move { store. gc_run ( config, protected_cb) . await } ) ;
256
+ * state = GcState :: Started ( Some ( run) ) ;
257
+ Ok ( ( ) )
258
+ }
246
259
247
260
pub ( crate ) async fn batches ( & self ) -> tokio:: sync:: MutexGuard < ' _ , BlobBatches > {
248
261
self . batches . lock ( ) . await
0 commit comments