Merge pull request #163 from serokell/diogo/#162-interrupt-io

[#162] Do not cancel the progress bar thread
This commit is contained in:
Diogo Castro 2022-09-26 14:16:33 +01:00 committed by GitHub
commit 54aa5415bf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 38 additions and 11 deletions

View File

@ -8,9 +8,10 @@ Unreleased
========== ==========
* [#145](https://github.com/serokell/xrefcheck/pull/145) * [#145](https://github.com/serokell/xrefcheck/pull/145)
+ Add check that there is no unknown fields in config. + Add check that there is no unknown fields in config.
* [#158](https://github.com/serokell/xrefcheck/pull/158) * [#158](https://github.com/serokell/xrefcheck/pull/158)
+ Fixed bug when we reported footnotes as broken links + Fixed bug when we reported footnotes as broken links
* [#163](https://github.com/serokell/xrefcheck/pull/163)
+ Fixed an issue where the progress bar thread might be unexpectedly cancelled and jumble up the output.
0.2.1 0.2.1
========== ==========

View File

@ -31,7 +31,7 @@ import Universum
import Data.Ratio ((%)) import Data.Ratio ((%))
import System.Console.Pretty (Color (..), Style (..), color, style) import System.Console.Pretty (Color (..), Style (..), color, style)
import Time (Second, Time, ms, sec, threadDelay, unTime, (-:-)) import Time (Second, Time, sec, unTime, (-:-))
----------------------------------------------------------- -----------------------------------------------------------
-- Task timestamp -- Task timestamp
@ -225,8 +225,6 @@ allowRewrite enabled = bracket prepare erase
erase (Rewrite RewriteCtx{..}) = liftIO $ do erase (Rewrite RewriteCtx{..}) = liftIO $ do
maxPrintedSize <- readIORef rMaxPrintedSize maxPrintedSize <- readIORef rMaxPrintedSize
hPutStr stderr $ '\r' : replicate maxPrintedSize ' ' ++ "\r" hPutStr stderr $ '\r' : replicate maxPrintedSize ' ' ++ "\r"
-- prevent our output to interleave with further outputs
threadDelay (ms 100)
erase RewriteDisabled = pass erase RewriteDisabled = pass
-- | Return caret and print the given text. -- | Return caret and print the given text.

View File

@ -33,8 +33,9 @@ module Xrefcheck.Verify
import Universum import Universum
import Control.Concurrent.Async (wait, withAsync) import Control.Concurrent.Async (async, wait, withAsync)
import Control.Exception (throwIO) import Control.Exception (throwIO)
import Control.Monad.Catch (handleJust)
import Control.Monad.Except (MonadError (..)) import Control.Monad.Except (MonadError (..))
import Data.ByteString qualified as BS import Data.ByteString qualified as BS
import Data.List qualified as L import Data.List qualified as L
@ -43,7 +44,7 @@ import Data.Text.Metrics (damerauLevenshteinNorm)
import Data.Time (UTCTime, defaultTimeLocale, formatTime, readPTime, rfc822DateFormat) import Data.Time (UTCTime, defaultTimeLocale, formatTime, readPTime, rfc822DateFormat)
import Data.Time.Clock.POSIX (getPOSIXTime) import Data.Time.Clock.POSIX (getPOSIXTime)
import Data.Traversable (for) import Data.Traversable (for)
import Fmt (Buildable (..), blockListF', listF, maybeF, nameF, (+|), (|+), unlinesF, indentF) import Fmt (Buildable (..), blockListF', indentF, listF, maybeF, nameF, unlinesF, (+|), (|+))
import GHC.Exts qualified as Exts import GHC.Exts qualified as Exts
import GHC.Read (Read (readPrec)) import GHC.Read (Read (readPrec))
import Network.FTP.Client import Network.FTP.Client
@ -57,13 +58,12 @@ import Network.HTTP.Types.Header (hRetryAfter)
import Network.HTTP.Types.Status (Status, statusCode, statusMessage) import Network.HTTP.Types.Status (Status, statusCode, statusMessage)
import System.Console.Pretty (Style (..), style) import System.Console.Pretty (Style (..), style)
import System.Directory (doesDirectoryExist, doesFileExist) import System.Directory (doesDirectoryExist, doesFileExist)
import System.FilePath (takeDirectory, (</>), normalise) import System.FilePath (normalise, takeDirectory, (</>))
import Text.ParserCombinators.ReadPrec qualified as ReadPrec (lift) import Text.ParserCombinators.ReadPrec qualified as ReadPrec (lift)
import Text.Regex.TDFA.Text (Regex, regexec) import Text.Regex.TDFA.Text (Regex, regexec)
import Text.URI (Authority (..), URI (..), mkURIBs, ParseExceptionBs) import Text.URI (Authority (..), ParseExceptionBs, URI (..), mkURIBs)
import Time (RatioNat, Second, Time (..), ms, sec, threadDelay, timeout, (+:+), (-:-)) import Time (RatioNat, Second, Time (..), ms, sec, threadDelay, timeout, (+:+), (-:-))
import URI.ByteString qualified as URIBS import URI.ByteString qualified as URIBS
import Control.Monad.Catch (handleJust)
import Data.Bits (toIntegralSized) import Data.Bits (toIntegralSized)
import Xrefcheck.Config import Xrefcheck.Config
@ -265,12 +265,13 @@ verifyRepo
progressRef <- newIORef $ initVerifyProgress (map snd toScan) progressRef <- newIORef $ initVerifyProgress (map snd toScan)
accumulated <- withAsync (printer progressRef) $ \_ -> accumulated <- loopAsyncUntil (printer progressRef) do
forConcurrentlyCaching toScan ifExternalThenCache $ \(file, ref) -> forConcurrentlyCaching toScan ifExternalThenCache $ \(file, ref) ->
verifyReference config mode progressRef repoInfo' root file ref verifyReference config mode progressRef repoInfo' root file ref
return $ fold accumulated return $ fold accumulated
where where
printer progressRef = forever $ do printer :: IORef VerifyProgress -> IO ()
printer progressRef = do
posixTime <- getPOSIXTime <&> posixTimeToTimeSecond posixTime <- getPOSIXTime <&> posixTimeToTimeSecond
progress <- atomicModifyIORef' progressRef $ \VerifyProgress{..} -> progress <- atomicModifyIORef' progressRef $ \VerifyProgress{..} ->
let prog = VerifyProgress{ vrExternal = let prog = VerifyProgress{ vrExternal =
@ -279,8 +280,10 @@ verifyRepo
} }
in (prog, prog) in (prog, prog)
reprintAnalyseProgress rw mode posixTime progress reprintAnalyseProgress rw mode posixTime progress
-- Slight pause so we're not refreshing the progress bar more often than needed.
threadDelay (ms 100) threadDelay (ms 100)
ifExternalThenCache :: (a, Reference) -> NeedsCaching Text
ifExternalThenCache (_, Reference{..}) = case locationType rLink of ifExternalThenCache (_, Reference{..}) = case locationType rLink of
ExternalLoc -> CacheUnderKey rLink ExternalLoc -> CacheUnderKey rLink
_ -> NoCaching _ -> NoCaching
@ -621,3 +624,28 @@ checkExternalResource VerifyConfig{..} link
pure () pure ()
where where
handler = if secure then withFTPS else withFTP handler = if secure then withFTPS else withFTP
----------------------------------------------------------------------------
-- Helpers
----------------------------------------------------------------------------
-- | @loopAsyncUntil ma mb@ will continually run @ma@ until @mb@ throws an exception or returns.
-- Once it does, it'll wait for @ma@ to finish running one last time and then return.
--
-- See #163 to read more on why it's important to let @ma@ finish cleanly.
-- * https://github.com/serokell/xrefcheck/issues/162
-- * https://github.com/serokell/xrefcheck/pull/163
loopAsyncUntil :: forall a b. IO a -> IO b -> IO b
loopAsyncUntil loopingAction action =
mask $ \restore -> do
shouldLoop <- newIORef True
loopingActionAsync <- async $ restore $ loopingAction' shouldLoop
restore action `finally` do
writeIORef shouldLoop False
wait loopingActionAsync
where
loopingAction' :: IORef Bool -> IO ()
loopingAction' shouldLoop = do
whenM (readIORef shouldLoop) do
void loopingAction
loopingAction' shouldLoop