[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 
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 

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

More information about the Haskell-Cafe mailing list