transient-haskell / transient

A full stack, reactive architecture for general purpose programming. Algebraic and monadically composable primitives for concurrency, parallelism, event handling, transactions, multithreading, Web, and distributed computing with complete de-inversion of control (No callbacks, no blocking, pure state)
MIT License
633 stars 28 forks source link

Can't get this code to terminate gracefully. #38

Closed DevJac closed 7 years ago

DevJac commented 7 years ago

I cannot get this code to run to completion and end gracefully. It always ends with a thread blocked indefinitely in an MVar operation error.

import Transient.Base
import Transient.Indeterminism
import Control.Monad.IO.Class
import Control.Concurrent (threadDelay)
import System.Random (randomRIO)

f _ = do
  threadDelay =<< randomRIO (0, 10000000)
  pure ["1", "2", "3"]

main :: IO ()
main = keep' $ do
  uris <- liftIO $ f ()
  url <- choose uris
  liftIO $ (mapM_ putStrLn =<< f url)

The program should print 1, 2, and 3, 3 times each. I do not care what order they are printed in, only that each number is printed 3 times.

I am using Stackage LTS 7.14.

agocorona commented 7 years ago

Hi, This error appears when keep' has finished all the threads and there is nothing else running. I have to make the error more informative however.

To avoid this, use exit which finalizes the keep' block and return the result or keep, which maintains a thread reading from the keyboard.

or leave a threar running. this keeper leave a thread running (waiting for a char), so the error do not appear

main :: IO ()
main = keeper $ do
  uris <- liftIO $ f ()
  url <- choose uris
  liftIO $ (mapM_ putStrLn =<< f url)

keeper f=  keep' $ (async getChar >> empty) <|> f
agocorona commented 7 years ago

For more details, please enter here: https://gitter.im/Transient-Transient-Universe-HPlay/Lobby

DevJac commented 7 years ago
module Main(main) where
import Transient.Base
import Transient.Indeterminism
import Control.Monad.IO.Class
import Control.Applicative (empty, (<|>))
import Control.Concurrent (threadDelay)
import System.Random (randomRIO)

keeper a =  keep' $ (async getChar >> empty) <|> a

f _ = do
  threadDelay =<< randomRIO (0, 10000000)
  pure ["1", "2", "3"]

main :: IO ()
main = keeper $ do
  uris <- liftIO $ f ()
  url <- choose uris
  liftIO $ (mapM_ putStrLn =<< f url)

The above code prints the numbers as desired, but then just sits waiting for user input. As soon as I enter a single letter the program exits with "thread blocked indefinitely in an MVar operation".

module Main(main) where
import Transient.Base
import Transient.Indeterminism
import Control.Monad.IO.Class
import Control.Concurrent (threadDelay)
import System.Random (randomRIO)

f _ = do
  threadDelay =<< randomRIO (0, 10000000)
  pure ["1", "2", "3"]

main :: IO ()
main = keep' $ do
  uris <- liftIO $ f ()
  url <- choose uris
  liftIO $ (mapM_ putStrLn =<< f url)
  exit ()

The above code only prints each number one time, then ends cleanly. The problem is that it ends too early, it only prints each number 1 time instead of 3 times.

To recap, all the code I've tried has at least one (and usually only one) of these issues:

Is it possible to have code that does none of those things?

agocorona commented 7 years ago

ok, exit is for premature exit from the keep block. as soon as exit is called, it exit. This is for some algorithms like search which end once a solution is found.

instead of keep' or keeper use this:

 keepUntilNoThreads f=  keep'  f  `catch`  \(e :: BlockedIndefinitelyOnMVar) -> return ()

it should work

DevJac commented 7 years ago

I have mixed feeling about that solution. Effectively it just prevents the error from being printed, but the same conditions that lead to the error still exist, you're just hiding the error message.

I would be interested in a "better" solution, though I can't define exactly what I mean by that.

I offer this as feedback, as a potential user of transient.

We talked about this on the #haskell IRC and you said eventually keep' will end gracefully without throwing a BlockedIndefinitelyOnMVar; that sounds like the better solution I was hoping for. :)

Sorry about that #haskell IRC conversation. For what it's worth transient looks good to me and I will be keeping an eye on it. Keep the the good work.

agocorona commented 7 years ago

Yeah, thanks a lot for your feedback. In the next version keep' will not produce that error. I suppose that I need to add more keep variants for different use cases.

The discussion in irc was excelent! I know that transient is quite "imperialistic" and it is natural that many people don't like to change the way they do things. It is is like another different approach to Haskell that is arguably more practical and more functional, and will succeed in the medium term