fix a deadlock

When finishedLiveUpdate was run on a different key than expected, it
blocked forever waiting for an indication the database had been updated.

Since the journal is locked when finishedLiveUpdate runs, this could
also have caused other git-annex commands to hang.
This commit is contained in:
Joey Hess 2024-08-27 00:13:54 -04:00
parent 21608716bd
commit 521e0a7062
No known key found for this signature in database
GPG key ID: DB12DB0FF05F8F38
2 changed files with 12 additions and 12 deletions

View file

@ -59,14 +59,13 @@ prepareLiveUpdate mu k sc = do
startv <- liftIO newEmptyMVar startv <- liftIO newEmptyMVar
readyv <- liftIO newEmptyMVar readyv <- liftIO newEmptyMVar
donev <- liftIO newEmptyMVar donev <- liftIO newEmptyMVar
finishv <- liftIO newEmptyMVar void $ liftIO $ forkIO $ waitstart startv readyv donev h u
void $ liftIO $ forkIO $ waitstart startv readyv donev finishv h u return (LiveUpdate needv startv readyv donev)
return (LiveUpdate needv startv readyv donev finishv)
where where
{- Wait for checkLiveUpdate to request a start, or for the {- Wait for checkLiveUpdate to request a start, or for the
- LiveUpdate to get garbage collected in the case where - LiveUpdate to get garbage collected in the case where
- it is not needed. -} - it is not needed. -}
waitstart startv readyv donev finishv h u = waitstart startv readyv donev h u =
tryNonAsync (takeMVar startv) >>= \case tryNonAsync (takeMVar startv) >>= \case
Right () -> do Right () -> do
pid <- getCurrentPid pid <- getCurrentPid
@ -77,21 +76,23 @@ prepareLiveUpdate mu k sc = do
- need live updates. -} - need live updates. -}
Db.startingLiveSizeChange h u k sc cid Db.startingLiveSizeChange h u k sc cid
putMVar readyv () putMVar readyv ()
waitdone donev finishv h u cid waitdone donev h u cid
Left _ -> noop Left _ -> noop
{- Wait for finishedLiveUpdate to be called, or for the LiveUpdate {- Wait for finishedLiveUpdate to be called, or for the LiveUpdate
- to get garbage collected in the case where the change didn't - to get garbage collected in the case where the change didn't
- actually happen. Updates the database. -} - actually happen. Updates the database. -}
waitdone donev finishv h u cid = tryNonAsync (takeMVar donev) >>= \case waitdone donev finishv h u cid = tryNonAsync (takeMVar donev) >>= \case
Right (Just (u', k', sc')) Right (Just (u', k', sc', finishv))
| u' == u && k' == k && sc' == sc -> do | u' == u && k' == k && sc' == sc -> do
Db.successfullyFinishedLiveSizeChange h u k sc cid Db.successfullyFinishedLiveSizeChange h u k sc cid
putMVar finishv () putMVar finishv ()
-- This can happen when eg, storing to a cluster -- This can happen when eg, storing to a cluster
-- causes fanout and so this is called with -- causes fanout and so this is called with
-- other UUIDs. -- other UUIDs.
| otherwise -> waitdone donev finishv h u cid | otherwise -> do
putMVar finishv ()
waitdone donev finishv h u cid
Right Nothing -> abandoned h u cid Right Nothing -> abandoned h u cid
Left _ -> abandoned h u cid Left _ -> abandoned h u cid
abandoned h u cid = Db.removeStaleLiveSizeChange h u k sc cid abandoned h u cid = Db.removeStaleLiveSizeChange h u k sc cid
@ -122,7 +123,7 @@ checkLiveUpdate lu a = do
finishedLiveUpdate :: LiveUpdate -> UUID -> Key -> SizeChange -> IO () finishedLiveUpdate :: LiveUpdate -> UUID -> Key -> SizeChange -> IO ()
finishedLiveUpdate NoLiveUpdate _ _ _ = noop finishedLiveUpdate NoLiveUpdate _ _ _ = noop
finishedLiveUpdate lu u k sc = do finishedLiveUpdate lu u k sc = do
tryNonAsync (putMVar (liveUpdateDone lu) (Just (u, k, sc))) >>= \case finishv <- newEmptyMVar
Right () -> void $ tryNonAsync (putMVar (liveUpdateDone lu) (Just (u, k, sc, finishv))) >>= \case
tryNonAsync $ readMVar $ liveUpdateFinish lu Right () -> void $ tryNonAsync $ takeMVar finishv
Left _ -> noop Left _ -> noop

View file

@ -39,8 +39,7 @@ data LiveUpdate
{ liveUpdateNeeded :: MVar () { liveUpdateNeeded :: MVar ()
, liveUpdateStart :: MVar () , liveUpdateStart :: MVar ()
, liveUpdateReady :: MVar () , liveUpdateReady :: MVar ()
, liveUpdateDone :: MVar (Maybe (UUID, Key, SizeChange)) , liveUpdateDone :: MVar (Maybe (UUID, Key, SizeChange, MVar ()))
, liveUpdateFinish :: MVar ()
} }
| NoLiveUpdate | NoLiveUpdate