[Haskell-cafe] Re: [reactive] problem with unamb -- doesn't kill enough threads

Conal Elliott conal at conal.net
Fri Dec 19 14:17:15 EST 2008


Peter,

Thanks for digging.  In your results below, I see only three out of four
threads killed even in the best case.  Each time, there is no report of the
'sleep 2' thread being killed.

When I run your code on Linux (Ubuntu 8.10), everything looks great when run
under ghci.  If compiled, with and without -threaded and with and without
+RTS -N2, I sometimes get four kill messages and sometimes fewer.  In the
latter case, I don't know if the other threads aren't getting killed or if
they're killed but not reported.

For example (removing messages other than "Killed"):

    conal at compy-doble:~/Haskell/Misc$ rm Threads.o ; ghc Threads.hs
-threaded -o Threads && ./Threads +RTS -N2
    Killed ThreadId 5
    Killed ThreadId 4

    conal at compy-doble:~/Haskell/Misc$ ./Threads +RTS -N2
    Killed ThreadId 5
    Killed ThreadId 4
    Killed ThreadId 7
    Killed ThreadId 6

    conal at compy-doble:~/Haskell/Misc$ ./Threads +RTS -N2
    Killed ThreadId 5
    Killed ThreadId 7
    Killed ThreadId 4
    Killed ThreadId 6

    conal at compy-doble:~/Haskell/Misc$ ./Threads +RTS -N2
    Killed ThreadId 5
    Killed ThreadId 4

    conal at compy-doble:~/Haskell/Misc$

Simon -- does this behavior look like a GHC bug to you?

   - Conal

On Fri, Dec 19, 2008 at 9:45 AM, Peter Verswyvelen <bugfact at gmail.com>wrote:

> I played a bit the the bracket function that timeout uses, but got strange
> results (both on Windows and OSX).
>
> Ugly code fragment follows:
>
>
> -%<-------------------------------------------------------------------------------------------------
>
> import Prelude hiding (catch)
>
> import Control.Concurrent
> import Control.Concurrent.MVar
> import Control.Exception
> import System.IO
> import Data.Char
>
> withThread a b = bracket (forkIO a) kill (const b)
>     where
>       kill id = do
>         putStrLn ("Killing "++show id++"\n")
>         killThread id
>         putStrLn ("Killed "++show id++"\n")
>
> race a b = do
>     v <- newEmptyMVar
>     let t x = x >>= putMVar v
>     withThread (t a) $ withThread (t b) $ takeMVar v
>
> forkPut :: IO a -> MVar a -> IO ThreadId
> forkPut act v = forkIO ((act >>= putMVar v) `catch` uhandler `catch`
> bhandler)
>  where
>    uhandler (ErrorCall "Prelude.undefined") = return ()
>    uhandler err                             = throw err
>    bhandler BlockedOnDeadMVar               = return ()
>
> sleep n = do
>   tid <- myThreadId
>   putStrLn ("Sleeping "++show n++" sec on "++show tid++"\n")
>   threadDelay (n*1000000)
>   putStrLn ("Slept "++show n++" sec on "++show tid++"\n")
>
> f = sleep 2 `race` sleep 3
>
> g = f `race` sleep 1
>
> main = do
>   hSetBuffering stdout LineBuffering
>   g
>
>
> -%<-------------------------------------------------------------------------------------------------
>
> Here's the output when running with GHCI:
>
> C:\temp>runghc racetest
> Sleeping 1 sec on ThreadId 26
> Sleeping 2 sec on ThreadId 27
> Sleeping 3 sec on ThreadId 28
> Slept 1 sec on ThreadId 26
> Killing ThreadId 26
> Killed ThreadId 26
> Killing ThreadId 25
> Killed ThreadId 25
> Killing ThreadId 28
> Killed ThreadId 28
>
> Fine, all threads got killed.
>
> Here's the output from an EXE compiled with GHC -threaded, but run without
> +RTS -N2
>
> C:\temp> racetest
> Sleeping 1 sec on ThreadId 5
> Sleeping 3 sec on ThreadId 7
> Sleeping 2 sec on ThreadId 6
> Slept 1 sec on ThreadId 5
> Killing ThreadId 5
> Killed ThreadId 5
> Killing ThreadId 4
> Killed ThreadId 4
> Killing ThreadId 7
>
> So "Killed ThreadId 7" is not printed here. What did I do wrong?
>
> Here's the output from an EXE compiled with GHC -threaded, but run with
> +RTS -N2
>
> C:\temp> racetest +RTS -N2
> Sleeping 1 sec on ThreadId 5
> Sleeping 3 sec on ThreadId 7
> Sleeping 2 sec on ThreadId 6
> Slept 1 sec on ThreadId 5
>
> Killing ThreadId 5
> Killed ThreadId 5
> Killing ThreadId 4
> Killed ThreadId 4
> Killing ThreadId 7
> Killed ThreadId 7
>
> This works again.
>
> Is this intended behavior?
>
> Cheers,
> Peter Verswyvelen
> CTO - Anygma
>
> On Fri, Dec 19, 2008 at 10:48 AM, Simon Marlow <marlowsd at gmail.com> wrote:
>
>> Sounds like you should use an exception handler so that when the parent
>> dies it also kills its children.  Be very careful with race conditions ;-)
>>
>> For a good example of how to do this sort of thing, see
>>
>>
>> http://www.haskell.org/ghc/docs/latest/html/libraries/base/System-Timeout.html
>>
>> the docs are sadly missing the source links at the moment, I'm not sure
>> why, but you can find the source in
>>
>> http://darcs.haskell.org/packages/base/System/Timeout.hs
>>
>> Cheers,
>>        Simon
>>
>> Conal Elliott wrote:
>>
>>> (I'm broadening the discussion to include haskell-cafe.)
>>>
>>> Andy -- What do you mean by "handling all thread forking locally"?
>>>
>>>  - Conal
>>>
>>> On Thu, Dec 18, 2008 at 1:57 PM, Andy Gill <andygill at ku.edu <mailto:
>>> andygill at ku.edu>> wrote:
>>>
>>>    Conal, et. al,
>>>
>>>    I was looking for exactly this about 6~9 months ago. I got the
>>>    suggestion to pose it as a challenge
>>>    to the community by Duncan Coutts. What you need is thread groups,
>>>     where for a ThreadId, you can send a signal
>>>    to all its children, even missing generations if needed.
>>>    I know of no way to fix this at the Haskell level without handling
>>>    all thread forking locally.
>>>    Perhaps a ICFP paper about the pending implementation :-) but I'm
>>>    not sure about the research content here.
>>>
>>>    Again, there is something deep about values with lifetimes.
>>>    Andy Gill
>>>
>>>
>>>    On Dec 18, 2008, at 3:43 PM, Conal Elliott wrote:
>>>
>>>     I realized in the shower this morning that there's a serious flaw
>>>>    in my unamb implementation as described in
>>>>
>>>> http://conal.net/blog/posts/functional-concurrency-with-unambiguous-choice.
>>>>    I'm looking for ideas for fixing the flaw.  Here's the code for
>>>>    racing computations:
>>>>
>>>>        race :: IO a -> IO a -> IO a
>>>>        a `race` b = do v  <- newEmptyMVar
>>>>                        ta <- forkPut a v
>>>>                        tb <- forkPut b v
>>>>                        x  <- takeMVar  v
>>>>                        killThread ta
>>>>                        killThread tb
>>>>                        return x
>>>>
>>>>        forkPut :: IO a -> MVar a -> IO ThreadId
>>>>        forkPut act v = forkIO ((act >>= putMVar v) `catch` uhandler
>>>>    `catch` bhandler)
>>>>         where
>>>>           uhandler (ErrorCall "Prelude.undefined") = return ()
>>>>           uhandler err                             = throw err
>>>>           bhandler BlockedOnDeadMVar               = return ()
>>>>
>>>>    The problem is that each of the threads ta and tb may have spawned
>>>>    other threads, directly or indirectly.  When I kill them, they
>>>>    don't get a chance to kill their sub-threads.
>>>>
>>>>    Perhaps I want some form of garbage collection of threads, perhaps
>>>>    akin to Henry Baker's paper "The Incremental Garbage Collection of
>>>>    Processes".  As with memory GC, dropping one consumer would
>>>>    sometimes result is cascading de-allocations.  That cascade is
>>>>    missing from my implementation.
>>>>
>>>>    Or maybe there's a simple and dependable manual solution,
>>>>    enhancing the method above.
>>>>
>>>>    Any ideas?
>>>>
>>>>       - Conal
>>>>
>>>>
>>>>    _______________________________________________
>>>>    Reactive mailing list
>>>>    Reactive at haskell.org <mailto:Reactive at haskell.org>
>>>>    http://www.haskell.org/mailman/listinfo/reactive
>>>>
>>>
>>>
>>>
>>> ------------------------------------------------------------------------
>>>
>>> _______________________________________________
>>> Haskell-Cafe mailing list
>>> Haskell-Cafe at haskell.org
>>> http://www.haskell.org/mailman/listinfo/haskell-cafe
>>>
>>
>> _______________________________________________
>> Haskell-Cafe mailing list
>> Haskell-Cafe at haskell.org
>> http://www.haskell.org/mailman/listinfo/haskell-cafe
>>
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://www.haskell.org/pipermail/haskell-cafe/attachments/20081219/6d2eac74/attachment.htm


More information about the Haskell-Cafe mailing list