Faster timeout but is it correct?

Simon Marlow marlowsd at gmail.com
Fri Mar 25 15:56:40 CET 2011


On 17/02/2011 19:34, Bas van Dijk wrote:
> On 17 February 2011 13:09, Simon Marlow<marlowsd at gmail.com>  wrote:
>> uninterruptibleMask is quite unsavoury,
>
> Agreed, that's why I called this implementation "fragile" because it
> relies on the, not well known semantics, of interruptible operations.
>
>> I don't think we should use it here.
>
> I agree that it looks fishy. However the biggest part of the
> computation passed to uninterruptibleMask is running in the restored
> state. The only part that is running in uninterruptible masked state
> that may potentially block (and thus potentially deadlock) is the
> killThread in the exception handler. However since the timeout thread
> is running inside unsafeUnmask it is ensured that the ThreadKilled
> exception always gets thrown.
>
>> I can see why you used it though: the killThread in the main thread will
>> always win over the throwTo in the timeout thread, and that lets you avoid
>> the outer exception handler.
>
> Yes, and I think that the removal of the outer exception handler makes
> the code run so much faster.
>
>> Hmm, it makes me uncomfortable, but I can't find any actual bugs.  At the
>> very least it needs some careful commentary to explain how it works.
>
> Good point, I will add a comment with an explanation how it works.
>
> My brother Roel had an interesting idea how to make the code run even
> faster by replacing the Unique with the ThreadId of the timeout
> thread. I implemented it and it now runs 19 times faster than the
> original compared to the 13 times faster of my previous version.
> Here's the new implementation:
>
> newtype Timeout = Timeout ThreadId deriving (Eq, Typeable)
>
> instance Show Timeout where
>      show _ = "<<timeout>>"
>
> instance Exception Timeout
>
> timeout :: Int ->  IO a ->  IO (Maybe a)
> timeout n f
>      | n<   0    = fmap Just f
>      | n == 0    = return Nothing
>      | otherwise = do
>          myTid<- myThreadId
>          uninterruptibleMask $ \restore ->  do
>            tid<- unsafeUnmask $ forkIO $ do
>                      tid<- myThreadId
>                      threadDelay n
>                      throwTo myTid $ Timeout tid
>            (restore (fmap Just f)>>= \mb ->  killThread tid>>  return mb)
>              `catch` \e ->
>                  case fromException e of
>                    Just (Timeout tid') | tid' == tid ->  return Nothing
>                    _ ->  killThread tid>>  throwIO e
>
> It relies on ThreadIds being unique, but I believe this is the case
> because otherwise the throwTo operation will be nondeterministic,
> right?

(sorry for the late reply, just clearing my backlog)

This won't work in the case of nested timeouts, unless I'm mistaken.

Cheers,
	Simon



More information about the Libraries mailing list