@@ -24,13 +24,16 @@ import Control.RAWLock
2424import qualified Control.RAWLock as RAWLock
2525import Control.ResourceRegistry
2626import Control.Tracer
27+ import Data.Foldable (traverse_ )
2728import qualified Data.Foldable as Foldable
2829import Data.Functor.Contravariant ((>$<) )
2930import Data.Kind (Type )
3031import Data.Map (Map )
3132import qualified Data.Map.Strict as Map
3233import Data.Set (Set )
3334import qualified Data.Set as Set
35+ import Data.Traversable (for )
36+ import Data.Tuple (Solo (.. ))
3437import Data.Void
3538import Data.Word
3639import GHC.Generics
@@ -193,19 +196,17 @@ mkInternals ::
193196mkInternals bss h =
194197 TestInternals
195198 { takeSnapshotNOW = \ whereTo suff -> getEnv h $ \ env -> do
196- st <-
197- ( case whereTo of
199+ let selectWhereTo = case whereTo of
198200 TakeAtImmutableTip -> anchorHandle
199201 TakeAtVolatileTip -> currentHandle
200- )
201- <$> readTVarIO (ldbSeq env)
202- Monad. void $
203- takeSnapshot
204- (configCodec . getExtLedgerCfg . ledgerDbCfg $ ldbCfg env)
205- (LedgerDBSnapshotEvent >$< ldbTracer env)
206- (ldbHasFS env)
207- suff
208- st
202+ withStateRef env (MkSolo . selectWhereTo) $ \ (MkSolo st) ->
203+ Monad. void $
204+ takeSnapshot
205+ (configCodec . getExtLedgerCfg . ledgerDbCfg $ ldbCfg env)
206+ (LedgerDBSnapshotEvent >$< ldbTracer env)
207+ (ldbHasFS env)
208+ suff
209+ st
209210 , push = \ st -> withRegistry $ \ reg -> do
210211 eFrk <- newForkerAtTarget h reg VolatileTip
211212 case eFrk of
@@ -368,13 +369,13 @@ implTryTakeSnapshot ::
368369implTryTakeSnapshot bss env mTime nrBlocks =
369370 if onDiskShouldTakeSnapshot (ldbSnapshotPolicy env) (uncurry (flip diffTime) <$> mTime) nrBlocks
370371 then do
371- Monad. void
372- . takeSnapshot
373- (configCodec . getExtLedgerCfg . ledgerDbCfg $ ldbCfg env)
374- ( LedgerDBSnapshotEvent >$< ldbTracer env)
375- (ldbHasFS env)
376- . anchorHandle
377- =<< readTVarIO (ldbSeq env)
372+ withStateRef env ( MkSolo . anchorHandle) $ \ ( MkSolo st) ->
373+ Monad. void $
374+ takeSnapshot
375+ (configCodec . getExtLedgerCfg . ledgerDbCfg $ ldbCfg env)
376+ ( LedgerDBSnapshotEvent >$< ldbTracer env)
377+ (ldbHasFS env)
378+ st
378379 Monad. void $
379380 trimSnapshots
380381 (LedgerDBSnapshotEvent >$< ldbTracer env)
@@ -457,6 +458,19 @@ data LedgerDBEnv m l blk = LedgerDBEnv
457458 , ldbResolveBlock :: ! (ResolveBlock m blk )
458459 , ldbQueryBatchSize :: ! QueryBatchSize
459460 , ldbOpenHandlesLock :: ! (RAWLock m LDBLock )
461+ -- ^ While holding a read lock (at least), all handles in the 'ldbSeq' are
462+ -- guaranteed to be open. During this time, the handle can be duplicated and
463+ -- then be used independently, see 'getStateRef' and 'withStateRef'.
464+ --
465+ -- Therefore, closing any handles which were previously in 'ldbSeq' requires
466+ -- acquiring a write lock. Concretely, both of the following approaches are
467+ -- fine:
468+ --
469+ -- * Modify 'ldbSeq' without any locking, and then close the removed handles
470+ -- while holding a write lock. See e.g. 'closeForkerEnv'.
471+ --
472+ -- * Modify 'ldbSeq' while holding a write lock, and then close the removed
473+ -- handles without any locking.
460474 }
461475 deriving Generic
462476
@@ -546,8 +560,36 @@ getEnvSTM (LDBHandle varState) f =
546560 Acquiring consistent views
547561-------------------------------------------------------------------------------}
548562
549- -- | This function must hold the 'LDBLock' such that handles are not released
550- -- before they are duplicated.
563+ -- | Get a 'StateRef' from the 'LedgerSeq' in the 'LedgerDBEnv', with the
564+ -- 'LedgerTablesHandle' having been duplicated (such that the original can be
565+ -- closed). The caller is responsible for closing the handle.
566+ --
567+ -- For more flexibility, an arbitrary 'Traversable' of the 'StateRef' can be
568+ -- returned; for the simple use case of getting a single 'StateRef', use @t ~
569+ -- 'Solo'@.
570+ getStateRef ::
571+ (IOLike m , Traversable t ) =>
572+ LedgerDBEnv m l blk ->
573+ (LedgerSeq m l -> t (StateRef m l )) ->
574+ m (t (StateRef m l ))
575+ getStateRef ldbEnv project =
576+ RAWLock. withReadAccess (ldbOpenHandlesLock ldbEnv) $ \ LDBLock -> do
577+ tst <- project <$> readTVarIO (ldbSeq ldbEnv)
578+ for tst $ \ st -> do
579+ tables' <- duplicate $ tables st
580+ pure st{tables = tables'}
581+
582+ -- | Like 'StateRef', but takes care of closing the handle when the given action
583+ -- returns or errors.
584+ withStateRef ::
585+ (IOLike m , Traversable t ) =>
586+ LedgerDBEnv m l blk ->
587+ (LedgerSeq m l -> t (StateRef m l )) ->
588+ (t (StateRef m l ) -> m a ) ->
589+ m a
590+ withStateRef ldbEnv project =
591+ bracket (getStateRef ldbEnv project) (traverse_ (close . tables))
592+
551593acquireAtTarget ::
552594 ( HeaderHash l ~ HeaderHash blk
553595 , IOLike m
@@ -557,41 +599,28 @@ acquireAtTarget ::
557599 ) =>
558600 LedgerDBEnv m l blk ->
559601 Either Word64 (Target (Point blk )) ->
560- LDBLock ->
561602 m (Either GetForkerError (StateRef m l ))
562- acquireAtTarget ldbEnv (Right VolatileTip ) _ = do
563- l <- readTVarIO (ldbSeq ldbEnv)
564- let StateRef st tbs = currentHandle l
565- t <- duplicate tbs
566- pure $ Right $ StateRef st t
567- acquireAtTarget ldbEnv (Right ImmutableTip ) _ = do
568- l <- readTVarIO (ldbSeq ldbEnv)
569- let StateRef st tbs = anchorHandle l
570- t <- duplicate tbs
571- pure $ Right $ StateRef st t
572- acquireAtTarget ldbEnv (Right (SpecificPoint pt)) _ = do
573- dblog <- readTVarIO (ldbSeq ldbEnv)
574- let immTip = getTip $ anchor dblog
575- case currentHandle <$> rollback pt dblog of
576- Nothing
577- | pointSlot pt < pointSlot immTip -> pure $ Left $ PointTooOld Nothing
578- | otherwise -> pure $ Left PointNotOnChain
579- Just (StateRef st tbs) ->
580- Right . StateRef st <$> duplicate tbs
581- acquireAtTarget ldbEnv (Left n) _ = do
582- dblog <- readTVarIO (ldbSeq ldbEnv)
583- case currentHandle <$> rollbackN n dblog of
584- Nothing ->
585- return $
586- Left $
603+ acquireAtTarget ldbEnv target =
604+ getStateRef ldbEnv $ \ l -> case target of
605+ Right VolatileTip -> pure $ currentHandle l
606+ Right ImmutableTip -> pure $ anchorHandle l
607+ Right (SpecificPoint pt) -> do
608+ let immTip = getTip $ anchor l
609+ case rollback pt l of
610+ Nothing
611+ | pointSlot pt < pointSlot immTip -> throwError $ PointTooOld Nothing
612+ | otherwise -> throwError PointNotOnChain
613+ Just t' -> pure $ currentHandle t'
614+ Left n -> case rollbackN n l of
615+ Nothing ->
616+ throwError $
587617 PointTooOld $
588- Just $
618+ Just
589619 ExceededRollback
590- { rollbackMaximum = maxRollback dblog
620+ { rollbackMaximum = maxRollback l
591621 , rollbackRequested = n
592622 }
593- Just (StateRef st tbs) ->
594- Right . StateRef st <$> duplicate tbs
623+ Just l' -> pure $ currentHandle l'
595624
596625newForkerAtTarget ::
597626 ( HeaderHash l ~ HeaderHash blk
@@ -605,8 +634,8 @@ newForkerAtTarget ::
605634 ResourceRegistry m ->
606635 Target (Point blk ) ->
607636 m (Either GetForkerError (Forker m l blk ))
608- newForkerAtTarget h rr pt = getEnv h $ \ ldbEnv@ LedgerDBEnv {ldbOpenHandlesLock = lock} ->
609- RAWLock. withReadAccess lock ( acquireAtTarget ldbEnv (Right pt) ) >>= traverse (newForker h ldbEnv rr)
637+ newForkerAtTarget h rr pt = getEnv h $ \ ldbEnv ->
638+ acquireAtTarget ldbEnv (Right pt) >>= traverse (newForker h ldbEnv rr)
610639
611640newForkerByRollback ::
612641 ( HeaderHash l ~ HeaderHash blk
@@ -620,8 +649,8 @@ newForkerByRollback ::
620649 ResourceRegistry m ->
621650 Word64 ->
622651 m (Either GetForkerError (Forker m l blk ))
623- newForkerByRollback h rr n = getEnv h $ \ ldbEnv@ LedgerDBEnv {ldbOpenHandlesLock = lock} -> do
624- RAWLock. withReadAccess lock ( acquireAtTarget ldbEnv (Left n) ) >>= traverse (newForker h ldbEnv rr)
652+ newForkerByRollback h rr n = getEnv h $ \ ldbEnv ->
653+ acquireAtTarget ldbEnv (Left n) >>= traverse (newForker h ldbEnv rr)
625654
626655-- | Close all open 'Forker's.
627656closeAllForkers ::
0 commit comments