{-# 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.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
import qualified Data.ByteString.Lazy.Char8 as BS
import qualified Data.ByteString.Char8 as BSS
#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)
setData WasParallel
empty
_ -> do
setData $ Log False (Exec : rs) (Exec: full) (hash + 1000)
r <- mx <** do setData $ Log False (Wait: rs) (Wait: full) (hash+ 100000)
Log recoverAfter lognew _ _ <- getData `onNothing` return ( Log False [][] 0)
let add= Var (toIDyn r): full
if recoverAfter && (not $ null lognew)
then do
setData WasParallel
(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)
return () !> ("RECEIVED log, n", rs,n)
case rs of
[] -> return Nothing
Var (IDyns s):t -> if s == show1 n
then do
return() !> "valid"
setData $ Log recover t full hash
return $ Just ()
else return Nothing
_ -> return Nothing
where
show1 x= if typeOf x == typeOf "" then unsafeCoerce x
else if typeOf x== typeOf (undefined :: BS.ByteString) then unsafeCoerce x
else if typeOf x== typeOf (undefined :: BSS.ByteString) 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)
return () !> ("PARAM",rs)
case rs of
[] -> return Nothing
Var (IDynamic v):t ->do
return () !> ("IDyn", show v)
setData $ Log recover t full hash
return $ cast v
Var (IDyns s):t -> do
return () !> ("IDyn",s)
let mr = reads1 s `asTypeOf` type1 res
case mr of
[] -> return Nothing
(v,r):_ -> do
setData $ Log recover t full hash
return $ Just v
_ -> return Nothing
where
type1 :: TransIO a -> [(a,String)]
type1= error "type1: typelevel"