@@ -125,26 +125,27 @@ newPendingLevelMerge ::
125125 -> Maybe (Ref (MergingTree m h ))
126126 -> m (Ref (MergingTree m h ))
127127newPendingLevelMerge [] (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+
128142newPendingLevelMerge 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 ))
182183newPendingUnionMerge 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+ --
205213newMergeTree ::
206214 (MonadMVar m , PrimMonad m , MonadMask m )
207215 => MergingTreeState m h
0 commit comments