module MagicHaskeller.TimeOut where
import Control.Concurrent(forkIO, killThread, myThreadId, ThreadId, threadDelay, yield)
import Control.Concurrent.MVar
import Control.Exception
import System.CPUTime
import System.IO.Unsafe(unsafePerformIO)
#if __GLASGOW_HASKELL__ >=702
import System.IO.Unsafe(unsafeDupablePerformIO)
#endif
import Control.Monad(when)
import System.IO
import Debug.Trace
import qualified System.Timeout
import MagicHaskeller.Options(Opt(..))
#ifdef FORCIBLETO
import qualified MagicHaskeller.ForcibleTO as ForcibleTO
unsafeWithPTOOpt :: ForcibleTO.Byte a => Opt b -> a -> Maybe a
unsafeWithPTOOpt opt = let pto = timeout opt
in if forcibleTimeout opt then ForcibleTO.unsafeWithPTO pto else unsafeWithPTO pto
maybeWithTOOpt opt = let pto = timeout opt
in if forcibleTimeout opt then ForcibleTO.maybeWithTO pto else maybeWithTO seq pto
#else
unsafeWithPTOOpt opt = let pto = timeout opt
in unsafeWithPTO pto
maybeWithTOOpt opt = let pto = timeout opt
in maybeWithTO seq pto
#endif
unsafeWithPTO :: Maybe Int -> a -> Maybe a
#if 0
unsafeWithPTO pto a = unsafeDupablePerformIO $ wrapExecution (
#else
unsafeWithPTO pto a = unsafePerformIO $ wrapExecution (
#endif
maybeWithTO seq pto (return $! a)
)
maybeWithTO :: (a -> IO () -> IO ())
-> Maybe Int -> IO a -> IO (Maybe a)
maybeWithTO sq mbt action = maybeWithTO' sq mbt (const action)
newPTO t = return t
unsafeOpWithPTO :: Maybe Int -> (a->b->c) -> a -> b -> Maybe c
unsafeOpWithPTO mto op l r = unsafeWithPTO mto (op l r)
maybeWithTO' :: (a -> IO () -> IO ()) -> Maybe Int -> ((IO b -> IO b) -> IO a) -> IO (Maybe a)
maybeWithTO' _ Nothing action = do a <- action id
return (Just a)
maybeWithTO' dsq (Just t) action = do tid <- myThreadId
bracket (forkIO (threadDelay t >>
throwTo tid (ErrorCall "Timeout")))
(\th -> killThread th)
(\_ -> fmap Just $ action (yield>>))
`Control.Exception.catch` \(e :: SomeException) ->
return Nothing
withTO' :: (a -> IO () -> IO ()) -> Int -> ((IO b -> IO b) -> IO a) -> IO (Maybe a)
withTO' dsq timeInMicroSecs action
= do
resMV <- newEmptyMVar
do
(catchIt resMV (do
tid <- myThreadId
chtid <- forkIO (do threadDelay timeInMicroSecs
hPutStrLn stderr "writing Nothing"
putMVar resMV Nothing
hPutStrLn stderr "killing the main"
killThread tid)
res <- action (yield>>)
hPutStrLn stderr "writing Just"
res `dsq` putMVar resMV (Just res)
hPutStrLn stderr "killing the thread for timeout"
killThread chtid))
hPutStrLn stderr "reading MV"
readMVar resMV
wrapExecution = id
measureExecutionTime act
= do begin <- getCPUTime
res <- act
end <- getCPUTime
hPutStrLn stderr (show (end begin))
return res
catchIt :: (MVar (Maybe a)) -> IO () -> IO ()
#ifdef REALDYNAMIC
catchIt sv act = act
#else
catchIt sv act = Control.Exception.catch act (handler sv)
#endif
handler sv err = Control.Exception.catch (realHandler sv (err::SomeException)) (handler sv)
realHandler sv err = putMVar sv Nothing
catchYield tid sv action = Control.Exception.catch (yield >> action) (childHandler tid sv)
childHandler tid sv err = Control.Exception.catch (realChildHandler tid sv (err::SomeException)) (childHandler tid sv)
realChildHandler tid sv err = do putMVar sv Nothing
killThread tid
error "This thread must have been killed...."