[Haskell-cafe] Spurious program crashes

Tomasz Zielonka tomasz.zielonka at gmail.com
Tue Nov 22 02:09:04 EST 2005


On Mon, Nov 21, 2005 at 10:41:38PM +0000, Joel Reymont wrote:
> STM would complicate things too much for me. At least I think so. I  
> would love to use STM but I would need to fit it into "type  
> ScriptState = ErrorT String (StateT World IO)" just to use the  
> logger. I'm not THAT comfortable with monads.

I am talking about Software Transactional Memory, which is in
Control.Concurrent.STM. I think you confused it with State
Transformer Monad.

In your case STM would allow you to wait simultaneously on (T)MVar and
(T)Chan. It would look like this:

    logger :: TMVar () -> IO ()
    logger die =
        join $ atomically $
            (do x <- readTChan parent
                return $ do
                    putStrLn x
                    logger die)
            `orElse`
            (do takeTMVar die
                return (return ()))

but you have to modify the rest of code to use STM. I modified your
Conc.hs to use STM, but using the greater guarantees of STM you
could surely simplify it further (see the attached patch).

> Let me see if I understand you correctly... Are you saying that I  
> should be using getChanContents in the code below?

I am not proposing to use getChanContents. You are busy-waiting
on MVar and Chan. I just proposed a solution to stuff messages
and die-request into the same concurrency primitive, so you
can wait for both events using a single operation.

But you are right (below) that this bug doesn't explain the behaviour of
your program. It is "only" a performance bug.
 
> logger :: Handle -> MVar () -> IO ()
> logger h die =
>     do empty <- isEmptyChan parent
>        unless empty $ do x <- readChan parent
>                          putStrLn x
>                          hPutStrLn h x
>        alive <- isEmptyMVar die
>        when (alive || not empty) $ logger h die

> I think using Maybe is a great trick but I'm curious why so few  
> messages actually get taken out of the channel in the code above?

Actually, I am not sure. I just noticed that your code uses a bad
coding practice and could be improved. If I find some time I'll try to 
examine it more closely.

> Are  you saing that with all the checking it does not get to pull
> messages  out?

As it is, you code can impose a big performance penalty, but indeed
it shouldn't change the semantics. Perhaps I miss something.

> I see clearly how using Maybe with getChanContents will work out  
> perfectly. I don't understand why the above code is inefficient to  
> the point of printing just a few messages (out of hundreds) out on  
> Windows. I would like to understand it to avoid such mistakes in the  
> future.

Yes, this is strange. Perhaps we're both missing something obvious.

Best regards
Tomasz
-------------- next part --------------

New patches:

[Use STM in Conc.hs
Tomasz Zielonka <tomasz.zielonka at gmail.com>**20051122065752] {
hunk ./Conc.hs 6
+import Control.Concurrent.STM
hunk ./Conc.hs 15
-children = unsafePerformIO $ newMVar []
+children = unsafePerformIO $ atomically $ newMVar []
hunk ./Conc.hs 20
-parent = unsafePerformIO newChan
+parent = unsafePerformIO $ atomically newChan
hunk ./Conc.hs 28
-       writeChan parent $ stamp ++ ": " ++ (show tid) ++ ": " ++ a
+       atomically $ writeChan parent $ stamp ++ ": " ++ (show tid) ++ ": " ++ a
hunk ./Conc.hs 46
-    do empty <- isEmptyChan parent
-       unless empty $ do x <- readChan parent
-                         putStrLn x
-       alive <- isEmptyMVar die
-       when (alive || not empty) $ logger die
+    join $ atomically $
+        (do x <- readChan parent
+            return $ do
+                putStrLn x
+                logger die)
+        `orElse`
+        (do takeMVar die
+            return (return ()))
hunk ./Conc.hs 58
-       logDie <- newEmptyMVar
-       logDead <- newEmptyMVar
-       l <- forkIO (logger logDie `finally` putMVar logDead ())
+       logDie <- atomically newEmptyMVar
+       logDead <- atomically newEmptyMVar
+       l <- forkIO (logger logDie `finally` atomically (putMVar logDead ()))
hunk ./Conc.hs 63
-              do cs <- takeMVar children
+              do cs <- atomically (takeMVar children)
hunk ./Conc.hs 65
-                         []   -> do putMVar die ()
-                                    takeMVar dead
+                         []   -> do atomically $ do
+                                        putMVar die ()
+                                        takeMVar dead
hunk ./Conc.hs 69
-                         m:ms -> do putMVar children ms
-                                    takeMVar m
+                         m:ms -> do atomically $ do
+                                        putMVar children ms
+                                        takeMVar m
hunk ./Conc.hs 76
-    do mvar <- newEmptyMVar
-       childs <- takeMVar children
-       putMVar children (mvar:childs)
-       forkIO (io `finally` putMVar mvar ())
+    do mvar <- atomically newEmptyMVar
+       atomically $ do
+           childs <- takeMVar children
+           putMVar children (mvar:childs)
+       forkIO (io `finally` atomically (putMVar mvar ()))
hunk ./Conc.hs 84
+
replace ./Conc.hs [A-Za-z_0-9] Chan TChan
replace ./Conc.hs [A-Za-z_0-9] MVar TMVar
replace ./Conc.hs [A-Za-z_0-9] newChan newTChan
replace ./Conc.hs [A-Za-z_0-9] newEmptyMVar newEmptyTMVar
replace ./Conc.hs [A-Za-z_0-9] newMVar newTMVar
replace ./Conc.hs [A-Za-z_0-9] putMVar putTMVar
replace ./Conc.hs [A-Za-z_0-9] readChan readTChan
replace ./Conc.hs [A-Za-z_0-9] takeMVar takeTMVar
replace ./Conc.hs [A-Za-z_0-9] writeChan writeTChan
}

Context:

[free ssl handle, read chan 1 item/time, trace excepts
joelr at well.com**20051119135750] 
[added crash dumps to readme
joelr at well.com**20051119023717] 
[readme update
joelr at well.com**20051119013400] 
[server.pem
joelr at well.com**20051119011324] 
[updated readme
joelr at well.com**20051119010300] 
[really small readme 
joelr at well.com**20051119005259] 
[launch 5000 clients, ulimit -n is your friend!
joelr at well.com**20051119004928] 
[small comment
joelr at well.com**20051119003032] 
[fully working
joelr at well.com**20051119000227] 
[ssl handshake working, client blocking indefinitely
joelr at well.com**20051118230017] 
[base
joelr at well.com**20051118145241] 
Patch bundle hash:
663bcce41b9368ca66e8f674d7c8e6f4df3c3892


More information about the Haskell-Cafe mailing list