{-# LANGUAGE CPP, ExistentialQuantification, FlexibleInstances, ScopedTypeVariables, UndecidableInstances #-}
module Transient.Logged(
Loggable, logged, received, param,
#ifndef ghcjs_HOST_OS
suspend, checkpoint, restore,
#endif
fromIDyn,maybeFromIDyn,toIDyn
) where
import Data.Typeable
import Unsafe.Coerce
import Transient.Base
import Transient.Internals(Loggable,read')
import Transient.Indeterminism(choose)
import Transient.Internals
import Control.Applicative
import Control.Monad.State
import System.Directory
import Control.Exception
import Control.Monad
import Control.Concurrent.MVar
#ifndef ghcjs_HOST_OS
import System.Random
#endif
#ifndef ghcjs_HOST_OS
logs= "logs/"
restore :: TransIO a -> TransIO a
restore proc= do
liftIO $ createDirectory logs `catch` (\(e :: SomeException) -> return ())
list <- liftIO $ getDirectoryContents logs
`catch` (\(e::SomeException) -> return [])
if null list || length list== 2 then proc else do
let list'= filter ((/=) '.' . head) list
file <- choose list'
logstr <- liftIO $ readFile (logs++file)
let log= length logstr `seq` read' logstr
log `seq` setData (Log True (reverse log) log)
liftIO $ remove $ logs ++ file
proc
where
read'= fst . head . reads1
remove f= removeFile f `catch` (\(e::SomeException) -> remove f)
suspend :: Typeable a => a -> TransIO a
suspend x= do
Log recovery _ log _ <- getData `onNothing` return (Log False [] [] 0)
if recovery then return x else do
logAll log
exit x
checkpoint :: TransIO ()
checkpoint = do
Log recovery _ log _ <- getData `onNothing` return (Log False [] [] 0)
if recovery then return () else logAll log
logAll log= liftIO $do
newlogfile <- (logs ++) <$> replicateM 7 (randomRIO ('a','z'))
logsExist <- doesDirectoryExist logs
when (not logsExist) $ createDirectory logs
writeFile newlogfile $ show log
#endif
maybeFromIDyn :: Loggable a => IDynamic -> Maybe a
maybeFromIDyn (IDynamic x)= r
where
r= if typeOf (Just x) == typeOf r then Just $ unsafeCoerce x else Nothing
maybeFromIDyn (IDyns s) = case reads s of
[] -> Nothing
[(x,"")] -> Just x
fromIDyn :: Loggable a => IDynamic -> a
fromIDyn (IDynamic x)=r where r= unsafeCoerce x
fromIDyn (IDyns s)=r `seq`r where r= read' s
toIDyn x= IDynamic x
logged :: Loggable a => TransIO a -> TransIO a
logged mx = Transient $ do
Log recover rs full hash <- getData `onNothing` return ( Log False [][] 0)
runTrans $
case (recover ,rs) of
(True, Var x: rs') -> do
return ()
setData $ Log True rs' full (hash+ 10000000)
return $ fromIDyn x
(True, Exec:rs') -> do
setData $ Log True rs' full (hash + 1000)
mx
(True, Wait:rs') -> do
setData $ Log True rs' full (hash + 100000)
empty !> "Wait"
_ -> do
setData $ Log False (Exec : rs) (Exec: full) (hash + 1000)
r <- mx <** ( do
r <- getSData <|> return NoRemote
case r of
WasParallel ->
setData $ Log False (Wait: rs) (Wait: full) (hash+ 100000)
_ -> return ())
Log recoverAfter lognew _ _ <- getData `onNothing` return ( Log False [][] 0)
let add= Var (toIDyn r): full
if recoverAfter && (not $ null lognew)
then do
(setData $ Log True lognew (reverse lognew ++ add) (hash + 10000000) )
else if recoverAfter && (null lognew) then do
setData $ Log False [] add (hash + 10000000)
else do
(setData $ Log False (Var (toIDyn r):rs) add (hash +10000000))
return r
received :: Loggable a => a -> TransIO ()
received n=Transient $ do
Log recover rs full hash <- getData `onNothing` return ( Log False [][] 0)
case rs of
[] -> return Nothing
Var (IDyns s):t -> if s == show1 n
then do
setData $ Log recover t full hash
return $ Just ()
else return Nothing
_ -> return Nothing
where
show1 x= if typeOf x == typeOf "" then unsafeCoerce x else show x
param :: Loggable a => TransIO a
param= res where
res= Transient $ do
Log recover rs full hash<- getData `onNothing` return ( Log False [][] 0)
case rs of
[] -> return Nothing
Var (IDynamic v):t ->do
setData $ Log recover t full hash
return $ cast v
Var (IDyns s):t -> do
let mr = reads1 s `asTypeOf` type1 res
case mr of
[] -> return Nothing
(v,r):_ -> do
setData $ Log recover t full
return $ Just v
_ -> return Nothing
where
type1 :: TransIO a -> [(a,String)]
type1= error "type1: typelevel"