2

I'm using Pipes.Concurrent to write a short GUI program with GTK. It's a minesweeper-esque game, so I'm constructing a grid of buttons.

I construct and connect my buttons with:

b <- buttonNewWithLabel (show $ adjacencies board ! i)
on b buttonActivated 
     $ void . atomically $ send output (ClickSignal i (mines board ! i))
return b

And I connect the pipe with:

(output, input)  <- spawn (latest (ClickSignal 0 False))

let run = do
        sig <- await
        case sig of
            ClickSignal i isMine ->
                if isMine
                then do
                    lift $ labelSetText info (show i ++ " -- LOSE!")
                else do 
                    lift $ labelSetText info (show i ++ " -- Ok")
                    run
            Empty -> do
                lift $ labelSetText info "Empty Case"
                run

void . forkIO $ do 
    runEffect $ fromInput input >-> run
    performGC

It runs almost as expected. But if I click on button 1, nothing happens. But if I press button 23, it will update the info label to "1..". If I click on another button, it will update to "23..", and so forth.

I suspect that either I'm failing to understand how concurrent pipes are meant to work on some level or lazy IO is doing something weird.

the spectre
  • 350
  • 4
  • 11

1 Answers1

3

Using the latest buffering strategy means there's always a value available to read in the buffer, so await will always return immediately; and send will likewise always succeed, so long as it gets a chance to run. A test program I wrote worked okay in ghci, but when compiled it never let the thread run that was trying to read from the console and send; I just got an endless stream of 0 until I terminated the program.

Your consumer run doesn't have anything in it that would cause it to pause, so it may similarly be starving the thread that is trying to send to output. If this is the case, a small threadDelay 100 or so before each call to run might help.

Another possibility, and a more efficient one, would be to use a buffer that blocks the Consumer until there's a message to act on. To avoid blocking the event callback, you could use the newest 1 buffer strategy, which would always succeed with send and overwrite the current value if there was already one there.

If neither of those help, then I suspect that there's something else going on with the way the GUI framework you're using is plumbed, but I am not very familiar with the GTK bindings in Haskell so I can't be of much help with that.

Levi Pearson
  • 4,884
  • 1
  • 16
  • 15