[Haskell-cafe] Timeout exceptions sometimes don't work, even in pure (non FFI) code

Daniel Fischer daniel.is.fischer at googlemail.com
Tue Feb 1 17:18:04 CET 2011


On Tuesday 01 February 2011 16:40:43, Job Vranish wrote:
> I'm trying to test some properties with quickcheck. If these tests fail,
> they will almost certainly fail by nontermination.
> I've been using the 'within' function to catch these nontermination
> cases. However, I was surprised to find that this doesn't always work.
> 'within' uses the 'timeout' function under the hood. Here is an example
> that demonstrates the problem:
>
> import System.Timeout
> import Control.Exception
>
> works :: Int -> Int
> works x = sum $ cycle [x]
>
> doesntWork :: Int -> Int
> doesntWork x = last $ cycle [x]
>
> test1 = timeout 1 $ evaluate $ works 5 == 5            -- terminates
> test2 = timeout 1 $ evaluate $ doesntWork 5 == 5   -- never terminates
>
>
> test1 returns Nothing as expected, but test2 never terminates. Why?

When compiled with optimisations, works doesn't terminate either.

I believe it's because works actually does some work and allocations 
(without optimisations), while doesntWork is a non-allocating loop.
GHC only makes context switches on allocations, so with a non-allocating 
loop, the timeout thread never gets to run to see whether the time limit is 
exceeded.
Without optimisations, sum allocates thunks, with optimisations it becomes 
a tight loop not hitting the heap. `last $ cycle [x]' becomes a tight loop 
even without optimisations.

>
> I thought timeout exceptions are supposed to always work with pure (non
> FFI) Haskell code.
> Is there any way I can work around this?

Make sure your tests always allocate, or write them as IO stuff and call 
yield (or threadDelay) within the loops to let GHC make some context 
switches.

>
> I'm using ghc 6.12.2
>
> Thanks,
>
> - Job




More information about the Haskell-Cafe mailing list