Discussion:
[Haskell-cafe] Timing out a pure evaluation of an expression I did not write myself
Ryan Reich
2018-11-17 23:21:53 UTC
Permalink
I want to time out a pure computation. My experience, and that described
in various previous questions here and elsewhere (the best of which is
https://mail.haskell.org/pipermail/haskell-cafe/2011-February/088820.html),
is that this doesn't always work: for instance,
timeout 1 $ evaluate $ let x = 0 : x in last x
does not time out because, apparently, the fact that the expression
evaluates in constant space (i.e. never allocates) means that it never
yields to the timeout monitor thread that would kill it.

The solution that is described in the other iterations is to embed
checkpoints in the expression that do allocate, giving the RTS a chance to
switch contexts. However, in my application, the expression is /arbitrary/
and I do not have the freedom to inject alterations into it. (Don't argue
this point, please. The expression is arbitrary.)

How can I time out a tight loop like the above? Clearly, it can be done,
because I can, say, alt-tab over to another terminal and kill the process,
which exploits the operating system's more aggressively pre-emptive
scheduling. Is there a solution using bound threads, say 'forkOS' instead
of 'forkIO' in the implementation of 'timeout'? Unix signals? Some
FFI-based workaround? Etc. Keep in mind that notwithstanding that
comment, I don't actually want to kill the whole process, but just the one
evaluation.

Thanks in advance,
Ryan Reich
Daniel Díaz Casanueva
2018-11-18 00:51:58 UTC
Permalink
Hello Ryan.

Try evaluating the expression to normal form instead of weak head normal
Post by Ryan Reich
timeout 1 $ evaluate $ force $ let x = 0 : x in last x
The function `force` comes from the deepseq package. You can read the docs
here:
http://hackage.haskell.org/package/deepseq-1.4.4.0/docs/Control-DeepSeq.html

I hope that helps.

Best regards,
Daniel
Post by Ryan Reich
I want to time out a pure computation. My experience, and that described
in various previous questions here and elsewhere (the best of which is
https://mail.haskell.org/pipermail/haskell-cafe/2011-February/088820.html),
is that this doesn't always work: for instance,
timeout 1 $ evaluate $ let x = 0 : x in last x
does not time out because, apparently, the fact that the expression
evaluates in constant space (i.e. never allocates) means that it never
yields to the timeout monitor thread that would kill it.
The solution that is described in the other iterations is to embed
checkpoints in the expression that do allocate, giving the RTS a chance to
switch contexts. However, in my application, the expression is /arbitrary/
and I do not have the freedom to inject alterations into it. (Don't argue
this point, please. The expression is arbitrary.)
How can I time out a tight loop like the above? Clearly, it can be done,
because I can, say, alt-tab over to another terminal and kill the process,
which exploits the operating system's more aggressively pre-emptive
scheduling. Is there a solution using bound threads, say 'forkOS' instead
of 'forkIO' in the implementation of 'timeout'? Unix signals? Some
FFI-based workaround? Etc. Keep in mind that notwithstanding that
comment, I don't actually want to kill the whole process, but just the one
evaluation.
Thanks in advance,
Ryan Reich
_______________________________________________
Haskell-Cafe mailing list
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Only members subscribed via the mailman list are allowed to post.
Daniel Díaz Casanueva
2018-11-18 00:55:56 UTC
Permalink
Actually, after reading the question again, it seems like my response
wasn't quite right. You are not actually building the list. In that case, I
am as confused as you. :)

Sorry!

Am So., 18. Nov. 2018 um 01:51 Uhr schrieb Daniel Díaz Casanueva <
Post by Daniel Díaz Casanueva
Hello Ryan.
Try evaluating the expression to normal form instead of weak head normal
Post by Ryan Reich
timeout 1 $ evaluate $ force $ let x = 0 : x in last x
The function `force` comes from the deepseq package. You can read the docs
http://hackage.haskell.org/package/deepseq-1.4.4.0/docs/Control-DeepSeq.html
I hope that helps.
Best regards,
Daniel
Am So., 18. Nov. 2018 um 00:22 Uhr schrieb Ryan Reich <
Post by Ryan Reich
I want to time out a pure computation. My experience, and that described
in various previous questions here and elsewhere (the best of which is
https://mail.haskell.org/pipermail/haskell-cafe/2011-February/088820.html),
is that this doesn't always work: for instance,
timeout 1 $ evaluate $ let x = 0 : x in last x
does not time out because, apparently, the fact that the expression
evaluates in constant space (i.e. never allocates) means that it never
yields to the timeout monitor thread that would kill it.
The solution that is described in the other iterations is to embed
checkpoints in the expression that do allocate, giving the RTS a chance to
switch contexts. However, in my application, the expression is /arbitrary/
and I do not have the freedom to inject alterations into it. (Don't argue
this point, please. The expression is arbitrary.)
How can I time out a tight loop like the above? Clearly, it can be done,
because I can, say, alt-tab over to another terminal and kill the process,
which exploits the operating system's more aggressively pre-emptive
scheduling. Is there a solution using bound threads, say 'forkOS' instead
of 'forkIO' in the implementation of 'timeout'? Unix signals? Some
FFI-based workaround? Etc. Keep in mind that notwithstanding that
comment, I don't actually want to kill the whole process, but just the one
evaluation.
Thanks in advance,
Ryan Reich
_______________________________________________
Haskell-Cafe mailing list
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Only members subscribed via the mailman list are allowed to post.
Ryan Reich
2018-11-18 00:57:56 UTC
Permalink
I was just about to reply with an observation to that effect :) The place
that I'd want to put 'force' is actually inside the 'let' clause, which of
course you can't do just by applying a function. The expression as a whole
is just an Integer.
Post by Daniel Díaz Casanueva
Actually, after reading the question again, it seems like my response
wasn't quite right. You are not actually building the list. In that case, I
am as confused as you. :)
Sorry!
Am So., 18. Nov. 2018 um 01:51 Uhr schrieb Daniel Díaz Casanueva <
Post by Daniel Díaz Casanueva
Hello Ryan.
Try evaluating the expression to normal form instead of weak head normal
Post by Ryan Reich
timeout 1 $ evaluate $ force $ let x = 0 : x in last x
The function `force` comes from the deepseq package. You can read the
http://hackage.haskell.org/package/deepseq-1.4.4.0/docs/Control-DeepSeq.html
I hope that helps.
Best regards,
Daniel
Am So., 18. Nov. 2018 um 00:22 Uhr schrieb Ryan Reich <
Post by Ryan Reich
I want to time out a pure computation. My experience, and that
described in various previous questions here and elsewhere (the best of
which is
https://mail.haskell.org/pipermail/haskell-cafe/2011-February/088820.html),
is that this doesn't always work: for instance,
timeout 1 $ evaluate $ let x = 0 : x in last x
does not time out because, apparently, the fact that the expression
evaluates in constant space (i.e. never allocates) means that it never
yields to the timeout monitor thread that would kill it.
The solution that is described in the other iterations is to embed
checkpoints in the expression that do allocate, giving the RTS a chance to
switch contexts. However, in my application, the expression is /arbitrary/
and I do not have the freedom to inject alterations into it. (Don't argue
this point, please. The expression is arbitrary.)
How can I time out a tight loop like the above? Clearly, it can be
done, because I can, say, alt-tab over to another terminal and kill the
process, which exploits the operating system's more aggressively
pre-emptive scheduling. Is there a solution using bound threads, say
'forkOS' instead of 'forkIO' in the implementation of 'timeout'? Unix
signals? Some FFI-based workaround? Etc. Keep in mind that
notwithstanding that comment, I don't actually want to kill the whole
process, but just the one evaluation.
Thanks in advance,
Ryan Reich
_______________________________________________
Haskell-Cafe mailing list
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Only members subscribed via the mailman list are allowed to post.
a***@gmail.com
2018-11-18 08:22:08 UTC
Permalink
Post by Ryan Reich
I want to time out a pure computation. My experience, and that
described in various previous questions here and elsewhere (the best
of which is
https://mail.haskell.org/pipermail/haskell-cafe/2011-February/088820.html
), is that this doesn't always work: for instance,
timeout 1 $ evaluate $ let x = 0 : x in last x
does not time out because, apparently, the fact that the expression
evaluates in constant space (i.e. never allocates) means that it
never yields to the timeout monitor thread that would kill it.
The solution that is described in the other iterations is to embed
checkpoints in the expression that do allocate, giving the RTS a
chance to switch contexts. However, in my application, the
expression is /arbitrary/ and I do not have the freedom to inject
alterations into it. (Don't argue this point, please. The
expression is arbitrary.)
How can I time out a tight loop like the above? Clearly, it can be
done, because I can, say, alt-tab over to another terminal and kill
the process, which exploits the operating system's more aggressively
pre-emptive scheduling. Is there a solution using bound threads, say
'forkOS' instead of 'forkIO' in the implementation of 'timeout'?
Unix signals? Some FFI-based workaround? Etc. Keep in mind that
notwithstanding that comment, I don't actually want to kill the whole
process, but just the one evaluation.
Thanks in advance,
Ryan Reich
If you are using GHC, the -fno-omit-yields compiler option might be of
help, which does not optimize out the allocation check that is also
used for interrupting threads.

See also:
https://stackoverflow.com/questions/34317730/haskell-timeout-diverging-computation

Are you using the threaded runtime (GHC option -threaded)?

hope this helps, Arjen
Ryan Reich
2018-11-19 19:26:17 UTC
Permalink
I suppose my question concerns the more general question of how to create
OS-managed threads in GHC. As I understand it, GHC's concurrency model
only exposes its RTS-managed internal threads, which are distributed
somehow to OS threads by the scheduler, and this is the source of my
timeout problem, because the scheduler never runs. Contrast this with
plain Linux C, where we can do something with pthread_create to call a
function in an OS-managed thread directly. I would have expected there to
be a corresponding operation in GHC Haskell ("bound threads" seem not to be
it, as they are still scheduled by the RTS) but it does not appear that
there is. Is this because of the need to keep the runtime unified?
Because it seems strange that we are prevented from operating truly
independent threads.

Ryan
Post by Ryan Reich
I want to time out a pure computation. My experience, and that described
in various previous questions here and elsewhere (the best of which is
https://mail.haskell.org/pipermail/haskell-cafe/2011-February/088820.html),
is that this doesn't always work: for instance,
timeout 1 $ evaluate $ let x = 0 : x in last x
does not time out because, apparently, the fact that the expression
evaluates in constant space (i.e. never allocates) means that it never
yields to the timeout monitor thread that would kill it.
The solution that is described in the other iterations is to embed
checkpoints in the expression that do allocate, giving the RTS a chance to
switch contexts. However, in my application, the expression is /arbitrary/
and I do not have the freedom to inject alterations into it. (Don't argue
this point, please. The expression is arbitrary.)
How can I time out a tight loop like the above? Clearly, it can be done,
because I can, say, alt-tab over to another terminal and kill the process,
which exploits the operating system's more aggressively pre-emptive
scheduling. Is there a solution using bound threads, say 'forkOS' instead
of 'forkIO' in the implementation of 'timeout'? Unix signals? Some
FFI-based workaround? Etc. Keep in mind that notwithstanding that
comment, I don't actually want to kill the whole process, but just the one
evaluation.
Thanks in advance,
Ryan Reich
Viktor Dukhovni
2018-11-19 20:26:51 UTC
Permalink
Post by Ryan Reich
I suppose my question concerns the more general question of how to create
OS-managed threads in GHC. [...]
I would have expected there to
be a corresponding operation in GHC Haskell ("bound threads" seem not to be
it, as they are still scheduled by the RTS) but it does not appear that
there is. Is this because of the need to keep the runtime unified?
Because it seems strange that we are prevented from operating truly
independent threads.
I was just reading:

https://cs.nyu.edu/~mwalfish/classes/14fa/ref/boehm05threads.pdf

which may offer some insight. The runtime needs to be able to
provide a consistent memory model to threads. Just running something
in an OS thread that's managed by the RTS could make that difficult,
and it is not clear how that cooperates with garbage collection.

But to your point, when adding threads to your example, I find that
the infinite loop then runs concurrently in all the threads, and
the timeout never happens. While:

Replacing: let x = 0:x in last x
with: let ! x = 0 + x in x

does make timeout work, so it is not entirely obvious which pure
computations can be timed out.
--
Viktor.
Carter Schonwald
2018-11-27 00:13:38 UTC
Permalink
so one way to handle no allocation things playing nicely with the scheduler
is to compile haskell code with -fno-omit-yields

this will generate code which has scheduler yields even in loops which dont
allocate

cheers!
-Carter
Post by Viktor Dukhovni
Post by Ryan Reich
I suppose my question concerns the more general question of how to create
OS-managed threads in GHC. [...]
I would have expected there to
be a corresponding operation in GHC Haskell ("bound threads" seem not to
be
Post by Ryan Reich
it, as they are still scheduled by the RTS) but it does not appear that
there is. Is this because of the need to keep the runtime unified?
Because it seems strange that we are prevented from operating truly
independent threads.
https://cs.nyu.edu/~mwalfish/classes/14fa/ref/boehm05threads.pdf
which may offer some insight. The runtime needs to be able to
provide a consistent memory model to threads. Just running something
in an OS thread that's managed by the RTS could make that difficult,
and it is not clear how that cooperates with garbage collection.
But to your point, when adding threads to your example, I find that
the infinite loop then runs concurrently in all the threads, and
Replacing: let x = 0:x in last x
with: let ! x = 0 + x in x
does make timeout work, so it is not entirely obvious which pure
computations can be timed out.
--
Viktor.
_______________________________________________
Haskell-Cafe mailing list
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Only members subscribed via the mailman list are allowed to post.
Ryan Reich
2018-11-28 17:39:01 UTC
Permalink
Thanks for the suggestion, which Arjen made also. Unfortunately, it does
not appear to help. See this simple program:

-- Loop.hs
import Control.Exception
import System.Timeout

main :: IO (Maybe Integer)
main = timeout 100000 $ evaluate $ last $ repeat 0
-- end

With either GHC invocation "stack exec ghc Loop[ -- -fno-omit-yields]",
running ./Loop fails to terminate (it should do so in 0.1s).

Based only on the very terse description of that flag in the User's Guide,
and its name, I think the problem is simply that GHC doesn't normally
*generate* yields in that loop, so there's nothing not to omit.
Post by Carter Schonwald
so one way to handle no allocation things playing nicely with the
scheduler is to compile haskell code with -fno-omit-yields
this will generate code which has scheduler yields even in loops which
dont allocate
cheers!
-Carter
Post by Ryan Reich
Post by Ryan Reich
I suppose my question concerns the more general question of how to
create
Post by Ryan Reich
OS-managed threads in GHC. [...]
I would have expected there to
be a corresponding operation in GHC Haskell ("bound threads" seem not
to be
Post by Ryan Reich
it, as they are still scheduled by the RTS) but it does not appear that
there is. Is this because of the need to keep the runtime unified?
Because it seems strange that we are prevented from operating truly
independent threads.
https://cs.nyu.edu/~mwalfish/classes/14fa/ref/boehm05threads.pdf
which may offer some insight. The runtime needs to be able to
provide a consistent memory model to threads. Just running something
in an OS thread that's managed by the RTS could make that difficult,
and it is not clear how that cooperates with garbage collection.
But to your point, when adding threads to your example, I find that
the infinite loop then runs concurrently in all the threads, and
Replacing: let x = 0:x in last x
with: let ! x = 0 + x in x
does make timeout work, so it is not entirely obvious which pure
computations can be timed out.
--
Viktor.
_______________________________________________
Haskell-Cafe mailing list
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Only members subscribed via the mailman list are allowed to post.
_______________________________________________
Haskell-Cafe mailing list
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Only members subscribed via the mailman list are allowed to post.
Viktor Dukhovni
2018-11-29 01:10:21 UTC
Permalink
Post by Ryan Reich
Thanks for the suggestion, which Arjen made also. Unfortunately, it does
-- Loop.hs
import Control.Exception
import System.Timeout
main :: IO (Maybe Integer)
main = timeout 100000 $ evaluate $ last $ repeat 0
-- end
With either GHC invocation "stack exec ghc Loop[ -- -fno-omit-yields]",
running ./Loop fails to terminate (it should do so in 0.1s).
Based only on the very terse description of that flag in the User's Guide,
and its name, I think the problem is simply that GHC doesn't normally
*generate* yields in that loop, so there's nothing not to omit.
It times out for me with GHC 8.4.4 on FreeBSD 11.2, and "ghc -O -fno-omit-yields"
and does not time out with "ghc -O":

$ cat /tmp/foo.hs
import Control.Exception
import System.Timeout

main :: IO (Maybe Integer)
main = timeout 1000000 $ evaluate $ last $ repeat 0

$ ghc -O -fno-omit-yields /tmp/foo.hs
[1 of 1] Compiling Main ( /tmp/foo.hs, /tmp/foo.o ) [Optimisation flags changed]
Linking /tmp/foo ...

$ time /tmp/foo

real 0m1.033s
user 0m1.025s
sys 0m0.008s

$ rm /tmp/foo
$ ghc -O /tmp/foo.hs
[1 of 1] Compiling Main ( /tmp/foo.hs, /tmp/foo.o ) [Optimisation flags changed]
Linking /tmp/foo ...

$ time /tmp/foo
^C^C

real 0m5.864s
user 0m5.857s
sys 0m0.000s

On MacOS X with GHC 7.10.3, it does not time out either way. Perhaps
some versions of GHC don't make the timeout possible.
--
Viktor.
Ryan Reich
2018-11-29 02:40:20 UTC
Permalink
I expected something like that. I'm all the way back in ghc-8.2.2, but I
think what this really shows is that the flag is unreliable and
version-dependent. Unfortunately there doesn't seem to be a precise
specification of where yield points should appear, and therefore, where
they might be not-omitted.
Post by Ryan Reich
Post by Ryan Reich
Thanks for the suggestion, which Arjen made also. Unfortunately, it does
-- Loop.hs
import Control.Exception
import System.Timeout
main :: IO (Maybe Integer)
main = timeout 100000 $ evaluate $ last $ repeat 0
-- end
With either GHC invocation "stack exec ghc Loop[ -- -fno-omit-yields]",
running ./Loop fails to terminate (it should do so in 0.1s).
Based only on the very terse description of that flag in the User's
Guide,
Post by Ryan Reich
and its name, I think the problem is simply that GHC doesn't normally
*generate* yields in that loop, so there's nothing not to omit.
It times out for me with GHC 8.4.4 on FreeBSD 11.2, and "ghc -O -fno-omit-yields"
$ cat /tmp/foo.hs
import Control.Exception
import System.Timeout
main :: IO (Maybe Integer)
main = timeout 1000000 $ evaluate $ last $ repeat 0
$ ghc -O -fno-omit-yields /tmp/foo.hs
[1 of 1] Compiling Main ( /tmp/foo.hs, /tmp/foo.o )
[Optimisation flags changed]
Linking /tmp/foo ...
$ time /tmp/foo
real 0m1.033s
user 0m1.025s
sys 0m0.008s
$ rm /tmp/foo
$ ghc -O /tmp/foo.hs
[1 of 1] Compiling Main ( /tmp/foo.hs, /tmp/foo.o )
[Optimisation flags changed]
Linking /tmp/foo ...
$ time /tmp/foo
^C^C
real 0m5.864s
user 0m5.857s
sys 0m0.000s
On MacOS X with GHC 7.10.3, it does not time out either way. Perhaps
some versions of GHC don't make the timeout possible.
--
Viktor.
_______________________________________________
Haskell-Cafe mailing list
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Only members subscribed via the mailman list are allowed to post.
Erik Hesselink
2018-11-29 10:15:49 UTC
Permalink
Hi Ryan,

I'm not quite sure of your use case, but lambdabot/mueval do something like
this I think. From a brief look at the source [1] it seems they compute in
a separate thread, then in another thread, wait until the timeout then kill
the computation thread. Would something like that work for you?

Erik

[1]
https://github.com/gwern/mueval/blob/09b6a7aa5a25c4115442ea2e6ae0c2db557007f8/Mueval/Parallel.hs
Post by Ryan Reich
I expected something like that. I'm all the way back in ghc-8.2.2, but I
think what this really shows is that the flag is unreliable and
version-dependent. Unfortunately there doesn't seem to be a precise
specification of where yield points should appear, and therefore, where
they might be not-omitted.
Post by Ryan Reich
Post by Ryan Reich
Thanks for the suggestion, which Arjen made also. Unfortunately, it
does
Post by Ryan Reich
-- Loop.hs
import Control.Exception
import System.Timeout
main :: IO (Maybe Integer)
main = timeout 100000 $ evaluate $ last $ repeat 0
-- end
With either GHC invocation "stack exec ghc Loop[ -- -fno-omit-yields]",
running ./Loop fails to terminate (it should do so in 0.1s).
Based only on the very terse description of that flag in the User's
Guide,
Post by Ryan Reich
and its name, I think the problem is simply that GHC doesn't normally
*generate* yields in that loop, so there's nothing not to omit.
It times out for me with GHC 8.4.4 on FreeBSD 11.2, and "ghc -O -fno-omit-yields"
$ cat /tmp/foo.hs
import Control.Exception
import System.Timeout
main :: IO (Maybe Integer)
main = timeout 1000000 $ evaluate $ last $ repeat 0
$ ghc -O -fno-omit-yields /tmp/foo.hs
[1 of 1] Compiling Main ( /tmp/foo.hs, /tmp/foo.o )
[Optimisation flags changed]
Linking /tmp/foo ...
$ time /tmp/foo
real 0m1.033s
user 0m1.025s
sys 0m0.008s
$ rm /tmp/foo
$ ghc -O /tmp/foo.hs
[1 of 1] Compiling Main ( /tmp/foo.hs, /tmp/foo.o )
[Optimisation flags changed]
Linking /tmp/foo ...
$ time /tmp/foo
^C^C
real 0m5.864s
user 0m5.857s
sys 0m0.000s
On MacOS X with GHC 7.10.3, it does not time out either way. Perhaps
some versions of GHC don't make the timeout possible.
--
Viktor.
_______________________________________________
Haskell-Cafe mailing list
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Only members subscribed via the mailman list are allowed to post.
_______________________________________________
Haskell-Cafe mailing list
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Only members subscribed via the mailman list are allowed to post.
Carter Schonwald
2018-11-29 14:24:50 UTC
Permalink
Yeah that’s probably a better approach.
Post by Erik Hesselink
Hi Ryan,
I'm not quite sure of your use case, but lambdabot/mueval do something
like this I think. From a brief look at the source [1] it seems they
compute in a separate thread, then in another thread, wait until the
timeout then kill the computation thread. Would something like that work
for you?
Erik
[1]
https://github.com/gwern/mueval/blob/09b6a7aa5a25c4115442ea2e6ae0c2db557007f8/Mueval/Parallel.hs
Post by Ryan Reich
I expected something like that. I'm all the way back in ghc-8.2.2, but I
think what this really shows is that the flag is unreliable and
version-dependent. Unfortunately there doesn't seem to be a precise
specification of where yield points should appear, and therefore, where
they might be not-omitted.
Post by Ryan Reich
Post by Ryan Reich
Thanks for the suggestion, which Arjen made also. Unfortunately, it
does
Post by Ryan Reich
-- Loop.hs
import Control.Exception
import System.Timeout
main :: IO (Maybe Integer)
main = timeout 100000 $ evaluate $ last $ repeat 0
-- end
With either GHC invocation "stack exec ghc Loop[ -- -fno-omit-yields]",
running ./Loop fails to terminate (it should do so in 0.1s).
Based only on the very terse description of that flag in the User's
Guide,
Post by Ryan Reich
and its name, I think the problem is simply that GHC doesn't normally
*generate* yields in that loop, so there's nothing not to omit.
It times out for me with GHC 8.4.4 on FreeBSD 11.2, and "ghc -O -fno-omit-yields"
$ cat /tmp/foo.hs
import Control.Exception
import System.Timeout
main :: IO (Maybe Integer)
main = timeout 1000000 $ evaluate $ last $ repeat 0
$ ghc -O -fno-omit-yields /tmp/foo.hs
[1 of 1] Compiling Main ( /tmp/foo.hs, /tmp/foo.o )
[Optimisation flags changed]
Linking /tmp/foo ...
$ time /tmp/foo
real 0m1.033s
user 0m1.025s
sys 0m0.008s
$ rm /tmp/foo
$ ghc -O /tmp/foo.hs
[1 of 1] Compiling Main ( /tmp/foo.hs, /tmp/foo.o )
[Optimisation flags changed]
Linking /tmp/foo ...
$ time /tmp/foo
^C^C
real 0m5.864s
user 0m5.857s
sys 0m0.000s
On MacOS X with GHC 7.10.3, it does not time out either way. Perhaps
some versions of GHC don't make the timeout possible.
--
Viktor.
_______________________________________________
Haskell-Cafe mailing list
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Only members subscribed via the mailman list are allowed to post.
_______________________________________________
Haskell-Cafe mailing list
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Only members subscribed via the mailman list are allowed to post.
_______________________________________________
Haskell-Cafe mailing list
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Only members subscribed via the mailman list are allowed to post.
Ian Denhardt
2018-11-29 14:28:43 UTC
Permalink
That should have the same problem; the exceptions can't be delivered
until the target thread reaches a yield. 'timeout' uses the same
underlying mechanism.

Obvious worrisome question: is it possible to use this to DoS
lambdabot/mueval?

Quoting Erik Hesselink (2018-11-29 05:15:49)
Post by Erik Hesselink
Hi Ryan,
I'm not quite sure of your use case, but lambdabot/mueval do something
like this I think. From a brief look at the source [1] it seems they
compute in a separate thread, then in another thread, wait until the
timeout then kill the computation thread. Would something like that
work for you?
Erik
[1]
[1]https://github.com/gwern/mueval/blob/09b6a7aa5a25c4115442ea2e6ae0c2d
b557007f8/Mueval/Parallel.hs
I expected something like that. I'm all the way back in ghc-8.2.2, but
I think what this really shows is that the flag is unreliable and
version-dependent. Unfortunately there doesn't seem to be a precise
specification of where yield points should appear, and therefore, where
they might be not-omitted.
Thanks for the suggestion, which Arjen made also.� Unfortunately,
it does
-- Loop.hs
import Control.Exception
import System.Timeout
main :: IO (Maybe Integer)
main = timeout 100000 $ evaluate $ last $ repeat 0
-- end
With either GHC invocation "stack exec ghc Loop[ --
-fno-omit-yields]",
running ./Loop fails to terminate (it should do so in 0.1s).
Based only on the very terse description of that flag in the
User's Guide,
and its name, I think the problem is simply that GHC doesn't
normally
*generate* yields in that loop, so there's nothing not to omit.
It times out for me with GHC 8.4.4 on FreeBSD 11.2, and "ghc -O -fno-omit-yields"
� � $ cat /tmp/foo.hs
� � import Control.Exception
� � import System.Timeout
� � main :: IO (Maybe Integer)
� � main = timeout 1000000 $ evaluate $ last $ repeat 0
� � $ ghc -O -fno-omit-yields /tmp/foo.hs
� � [1 of 1] Compiling Main� � � � � � � ( /tmp/foo.hs,
/tmp/foo.o ) [Optimisation flags changed]
� � Linking /tmp/foo ...
� � $ time /tmp/foo
� � real� � 0m1.033s
� � user� � 0m1.025s
� � sys� � � 0m0.008s
� � $ rm /tmp/foo
� � $ ghc -O /tmp/foo.hs
� � [1 of 1] Compiling Main� � � � � � � ( /tmp/foo.hs,
/tmp/foo.o ) [Optimisation flags changed]
� � Linking /tmp/foo ...
� � $ time /tmp/foo
� � ^C^C
� � real� � 0m5.864s
� � user� � 0m5.857s
� � sys� � � 0m0.000s
On MacOS X with GHC 7.10.3, it does not time out either way.�
Perhaps
some versions of GHC don't make the timeout possible.
--
� � � � Viktor.
_______________________________________________
Haskell-Cafe mailing list
[4]http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Only members subscribed via the mailman list are allowed to post.
_______________________________________________
Haskell-Cafe mailing list
[5]http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Only members subscribed via the mailman list are allowed to post.
Verweise
1. https://github.com/gwern/mueval/blob/09b6a7aa5a25c4115442ea2e6ae0c2db557007f8/Mueval/Parallel.hs
4. http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
5. http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Ryan Reich
2018-11-29 15:32:31 UTC
Permalink
I did actually construct a workaround where I compute in a separate
(forked, via System.Posix) *process *to time it out, then recomputed in the
main process to get the value. It's got a significant overhead but I'm not
sure yet if that is a real issue.

Using just a separate rts thread is how the standard timeout works; if you
mean that those guys use a separate OS thread that gets scheduled outside
the rts, that would be perfect. I'm just faking that with the fork.
Post by Erik Hesselink
Hi Ryan,
I'm not quite sure of your use case, but lambdabot/mueval do something
like this I think. From a brief look at the source [1] it seems they
compute in a separate thread, then in another thread, wait until the
timeout then kill the computation thread. Would something like that work
for you?
Erik
[1]
https://github.com/gwern/mueval/blob/09b6a7aa5a25c4115442ea2e6ae0c2db557007f8/Mueval/Parallel.hs
Post by Ryan Reich
I expected something like that. I'm all the way back in ghc-8.2.2, but I
think what this really shows is that the flag is unreliable and
version-dependent. Unfortunately there doesn't seem to be a precise
specification of where yield points should appear, and therefore, where
they might be not-omitted.
Post by Ryan Reich
Post by Ryan Reich
Thanks for the suggestion, which Arjen made also. Unfortunately, it
does
Post by Ryan Reich
-- Loop.hs
import Control.Exception
import System.Timeout
main :: IO (Maybe Integer)
main = timeout 100000 $ evaluate $ last $ repeat 0
-- end
With either GHC invocation "stack exec ghc Loop[ -- -fno-omit-yields]",
running ./Loop fails to terminate (it should do so in 0.1s).
Based only on the very terse description of that flag in the User's
Guide,
Post by Ryan Reich
and its name, I think the problem is simply that GHC doesn't normally
*generate* yields in that loop, so there's nothing not to omit.
It times out for me with GHC 8.4.4 on FreeBSD 11.2, and "ghc -O -fno-omit-yields"
$ cat /tmp/foo.hs
import Control.Exception
import System.Timeout
main :: IO (Maybe Integer)
main = timeout 1000000 $ evaluate $ last $ repeat 0
$ ghc -O -fno-omit-yields /tmp/foo.hs
[1 of 1] Compiling Main ( /tmp/foo.hs, /tmp/foo.o )
[Optimisation flags changed]
Linking /tmp/foo ...
$ time /tmp/foo
real 0m1.033s
user 0m1.025s
sys 0m0.008s
$ rm /tmp/foo
$ ghc -O /tmp/foo.hs
[1 of 1] Compiling Main ( /tmp/foo.hs, /tmp/foo.o )
[Optimisation flags changed]
Linking /tmp/foo ...
$ time /tmp/foo
^C^C
real 0m5.864s
user 0m5.857s
sys 0m0.000s
On MacOS X with GHC 7.10.3, it does not time out either way. Perhaps
some versions of GHC don't make the timeout possible.
--
Viktor.
_______________________________________________
Haskell-Cafe mailing list
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Only members subscribed via the mailman list are allowed to post.
_______________________________________________
Haskell-Cafe mailing list
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Only members subscribed via the mailman list are allowed to post.
Loading...