@@ -340,12 +340,17 @@ impl<T> BaoFileHandleWeak<T> {
340
340
}
341
341
}
342
342
343
+ /// a type alias which represents the callback which is executed after
344
+ /// the write guard is dropped
345
+ type AfterLockWriteCb < T > = Box < dyn Fn ( & BaoFileStorage < T > ) + Send + Sync + ' static > ;
346
+
343
347
/// The inner part of a bao file handle.
344
348
#[ derive( Debug ) ]
345
349
pub struct BaoFileHandleInner < T > {
346
- pub ( crate ) storage : tokio :: sync :: RwLock < BaoFileStorage < T > > ,
350
+ pub ( crate ) storage : CallbackLock < BaoFileStorage < T > , AfterLockWriteCb < T > > ,
347
351
config : Arc < BaoFileConfig > ,
348
352
hash : Hash ,
353
+ rx : tokio:: sync:: watch:: Receiver < StorageMeta > ,
349
354
}
350
355
351
356
/// A cheaply cloneable handle to a bao file, including the hash and the configuration.
@@ -511,21 +516,55 @@ enum HandleChange {
511
516
// later: size verified
512
517
}
513
518
519
+ /// struct which stores simple metadata about the [BaoFileHandle] in a way that is
520
+ /// accessible in synchronous function calls
521
+ #[ derive( Debug ) ]
522
+ struct StorageMeta {
523
+ complete : bool ,
524
+ size : Result < u64 , io:: Error > ,
525
+ }
526
+
527
+ impl StorageMeta {
528
+ fn new < T : bao_tree:: io:: sync:: ReadAt > ( storage : & BaoFileStorage < T > ) -> Self {
529
+ let size = match storage {
530
+ BaoFileStorage :: Complete ( mem) => Ok ( mem. data_size ( ) ) ,
531
+ BaoFileStorage :: IncompleteMem ( mem) => Ok ( mem. current_size ( ) ) ,
532
+ BaoFileStorage :: IncompleteFile ( file) => file. current_size ( ) ,
533
+ } ;
534
+ StorageMeta {
535
+ complete : matches ! ( storage, BaoFileStorage :: Complete ( _) ) ,
536
+ size,
537
+ }
538
+ }
539
+ }
540
+
514
541
impl < T > BaoFileHandle < T >
515
542
where
516
543
T : bao_tree:: io:: sync:: ReadAt ,
517
544
{
545
+ /// internal helper function to initialize a new instance of self
546
+ fn new_inner ( storage : BaoFileStorage < T > , config : Arc < BaoFileConfig > , hash : Hash ) -> Self {
547
+ let ( tx, rx) = tokio:: sync:: watch:: channel ( StorageMeta :: new ( & storage) ) ;
548
+ Self ( Arc :: new ( BaoFileHandleInner {
549
+ storage : CallbackLock :: new (
550
+ storage,
551
+ Box :: new ( move |storage : & BaoFileStorage < T > | {
552
+ let _ = tx. send ( StorageMeta :: new ( storage) ) ;
553
+ } ) ,
554
+ ) ,
555
+ config,
556
+ hash,
557
+ rx,
558
+ } ) )
559
+ }
560
+
518
561
/// Create a new bao file handle.
519
562
///
520
563
/// This will create a new file handle with an empty memory storage.
521
564
/// Since there are very likely to be many of these, we use an arc rwlock
522
565
pub fn incomplete_mem ( config : Arc < BaoFileConfig > , hash : Hash ) -> Self {
523
566
let storage = BaoFileStorage :: incomplete_mem ( ) ;
524
- Self ( Arc :: new ( BaoFileHandleInner {
525
- storage : tokio:: sync:: RwLock :: new ( storage) ,
526
- config,
527
- hash,
528
- } ) )
567
+ Self :: new_inner ( storage, config, hash)
529
568
}
530
569
531
570
/// Create a new bao file handle with a partial file.
@@ -536,11 +575,7 @@ where
536
575
outboard : create_read_write ( & paths. outboard ) ?,
537
576
sizes : create_read_write ( & paths. sizes ) ?,
538
577
} ) ;
539
- Ok ( Self ( Arc :: new ( BaoFileHandleInner {
540
- storage : tokio:: sync:: RwLock :: new ( storage) ,
541
- config,
542
- hash,
543
- } ) ) )
578
+ Ok ( Self :: new_inner ( storage, config, hash) )
544
579
}
545
580
546
581
/// Create a new complete bao file handle.
@@ -551,11 +586,7 @@ where
551
586
outboard : MemOrFile < Bytes , FileAndSize < T > > ,
552
587
) -> Self {
553
588
let storage = BaoFileStorage :: Complete ( CompleteStorage { data, outboard } ) ;
554
- Self ( Arc :: new ( BaoFileHandleInner {
555
- storage : tokio:: sync:: RwLock :: new ( storage) ,
556
- config,
557
- hash,
558
- } ) )
589
+ Self :: new_inner ( storage, config, hash)
559
590
}
560
591
561
592
/// Transform the storage in place. If the transform fails, the storage will
@@ -573,10 +604,7 @@ where
573
604
574
605
/// True if the file is complete.
575
606
pub fn is_complete ( & self ) -> bool {
576
- matches ! (
577
- self . storage. try_read( ) . unwrap( ) . deref( ) ,
578
- BaoFileStorage :: Complete ( _)
579
- )
607
+ self . rx . borrow ( ) . deref ( ) . complete
580
608
}
581
609
582
610
/// An AsyncSliceReader for the data file.
@@ -596,18 +624,25 @@ where
596
624
}
597
625
598
626
/// The most precise known total size of the data file.
599
- pub fn current_size ( & self ) -> io:: Result < u64 > {
600
- match self . storage . try_read ( ) . unwrap ( ) . deref ( ) {
601
- BaoFileStorage :: Complete ( mem) => Ok ( mem. data_size ( ) ) ,
602
- BaoFileStorage :: IncompleteMem ( mem) => Ok ( mem. current_size ( ) ) ,
603
- BaoFileStorage :: IncompleteFile ( file) => file. current_size ( ) ,
604
- }
627
+ pub fn current_size ( & self ) -> Result < u64 , io:: ErrorKind > {
628
+ self . rx
629
+ . borrow ( )
630
+ . size
631
+ . as_ref ( )
632
+ // NB: we return the io::ErrorKind here
633
+ // because io::Error is !Clone
634
+ . map_err ( |e| e. kind ( ) )
635
+ . copied ( )
605
636
}
606
637
607
638
/// The outboard for the file.
608
639
pub fn outboard ( & self ) -> io:: Result < PreOrderOutboard < OutboardReader < T > > > {
609
640
let root = self . hash . into ( ) ;
610
- let tree = BaoTree :: new ( self . current_size ( ) ?, IROH_BLOCK_SIZE ) ;
641
+ let tree = BaoTree :: new (
642
+ self . current_size ( )
643
+ . map_err ( |kind| io:: Error :: new ( kind, "an io error has occurred" ) ) ?,
644
+ IROH_BLOCK_SIZE ,
645
+ ) ;
611
646
let outboard = self . outboard_reader ( ) ;
612
647
Ok ( PreOrderOutboard {
613
648
root,
0 commit comments