Skip to content

Commit

Permalink
Fix overly long mtime change delay calibration with HFS+.
Browse files Browse the repository at this point in the history
Fixes #4230.
  • Loading branch information
23Skidoo committed Feb 16, 2017
1 parent ca00c82 commit 9752f24
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 8 deletions.
1 change: 1 addition & 0 deletions Cabal/Cabal.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,7 @@ test-suite unit-tests
containers,
directory,
filepath,
time,
tasty,
tasty-hunit,
tasty-quickcheck,
Expand Down
26 changes: 20 additions & 6 deletions Cabal/Distribution/Compat/Time.hs
Original file line number Diff line number Diff line change
Expand Up @@ -184,19 +184,33 @@ getCurTime = posixTimeToModTime `fmap` getPOSIXTime -- Uses 'gettimeofday'.
calibrateMtimeChangeDelay :: IO (Int, Int)
calibrateMtimeChangeDelay =
withTempDirectory silent "." "calibration-" $ \dir -> do
let fileName = dir </> "probe"
mtimes <- for [1..25] $ \(i::Int) -> time $ do
mtimes <- sampleMtimes (dir </> "probe")
let mtimeChange = maximum mtimes
mtimeChange' = min 1000000 $ (max 10000 mtimeChange) * 2
return (mtimeChange, mtimeChange')
where

sampleMtimes :: FilePath -> IO [Int]
sampleMtimes fileName = do
mtime0 <- sampleMtime 1 fileName
-- Some filesystems (e.g. HFS+ on Mac) store mtimes with 1
-- second resolution. Bail out directly instead of spinning in
-- that case.
if (mtime0 >= 1000000) || (1000000 - mtime0 <= 10000)
then return [mtime0]
else do mtimes <- for [2..25] $ \(i::Int) -> sampleMtime i fileName
return (mtime0:mtimes)

sampleMtime :: Int -> FilePath -> IO Int
sampleMtime i fileName = time $ do
writeFile fileName $ show i
t0 <- getModTime fileName
let spin j = do
writeFile fileName $ show (i,j)
t1 <- getModTime fileName
unless (t0 < t1) (spin $ j + 1)
spin (0::Int)
let mtimeChange = maximum mtimes
mtimeChange' = min 1000000 $ (max 10000 mtimeChange) * 2
return (mtimeChange, mtimeChange')
where

time :: IO () -> IO Int
time act = do
t0 <- getCurrentTime
Expand Down
17 changes: 15 additions & 2 deletions Cabal/tests/UnitTests/Distribution/Compat/Time.hs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
{-# LANGUAGE BangPatterns #-}
module UnitTests.Distribution.Compat.Time (tests) where

import Control.Concurrent (threadDelay)
import Data.Time (diffUTCTime, getCurrentTime)
import System.FilePath

import Distribution.Simple.Utils (withTempDirectory)
Expand All @@ -13,8 +15,10 @@ import Test.Tasty.HUnit

tests :: Int -> [TestTree]
tests mtimeChange =
[ testCase "getModTime has sub-second resolution" $ getModTimeTest mtimeChange
, testCase "getCurTime works as expected" $ getCurTimeTest mtimeChange
[ testCase "getModTime has expected resolution" $ getModTimeTest mtimeChange
, testCase "getCurTime works as expected" $ getCurTimeTest mtimeChange
, testCase "calibrateMtimeChangeDelay is reasonably fast"
$ calibrateMtimeChangeDelayTest
]

getModTimeTest :: Int -> Assertion
Expand Down Expand Up @@ -47,3 +51,12 @@ getCurTimeTest mtimeChange =
assertBool ("expected current time (" ++ show t1
++ ") to be earlier than file mtime (" ++ show t2 ++ ")")
(t1 < t2)

-- See #4230.
calibrateMtimeChangeDelayTest :: Assertion
calibrateMtimeChangeDelayTest = do
t0 <- getCurrentTime
(!_maxDelay, !_recDelay) <- calibrateMtimeChangeDelay
t1 <- getCurrentTime
assertBool "expected calibrateMtimeChangeDelay to take less than 2 seconds" $
(t1 `diffUTCTime` t0) < 2

0 comments on commit 9752f24

Please sign in to comment.