I have to write a command line tool gluing together some components for an experiment and need help for a code design that meet my requirements.
At top level I have to work with samples each generated by a – in runtime as well as in memory consumption – expensive call to another program with the function "System.Process.readProcessWithExitCode". Therefore you can imagine to have a (expensive) function "genSample :: IO a" and you need n return values of that function.
My requirements are: 1. Let p be the number of processors, then at most p samples (i.e. calls to genSample) should be computed in parallel. 2. A timeout should be possible set which aborts the generation of the samples. 3. If the computation of all the samples times out, the started processes within a genSample-call should be stopped
My current solution meets requirements 1 and 2. For the third one I currently help myself by executing a killall-command. That seems to be a dirty hack to me. Perhaps someone has a better idea?
Here the central part of my current solution:
import qualified Control.Monad.Par.Class as ParIO
import qualified Control.Monad.Par.IO as ParIO
…
-- | @parRepeatM i n a@ performs action @a@ @n@ times in parallel with timeout @t@
parRepeatM :: ParIO.NFData a =>
Integer -- ^ timeout in seconds
-> Integer -- ^ number of duplicates (here: number of req. samples)
-> IO a -- ^ action to perform (here: genSample)
-> IO (Maybe [a])
parRepeatM t n a = timeout t $ ParIO.runParIO $ do
let tasks = genericReplicate n $ liftIO a -- :: [ParIO a]
ivars <- mapM ParIO.spawn tasks
mapM ParIO.get ivars
A central problem at the moment is that after abortion due to a timeout the command called within genSample continues it's execution – in the worst case until the whole haskell-gluing-program ends.