Skip to content

Commit 19bf835

Browse files
committed
Improve and clarify exception safety of newPending{Level,Union}Merge
We rely on doing interruptable operations that could receive async exceptions _before_ doing operations that allocate fresh resources.
1 parent a45babe commit 19bf835

File tree

1 file changed

+32
-24
lines changed

1 file changed

+32
-24
lines changed

src/Database/LSMTree/Internal/MergingTree.hs

Lines changed: 32 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -125,26 +125,27 @@ newPendingLevelMerge ::
125125
-> Maybe (Ref (MergingTree m h))
126126
-> m (Ref (MergingTree m h))
127127
newPendingLevelMerge [] (Just t) = dupRef t
128+
newPendingLevelMerge [PreExistingRun r] Nothing = do
129+
-- No need to create a pending merge here.
130+
--
131+
-- We could do something similar for PreExistingMergingRun, but it's:
132+
-- * complicated, because of the LevelMergeType\/TreeMergeType mismatch.
133+
-- * unneeded, since that case should never occur. If there is only a
134+
-- single entry in the list, there can only be one level in the input
135+
-- table. At level 1 there are no merging runs, so it must be a
136+
-- PreExistingRun.
137+
r' <- dupRef r
138+
-- There are no interruption points here, and thus provided async
139+
-- exceptions are masked then there can be no async exceptions here at all.
140+
newMergeTree (CompletedTreeMerge r')
141+
128142
newPendingLevelMerge prs mmt = do
129-
-- There are no interruption points here, and thus provided async exceptions
130-
-- are masked then there can be no async exceptions here at all.
131-
mergeTreeState <- case (prs, mmt) of
132-
([PreExistingRun r], Nothing) ->
133-
-- No need to create a pending merge here.
134-
--
135-
-- We could do something similar for PreExistingMergingRun, but it's:
136-
-- * complicated, because of the LevelMergeType/TreeMergeType mismatch.
137-
-- * unneeded, since that case should never occur. If there is only a
138-
-- single entry in the list, there can only be one level in the input
139-
-- table. At level 1 there are no merging runs, so it must be a
140-
-- PreExistingRun.
141-
CompletedTreeMerge <$> dupRef r
142-
143-
_ -> PendingTreeMerge <$>
144-
(PendingLevelMerge <$> traverse dupPreExistingRun (V.fromList prs)
145-
<*> dupMaybeMergingTree mmt)
146-
147-
newMergeTree mergeTreeState
143+
-- isStructurallyEmpty is an interruption point, and can receive async
144+
-- exceptions even when masked. So we use it first, *before* allocating
145+
-- new references.
146+
mmt' <- dupMaybeMergingTree mmt
147+
prs' <- traverse dupPreExistingRun (V.fromList prs)
148+
newMergeTree (PendingTreeMerge (PendingLevelMerge prs' mmt'))
148149
where
149150
dupPreExistingRun (PreExistingRun r) =
150151
PreExistingRun <$!> dupRef r
@@ -180,12 +181,14 @@ newPendingUnionMerge ::
180181
=> [Ref (MergingTree m h)]
181182
-> m (Ref (MergingTree m h))
182183
newPendingUnionMerge mts = do
183-
mts' <- V.mapM dupRef
184-
=<< V.filterM (fmap not . isStructurallyEmpty) (V.fromList mts)
185-
case V.uncons mts' of
186-
Just (mt, mts'') | V.null mts''
184+
mts' <- V.filterM (fmap not . isStructurallyEmpty) (V.fromList mts)
185+
-- isStructurallyEmpty is interruptable even with async exceptions masked,
186+
-- but we use it before allocating new references.
187+
mts'' <- V.mapM dupRef mts'
188+
case V.uncons mts'' of
189+
Just (mt, x) | V.null x
187190
-> return mt
188-
_ -> newMergeTree (PendingTreeMerge (PendingUnionMerge mts'))
191+
_ -> newMergeTree (PendingTreeMerge (PendingUnionMerge mts''))
189192

190193
-- | Test if a 'MergingTree' is \"obviously\" empty by virtue of its structure.
191194
-- This is not the same as being empty due to a pending or ongoing merge
@@ -202,6 +205,11 @@ isStructurallyEmpty (DeRef MergingTree {mergeState}) =
202205
-- a zero length runs as empty.
203206

204207
-- | Constructor helper.
208+
--
209+
-- This adopts the references in the MergingTreeState, so callers should
210+
-- duplicate first. This is not the normal pattern, but this is an internal
211+
-- helper only.
212+
--
205213
newMergeTree ::
206214
(MonadMVar m, PrimMonad m, MonadMask m)
207215
=> MergingTreeState m h

0 commit comments

Comments
 (0)