Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does `mask_` neutralize `timeout`?

I finally was able to track a weird bug I was having down to the (at least to me) surprising interaction between mask and timeout:

import System.Timeout
import Control.Exception

ack :: Int -> Int -> Int
ack m n | m == 0, n >= 0  = n + 1
        | m >  0, n == 0  = ack (m - 1) 1
        | m >  0, n >  0  = ack (m - 1) (ack m (n - 1))

tryack :: Int -> Int -> IO (Maybe Int)
tryack m n = timeout 100000 {- uS -} $ evaluate $ ack m n

main :: IO ()
main = do
  a <- tryack 3 11
  print a -- Nothing

  b <- mask_ $ tryack 3 11
  print b -- Just 16381 after a few seconds

This strikes me as a rather "non-compositional" interaction, as it means that if a library internally uses timeout, an externally applied mask somewhere up the call-chain may cause the library to malfunction.

So is this a (known) deficiency in the implementation of timeout or is it intentional?

like image 312
hvr Avatar asked Feb 23 '23 22:02

hvr


1 Answers

What does mask_ do?

Executes an IO computation with asynchronous exceptions masked. That is, any thread which attempts to raise an exception in the current thread with Control.Exception.throwTo will be blocked until asynchronous exceptions are unmasked again.

And what does timeout do?

Wrap an IO computation to time out and return Nothing in case no result is available within n microseconds (1/10^6 seconds).In case a result is available before the timeout expires, Just a is returned. ... A tricky implementation detail is the question of how to abort an IO computation. This combinator relies on asynchronous exceptions internally.

So... mask_ is preventing timeout from delivering its exceptions. That's just how it is.

You just can't use mask and have timeout work.

Perhaps a better approach would be to use a handler to catch anything but the exception that timeout uses?

like image 87
Don Stewart Avatar answered Feb 27 '23 06:02

Don Stewart