Hmm, that's good to know.
It looks like it also works for most data dependency cases.
works = let
a = b
b = a
in a
main = timeout 1 $ evaluate $ works
This terminates even with optimizations.
So I think I'll be able to work around it.
I wish timeout worked a little more consistently. This behavior is quite annoying.
Thanks for your help,
- Job
On Tuesday 01 February 2011 16:40:43, Job Vranish wrote:When compiled with optimisations, works doesn't terminate either.
> 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?
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.
Make sure your tests always allocate, or write them as IO stuff and call
>
> I thought timeout exceptions are supposed to always work with pure (non
> FFI) Haskell code.
> Is there any way I can work around this?
yield (or threadDelay) within the loops to let GHC make some context
switches.
>
> I'm using ghc 6.12.2
>
> Thanks,
>
> - Job