{-# LANGUAGE CPP #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE ConstraintKinds #-}
module Transient.Internals where
import Control.Applicative
import Control.Monad.State
import Data.Dynamic
import qualified Data.Map as M
import System.IO.Unsafe
import Unsafe.Coerce
import Control.Exception hiding (try,onException)
import qualified Control.Exception (try)
import Control.Concurrent
import System.Mem.StableName
import Data.Maybe
import Data.List
import Data.IORef
import System.Environment
import System.IO
import System.IO.Error
import Data.String
import qualified Data.ByteString.Char8 as BS
import Data.Typeable
#ifndef ETA_VERSION
import Data.Atomics
#endif
#ifdef DEBUG
import Debug.Trace
import System.Exit
tshow :: Show a => a -> x -> x
tshow= Debug.Trace.traceShow
{-# INLINE (!>) #-}
(!>) :: Show a => b -> a -> b
(!>) x y = trace (show y) x
infixr 0 !>
#else
tshow :: Show a => a -> x -> x
tshow _ y= y
{-# INLINE (!>) #-}
(!>) :: a -> b -> a
(!>) = const
#endif
#ifdef ETA_VERSION
atomicModifyIORefCAS = atomicModifyIORef
#endif
type StateIO = StateT EventF IO
newtype TransIO a = Transient { runTrans :: StateIO (Maybe a) }
type SData = ()
type EventId = Int
type TransientIO = TransIO
data LifeCycle = Alive | Parent | Listener | Dead
deriving (Eq, Show)
data EventF = forall a b. EventF
{ event :: Maybe SData
, xcomp :: TransIO a
, fcomp :: [b -> TransIO b]
, mfData :: M.Map TypeRep SData
, mfSequence :: Int
, threadId :: ThreadId
, freeTh :: Bool
, parent :: Maybe EventF
, children :: MVar [EventF]
, maxThread :: Maybe (IORef Int)
, labelth :: IORef (LifeCycle, BS.ByteString)
} deriving Typeable
instance MonadState EventF TransIO where
get = Transient $ get >>= return . Just
put x = Transient $ put x >> return (Just ())
state f = Transient $ do
s <- get
let ~(a, s') = f s
put s'
return $ Just a
noTrans :: StateIO x -> TransIO x
noTrans x = Transient $ x >>= return . Just
emptyEventF :: ThreadId -> IORef (LifeCycle, BS.ByteString) -> MVar [EventF] -> EventF
emptyEventF th label childs =
EventF { event = mempty
, xcomp = empty
, fcomp = []
, mfData = mempty
, mfSequence = 0
, threadId = th
, freeTh = False
, parent = Nothing
, children = childs
, maxThread = Nothing
, labelth = label }
runTransient :: TransIO a -> IO (Maybe a, EventF)
runTransient t = do
th <- myThreadId
label <- newIORef $ (Alive, BS.pack "top")
childs <- newMVar []
runStateT (runTrans t) $ emptyEventF th label childs
runTransState :: EventF -> TransIO x -> IO (Maybe x, EventF)
runTransState st x = runStateT (runTrans x) st
emptyIfNothing :: Maybe a -> TransIO a
emptyIfNothing = Transient . return
getCont :: TransIO EventF
getCont = Transient $ Just <$> get
runCont :: EventF -> StateIO (Maybe a)
runCont EventF { xcomp = x, fcomp = fs } = runTrans $ do
r <- unsafeCoerce x
compose fs r
runCont' :: EventF -> IO (Maybe a, EventF)
runCont' cont = runStateT (runCont cont) cont
getContinuations :: StateIO [a -> TransIO b]
getContinuations = do
EventF { fcomp = fs } <- get
return $ unsafeCoerce fs
{-# INLINE compose #-}
compose :: [a -> TransIO a] -> (a -> TransIO b)
compose [] = const empty
compose (f:fs) = \x -> f x >>= compose fs
runClosure :: EventF -> StateIO (Maybe a)
runClosure EventF { xcomp = x } = unsafeCoerce (runTrans x)
runContinuation :: EventF -> a -> StateIO (Maybe b)
runContinuation EventF { fcomp = fs } =
runTrans . (unsafeCoerce $ compose $ fs)
setContinuation :: TransIO a -> (a -> TransIO b) -> [c -> TransIO c] -> StateIO ()
setContinuation b c fs = do
modify $ \EventF{..} -> EventF { xcomp = b
, fcomp = unsafeCoerce c : fs
, .. }
withContinuation :: b -> TransIO a -> TransIO a
withContinuation c mx = do
EventF { fcomp = fs, .. } <- get
put $ EventF { xcomp = mx
, fcomp = unsafeCoerce c : fs
, .. }
r <- mx
restoreStack fs
return r
restoreStack :: MonadState EventF m => [a -> TransIO a] -> m ()
restoreStack fs = modify $ \EventF {..} -> EventF { event = Nothing, fcomp = fs, .. }
runContinuations :: [a -> TransIO b] -> c -> TransIO d
runContinuations fs x = compose (unsafeCoerce fs) x
instance Functor TransIO where
fmap f mx = do
x <- mx
return $ f x
instance Applicative TransIO where
pure a = Transient . return $ Just a
mf <*> mx = do
r1 <- liftIO $ newIORef Nothing
r2 <- liftIO $ newIORef Nothing
fparallel r1 r2 <|> xparallel r1 r2
where
fparallel r1 r2= do
f <- mf
r <- getState <|> return NoRemote
if r == NoRemote then do
x <- mx !> "second serial"
return $ f x
else do
liftIO $ (writeIORef r1 $ Just f)
mx <- liftIO (readIORef r2)
case mx of
Nothing -> empty
Just x -> return $ f x
xparallel r1 r2 = do
r <- getState <|> return NoRemote
if r == WasParallel then do
x <- mx
liftIO $ (writeIORef r2 $ Just x)
mf <- liftIO (readIORef r1)
case mf of
Nothing -> empty
Just f -> return $ f x
else empty
fullStop :: TransIO stop
fullStop= setData WasRemote >> stop
instance Monad TransIO where
return = pure
x >>= f = Transient $ do
setEventCont x f
mk <- runTrans x
resetEventCont mk
case mk of
Just k -> runTrans (f k)
Nothing -> return Nothing
instance MonadIO TransIO where
liftIO x = Transient $ liftIO x >>= return . Just
instance Monoid a => Monoid (TransIO a) where
mempty = return mempty
#if MIN_VERSION_base(4,11,0)
mappend = (<>)
instance (Monoid a) => Semigroup (TransIO a) where
(<>)= mappendt
#else
mappend= mappendt
#endif
mappendt x y = mappend <$> x <*> y
instance Alternative TransIO where
empty = Transient $ return Nothing
(<|>) = mplus
instance MonadPlus TransIO where
mzero = empty
mplus x y = Transient $ do
mx <- runTrans x
was <- getData `onNothing` return NoRemote
if was == WasRemote
then return Nothing
else case mx of
Nothing -> runTrans y
justx -> return justx
readWithErr :: (Typeable a, Read a) => Int -> String -> IO [(a, String)]
readWithErr n line =
(v `seq` return [(v, left)])
`catch` (\(_ :: SomeException) ->
throw $ ParseError $ "read error trying to read type: \"" ++ show (typeOf v)
++ "\" in: " ++ " <" ++ show line ++ "> ")
where (v, left):_ = readsPrec n line
newtype ParseError= ParseError String deriving (Show)
instance Exception ParseError
read' s= case readsPrec' 0 s of
[(x,"")] -> x
_ -> throw $ ParseError $ "reading " ++ s
readsPrec' n = unsafePerformIO . readWithErr n
type Loggable a = (Show a, Read a, Typeable a)
data IDynamic =
IDyns String
| forall a. Loggable a => IDynamic a
instance Show IDynamic where
show (IDynamic x) = show (show x)
show (IDyns s) = show s
instance Read IDynamic where
readsPrec n str = map (\(x,s) -> (IDyns x,s)) $ readsPrec' n str
type Recover = Bool
type CurrentPointer = [LogElem]
type LogEntries = [LogElem]
type Hash = Int
data LogElem = Wait | Exec | Var IDynamic
deriving (Read, Show)
data Log = Log Recover CurrentPointer LogEntries Hash
deriving (Typeable, Show)
data RemoteStatus = WasRemote | WasParallel | NoRemote
deriving (Typeable, Eq, Show)
stop :: Alternative m => m stopped
stop = empty
instance (Num a,Eq a,Fractional a) =>Fractional (TransIO a)where
mf / mg = (/) <$> mf <*> mg
fromRational r = return $ fromRational r
instance (Num a, Eq a) => Num (TransIO a) where
fromInteger = return . fromInteger
mf + mg = (+) <$> mf <*> mg
mf * mg = (*) <$> mf <*> mg
negate f = f >>= return . negate
abs f = f >>= return . abs
signum f = f >>= return . signum
class AdditionalOperators m where
(**>) :: m a -> m b -> m b
(<**) :: m a -> m b -> m a
atEnd' :: m a -> m b -> m a
atEnd' = (<**)
(<***) :: m a -> m b -> m a
atEnd :: m a -> m b -> m a
atEnd = (<***)
instance AdditionalOperators TransIO where
(**>) x y =
Transient $ do
runTrans x
runTrans y
(<***) ma mb =
Transient $ do
fs <- getContinuations
setContinuation ma (\x -> mb >> return x) fs
a <- runTrans ma
runTrans mb
restoreStack fs
return a
(<**) ma mb =
Transient $ do
a <- runTrans ma
runTrans mb
return a
infixr 1 <***, <**, **>
(<|) :: TransIO a -> TransIO b -> TransIO a
(<|) ma mb = Transient $ do
fs <- getContinuations
ref <- liftIO $ newIORef False
setContinuation ma (cont ref) fs
r <- runTrans ma
restoreStack fs
return r
where cont ref x = Transient $ do
n <- liftIO $ readIORef ref
if n == True
then return $ Just x
else do liftIO $ writeIORef ref True
runTrans mb
return $ Just x
{-# INLINABLE setEventCont #-}
setEventCont :: TransIO a -> (a -> TransIO b) -> StateIO ()
setEventCont x f = modify $ \EventF { fcomp = fs, .. }
-> EventF { xcomp = x
, fcomp = unsafeCoerce f : fs
, .. }
{-# INLINABLE resetEventCont #-}
resetEventCont mx =
modify $ \EventF { fcomp = fs, .. }
-> EventF { xcomp = case mx of
Nothing -> empty
Just x -> unsafeCoerce (head fs) x
, fcomp = tailsafe fs
, .. }
{-# INLINE tailsafe #-}
tailsafe :: [a] -> [a]
tailsafe [] = []
tailsafe (_:xs) = xs
waitQSemB sem = atomicModifyIORefCAS sem $ \n ->
if n > 0 then(n - 1, True) else (n, False)
signalQSemB sem = atomicModifyIORefCAS sem $ \n -> (n + 1, ())
threads :: Int -> TransIO a -> TransIO a
threads n process = do
msem <- gets maxThread
sem <- liftIO $ newIORef n
modify $ \s -> s { maxThread = Just sem }
r <- process <** (modify $ \s -> s { maxThread = msem })
return r
oneThread :: TransIO a -> TransIO a
oneThread comp = do
st <- get
rchs <- liftIO $ newMVar []
label <- liftIO $ newIORef (Alive, BS.pack "oneThread")
let st' = st { parent = Just st
, children = rchs
, labelth = label }
liftIO $ do
atomicModifyIORefCAS (labelth st) $ \(_, label) -> ((Parent,label),())
hangThread st st'
put st'
x <- comp
th <- liftIO myThreadId
!> ("FATHER:", threadId st)
chs <- liftIO $ readMVar rchs
liftIO $ mapM_ (killChildren1 th) chs
!> ("KILLEVENT1 ", map threadId chs )
return x
where
killChildren1 :: ThreadId -> EventF -> IO ()
killChildren1 th state = do
ths' <- modifyMVar (children state) $ \ths -> do
let (inn, ths')= partition (\st -> threadId st == th) ths
return (inn, ths')
mapM_ (killChildren1 th) ths'
mapM_ (killThread . threadId) ths'
labelState :: (MonadIO m,MonadState EventF m) => BS.ByteString -> m ()
labelState l = do
st <- get
liftIO $ atomicModifyIORefCAS (labelth st) $ \(status,_) -> ((status, l), ())
printBlock :: MVar ()
printBlock = unsafePerformIO $ newMVar ()
showThreads :: MonadIO m => EventF -> m ()
showThreads st = liftIO $ withMVar printBlock $ const $ do
mythread <- myThreadId
putStrLn "---------Threads-----------"
let showTree n ch = do
liftIO $ do
putStr $ take n $ repeat ' '
(state, label) <- readIORef $ labelth ch
if BS.null label
then putStr . show $ threadId ch
else do BS.putStr label; putStr . drop 8 . show $ threadId ch
when (state == Dead) $ putStr " dead"
putStrLn $ if mythread == threadId ch then " <--" else ""
chs <- readMVar $ children ch
mapM_ (showTree $ n + 2) $ reverse chs
showTree 0 st
topState :: MonadState EventF m => m EventF
topState = do
st <- get
return $ toplevel st
where toplevel st = case parent st of
Nothing -> st
Just p -> toplevel p
findState :: (MonadIO m, Alternative m) => (EventF -> m Bool) -> EventF -> m EventF
findState filter top= do
r <- filter top
if r then return top
else do
sts <- liftIO $ readMVar $ children top
foldl (<|>) empty $ map (findState filter) sts
getStateFromThread :: (Typeable a, MonadIO m, Alternative m) => String -> EventF -> m (Maybe a)
getStateFromThread th top= getstate =<< findState (matchth th) top
where
matchth th th'= do
let thstring = drop 9 . show $ threadId th'
return $ if thstring == th then True else False
getstate st = resp
where resp= case M.lookup (typeOf $ typeResp resp) $ mfData st of
Just x -> return . Just $ unsafeCoerce x
Nothing -> return Nothing
typeResp :: m (Maybe x) -> x
typeResp = undefined
processStates :: Typeable a => (a-> TransIO ()) -> EventF -> TransIO()
processStates display st = do
getstate st >>= display
liftIO $ print $ threadId st
sts <- liftIO $ readMVar $ children st
mapM_ (processStates display) sts
where
getstate st =
case M.lookup (typeOf $ typeResp display) $ mfData st of
Just x -> return $ unsafeCoerce x
Nothing -> empty
typeResp :: (a -> TransIO()) -> a
typeResp = undefined
addThreads' :: Int -> TransIO ()
addThreads' n= noTrans $ do
msem <- gets maxThread
case msem of
Just sem -> liftIO $ modifyIORef sem $ \n' -> n + n'
Nothing -> do
sem <- liftIO (newIORef n)
modify $ \ s -> s { maxThread = Just sem }
addThreads :: Int -> TransIO ()
addThreads n = noTrans $ do
msem <- gets maxThread
case msem of
Nothing -> return ()
Just sem -> liftIO $ modifyIORef sem $ \n' -> if n' > n then n' else n
freeThreads :: TransIO a -> TransIO a
freeThreads process = Transient $ do
st <- get
put st { freeTh = True }
r <- runTrans process
modify $ \s -> s { freeTh = freeTh st }
return r
hookedThreads :: TransIO a -> TransIO a
hookedThreads process = Transient $ do
st <- get
put st {freeTh = False}
r <- runTrans process
modify $ \s -> s { freeTh = freeTh st }
return r
killChilds :: TransIO ()
killChilds = noTrans $ do
cont <- get
liftIO $ do
killChildren $ children cont
writeIORef (labelth cont) (Alive, mempty)
return ()
killBranch :: TransIO ()
killBranch = noTrans $ do
st <- get
liftIO $ killBranch' st
killBranch' :: EventF -> IO ()
killBranch' cont = do
killChildren $ children cont
let thisth = threadId cont
mparent = parent cont
when (isJust mparent) $
modifyMVar_ (children $ fromJust mparent) $ \sts ->
return $ filter (\st -> threadId st /= thisth) sts
killThread $ thisth !> ("kill this thread:",thisth)
getData :: (MonadState EventF m, Typeable a) => m (Maybe a)
getData = resp
where resp = do
list <- gets mfData
case M.lookup (typeOf $ typeResp resp) list of
Just x -> return . Just $ unsafeCoerce x
Nothing -> return Nothing
typeResp :: m (Maybe x) -> x
typeResp = undefined
getSData :: Typeable a => TransIO a
getSData = Transient getData
getState :: Typeable a => TransIO a
getState = getSData
setData :: (MonadState EventF m, Typeable a) => a -> m ()
setData x = modify $ \st -> st { mfData = M.insert t (unsafeCoerce x) (mfData st) }
where t = typeOf x
modifyData :: (MonadState EventF m, Typeable a) => (Maybe a -> Maybe a) -> m ()
modifyData f = modify $ \st -> st { mfData = M.alter alterf t (mfData st) }
where typeResp :: (Maybe a -> b) -> a
typeResp = undefined
t = typeOf (typeResp f)
alterf mx = unsafeCoerce $ f x'
where x' = case mx of
Just x -> Just $ unsafeCoerce x
Nothing -> Nothing
modifyData' :: (MonadState EventF m, Typeable a) => (a -> a) -> a -> m a
modifyData' f v= do
st <- get
let (ma,nmap)= M.insertLookupWithKey alterf t (unsafeCoerce v) (mfData st)
put st { mfData =nmap}
return $ if isNothing ma then v else unsafeCoerce $ fromJust ma
where t = typeOf v
alterf _ _ x = unsafeCoerce $ f $ unsafeCoerce x
modifyState :: (MonadState EventF m, Typeable a) => (Maybe a -> Maybe a) -> m ()
modifyState = modifyData
setState :: (MonadState EventF m, Typeable a) => a -> m ()
setState = setData
delData :: (MonadState EventF m, Typeable a) => a -> m ()
delData x = modify $ \st -> st { mfData = M.delete (typeOf x) (mfData st) }
delState :: (MonadState EventF m, Typeable a) => a -> m ()
delState = delData
newtype Ref a = Ref (IORef a)
setRState:: (MonadIO m,MonadState EventF m, Typeable a) => a -> m ()
setRState x= do
Ref ref <- getData `onNothing` do
ref <- Ref <$> liftIO (newIORef x)
setData ref
return ref
liftIO $ atomicModifyIORefCAS ref $ const (x,())
getRData :: (MonadIO m, MonadState EventF m, Typeable a) => m (Maybe a)
getRData= do
mref <- getData
case mref of
Just (Ref ref) -> Just <$> (liftIO $ readIORef ref)
Nothing -> return Nothing
getRState :: Typeable a => TransIO a
getRState= Transient getRData
delRState x= delState (undefined `asTypeOf` ref x)
where ref :: a -> Ref a
ref = undefined
try :: TransIO a -> TransIO a
try mx = do
sd <- gets mfData
mx <|> (modify (\s -> s { mfData = sd }) >> empty)
sandbox :: TransIO a -> TransIO a
sandbox mx = do
sd <- gets mfData
mx <*** modify (\s ->s { mfData = sd})
genGlobalId :: MonadIO m => m Int
genGlobalId= liftIO $ atomicModifyIORefCAS rglobalId $ \n -> (n +1,n)
rglobalId= unsafePerformIO $ newIORef (0 :: Int)
genId :: MonadState EventF m => m Int
genId = do
st <- get
let n = mfSequence st
put st { mfSequence = n + 1 }
return n
getPrevId :: MonadState EventF m => m Int
getPrevId = gets mfSequence
instance Read SomeException where
readsPrec n str = [(SomeException $ ErrorCall s, r)]
where [(s , r)] = readsPrec n str
data StreamData a =
SMore a
| SLast a
| SDone
| SError SomeException
deriving (Typeable, Show,Read)
instance Functor StreamData where
fmap f (SMore a)= SMore (f a)
fmap f (SLast a)= SLast (f a)
fmap _ SDone= SDone
waitEvents :: IO a -> TransIO a
waitEvents io = do
mr <- parallel (SMore <$> io)
case mr of
SMore x -> return x
SError e -> back e
async :: IO a -> TransIO a
async io = do
mr <- parallel (SLast <$> io)
case mr of
SLast x -> return x
SError e -> back e
sync :: TransIO a -> TransIO a
sync x = do
setData WasRemote
r <- x
delData WasRemote
return r
spawn :: IO a -> TransIO a
spawn = freeThreads . waitEvents
sample :: Eq a => IO a -> Int -> TransIO a
sample action interval = do
v <- liftIO action
prev <- liftIO $ newIORef v
waitEvents (loop action prev) <|> async (return v)
where loop action prev = loop'
where loop' = do
threadDelay interval
v <- action
v' <- readIORef prev
if v /= v' then writeIORef prev v >> return v else loop'
parallel :: IO (StreamData b) -> TransIO (StreamData b)
parallel ioaction = Transient $ do
was <- getData `onNothing` return NoRemote
when (was /= WasRemote) $ setData WasParallel
cont <- get
case event cont of
j@(Just _) -> do
put cont { event = Nothing }
return $ unsafeCoerce j
Nothing -> do
liftIO $ atomicModifyIORefCAS (labelth cont) $ \(_, lab) -> ((Parent, lab), ())
liftIO $ loop cont ioaction
return Nothing
loop :: EventF -> IO (StreamData t) -> IO ()
loop parentc rec = forkMaybe parentc $ \cont -> do
liftIO $ atomicModifyIORefCAS (labelth cont) $ const ((Listener,BS.pack "wait"),())
let loop'= do
mdat <- rec `catch` \(e :: SomeException) -> return $ SError e
case mdat of
se@(SError _) -> setworker cont >> iocont se cont
SDone -> setworker cont >> iocont SDone cont
last@(SLast _) -> setworker cont >> iocont last cont
more@(SMore _) -> do
forkMaybe cont $ iocont more
loop'
where
setworker cont= liftIO $ atomicModifyIORefCAS (labelth cont) $ const ((Alive,BS.pack "work"),())
iocont dat cont = do
let cont'= cont{event= Just $ unsafeCoerce dat}
runStateT (runCont cont') cont'
return ()
loop'
return ()
where
{-# INLINABLE forkMaybe #-}
forkMaybe parent proc = do
case maxThread parent of
Nothing -> forkIt parent proc
Just sem -> do
dofork <- waitQSemB sem
if dofork then forkIt parent proc else proc parent
forkIt parent proc= do
chs <- liftIO $ newMVar []
label <- newIORef (Alive, BS.pack "work")
let cont = parent{parent=Just parent,children= chs, labelth= label}
forkFinally1 (do
th <- myThreadId
let cont'= cont{threadId=th}
when(not $ freeTh parent )$ hangThread parent cont'
proc cont')
$ \me -> do
case me of
Left e -> exceptBack cont e >> return ()
_ -> do
case maxThread cont of
Just sem -> signalQSemB sem
Nothing -> return ()
when(not $ freeTh parent ) $ do
th <- myThreadId
(can,label) <- atomicModifyIORefCAS (labelth cont) $ \(l@(status,label)) ->
((if status== Alive then Dead else status, label),l)
when (can /= Parent ) $ free th parent
return ()
forkFinally1 :: IO a -> (Either SomeException a -> IO ()) -> IO ThreadId
forkFinally1 action and_then =
mask $ \restore -> forkIO $ Control.Exception.try (restore action) >>= and_then
free th env= do
threadDelay 1000
let sibling= children env
(sbs',found) <- modifyMVar sibling $ \sbs -> do
let (sbs', found) = drop [] th sbs
return (sbs',(sbs',found))
if found
then do
(typ,_) <- readIORef $ labelth env
if (null sbs' && typ /= Listener && isJust (parent env))
then free (threadId env) ( fromJust $ parent env)
else return ()
else return ()
where
drop processed th []= (processed,False)
drop processed th (ev:evts)| th == threadId ev= (processed ++ evts, True)
| otherwise= drop (ev:processed) th evts
hangThread parentProc child = do
let headpths= children parentProc
modifyMVar_ headpths $ \ths -> return (child:ths)
killChildren childs = do
ths <- modifyMVar childs $ \ths -> return ([],ths)
mapM_ (killChildren . children) ths
mapM_ (killThread . threadId) ths !> ("Kill children", map threadId ths )
react
:: Typeable eventdata
=> ((eventdata -> IO response) -> IO ())
-> IO response
-> TransIO eventdata
react setHandler iob= Transient $ do
was <- getData `onNothing` return NoRemote
when (was /= WasRemote) $ setData WasParallel
cont <- get
case event cont of
Nothing -> do
liftIO $ setHandler $ \dat ->do
runStateT (runCont cont) cont{event= Just $ unsafeCoerce dat} `catch` exceptBack cont
iob
return Nothing
j@(Just _) -> do
put cont{event=Nothing}
return $ unsafeCoerce j
abduce = async $ return ()
option :: (Typeable b, Show b, Read b, Eq b) =>
b -> String -> TransIO b
option = optionf False
option1 :: (Typeable b, Show b, Read b, Eq b) =>
b -> String -> TransIO b
option1= optionf True
optionf :: (Typeable b, Show b, Read b, Eq b) =>
Bool -> b -> String -> TransIO b
optionf flag ret message = do
let sret= if typeOf ret == typeOf "" then unsafeCoerce ret else show ret
liftIO $ putStrLn $ "Enter "++sret++"\t\tto: " ++ message
inputf flag sret Nothing ( == sret)
liftIO $ putStr "\noption: " >> putStrLn sret
return ret
inputf :: Loggable a => Bool -> String -> Maybe a -> (a -> Bool) -> TransIO a
inputf flag ident mv cond= do
str <- react (addListener ident) (return ())
when flag $ liftIO $ delListener ident
c <- liftIO $ readIORef rconsumed
if c then returnm mv else do
if null str then do liftIO $ writeIORef rconsumed True; returnm mv else do
let rr = read1 str
case rr of
Just x -> if cond x
then liftIO $ do
writeIORef rconsumed True
return x
else do liftIO $ when (isJust mv) $ putStrLn ""; returnm mv
_ -> do liftIO $ when (isJust mv) $ putStrLn ""; returnm mv
where
returnm (Just x)= return x
returnm _ = empty
read1 s= r
where
r = if (typeOf $ fromJust r) == typeOf ""
then Just $ unsafeCoerce s
else case reads s of
[] -> Nothing
[(x,"")] -> Just x
input :: (Typeable a, Read a,Show a) => (a -> Bool) -> String -> TransIO a
input= input' Nothing
input' :: (Typeable a, Read a,Show a) => Maybe a -> (a -> Bool) -> String -> TransIO a
input' mv cond prompt= do
liftIO $ putStr prompt >> hFlush stdout
inputf True "input" mv cond
rcb= unsafePerformIO $ newIORef [] :: IORef [ (String,String -> IO())]
addListener :: String -> (String -> IO ()) -> IO ()
addListener name cb= atomicModifyIORef rcb $ \cbs -> (filter ((/=) name . fst) cbs ++ [(name, cb)],())
delListener :: String -> IO ()
delListener name= atomicModifyIORef rcb $ \cbs -> (filter ((/=) name . fst) cbs,())
reads1 s=x where
x= if typeOf(typeOfr x) == typeOf "" then unsafeCoerce[(s,"")] else readsPrec' 0 s
typeOfr :: [(a,String)] -> a
typeOfr = undefined
read1 s= let [(x,"")]= reads1 s in x
rprompt= unsafePerformIO $ newIORef "> "
inputLoop= do
line <- getLine
processLine line
threadDelay 1000000
prompt <- readIORef rprompt
when (not $ null prompt) $ do putStr prompt ; hFlush stdout
inputLoop
{-# NOINLINE rconsumed #-}
rconsumed = unsafePerformIO $ newIORef False
{-# NOINLINE lineprocessmode #-}
lineprocessmode= unsafePerformIO $ newIORef False
processLine r = do
linepro <- readIORef lineprocessmode
if linepro then do
mapM' invokeParsers [r]
else do
let rs = breakSlash [] r
mapM' invokeParsers rs
where
invokeParsers x= do
mbs <- readIORef rcb
mapM_ (\cb -> cb x) $ map snd mbs
mapM' f []= return ()
mapM' f (xss@(x:xs)) =do
f x
r <- readIORef rconsumed
if r
then do
writeIORef riterloop 0
writeIORef rconsumed False
mapM' f xs
else do
threadDelay 1000
n <- atomicModifyIORef riterloop $ \n -> (n+1,n)
if n==100
then do
when (not $ null x) $ hPutStr stderr x >> hPutStrLn stderr ": can't read, skip"
writeIORef riterloop 0
writeIORef rconsumed False
mapM' f xs
else mapM' f xss
riterloop= unsafePerformIO $ newIORef 0
breakSlash :: [String] -> String -> [String]
breakSlash [] ""= [""]
breakSlash s ""= s
breakSlash res ('\"':s)=
let (r,rest) = span(/= '\"') s
in breakSlash (res++[r]) $ tail1 rest
breakSlash res s=
let (r,rest) = span(\x -> x /= '/' && x /= ' ') s
in breakSlash (res++[r]) $ tail1 rest
tail1 []= []
tail1 x= tail x
stay rexit= takeMVar rexit
`catch` \(e :: BlockedIndefinitelyOnMVar) -> return Nothing
newtype Exit a= Exit a deriving Typeable
keep :: Typeable a => TransIO a -> IO (Maybe a)
keep mx = do
liftIO $ hSetBuffering stdout LineBuffering
rexit <- newEmptyMVar
forkIO $ do
runTransient $ do
onException $ \(e :: SomeException ) -> liftIO $ putStr "keep block: " >> print e
onException $ \(e :: IOException) -> do
when (ioeGetErrorString e == "resource busy") $ do
liftIO $ do print e ; putStrLn "EXITING!!!"; putMVar rexit Nothing
empty
st <- get
setData $ Exit rexit
(abduce >> labelState (fromString "input") >> liftIO inputLoop >> empty)
<|> do
option "options" "show all options"
mbs <- liftIO $ readIORef rcb
liftIO $ mapM_ (\c ->do putStr c; putStr "|") $ map fst mbs
liftIO $ putStrLn ""
empty
<|> do
option "ps" "show threads"
liftIO $ showThreads st
empty
<|> do
option "log" "inspect the log of a thread"
th <- input (const True) "thread number>"
ml <- liftIO $ getStateFromThread th st
liftIO $ print $ fmap (\(Log _ _ log _) -> reverse log) ml
empty
<|> do
option "end" "exit"
liftIO $ putStrLn "exiting..."
abduce
killChilds
liftIO $ putMVar rexit Nothing
empty
<|> mx
return ()
threadDelay 10000
execCommandLine
stay rexit
where
type1 :: TransIO a -> Either String (Maybe a)
type1= undefined
keep' :: Typeable a => TransIO a -> IO (Maybe a)
keep' mx = do
liftIO $ hSetBuffering stdout LineBuffering
rexit <- newEmptyMVar
forkIO $ do
runTransient $ do
onException $ \(e :: SomeException ) -> liftIO $ putStr "keep block: " >> print e
onException $ \(e :: IOException) -> do
when (ioeGetErrorString e == "resource busy") $ do
liftIO $ do print e ; putStrLn "EXITING!!!"; putMVar rexit Nothing
liftIO $ putMVar rexit Nothing
empty
setData $ Exit rexit
mx
return ()
threadDelay 10000
forkIO $ execCommandLine
stay rexit
execCommandLine= do
args <- getArgs
let mindex = findIndex (\o -> o == "-p" || o == "--path" ) args
when (isJust mindex) $ do
let i= fromJust mindex +1
when (length args >= i) $ do
let path= args !! i
putStr "Executing: " >> print path
processLine path
exit :: Typeable a => a -> TransIO a
exit x= do
Exit rexit <- getSData <|> error "exit: not the type expected" `asTypeOf` type1 x
liftIO $ putMVar rexit $ Just x
stop
where
type1 :: a -> TransIO (Exit (MVar (Maybe a)))
type1= undefined
onNothing :: Monad m => m (Maybe b) -> m b -> m b
onNothing iox iox'= do
mx <- iox
case mx of
Just x -> return x
Nothing -> iox'
data Backtrack b= Show b =>Backtrack{backtracking :: Maybe b
,backStack :: [EventF] }
deriving Typeable
backCut :: (Typeable b, Show b) => b -> TransientIO ()
backCut reason= Transient $ do
delData $ Backtrack (Just reason) []
return $ Just ()
undoCut :: TransientIO ()
undoCut = backCut ()
{-# NOINLINE onBack #-}
onBack :: (Typeable b, Show b) => TransientIO a -> ( b -> TransientIO a) -> TransientIO a
onBack ac bac = registerBack (typeof bac) $ Transient $ do
Backtrack mreason stack <- getData `onNothing` (return $ backStateOf (typeof bac))
runTrans $ case mreason of
Nothing -> ac
Just reason -> do
bac reason
where
typeof :: (b -> TransIO a) -> b
typeof = undefined
onUndo :: TransientIO a -> TransientIO a -> TransientIO a
onUndo x y= onBack x (\() -> y)
{-# NOINLINE registerUndo #-}
registerBack :: (Typeable b, Show b) => b -> TransientIO a -> TransientIO a
registerBack witness f = Transient $ do
cont@(EventF _ x _ _ _ _ _ _ _ _ _) <- get
md <- getData `asTypeOf` (Just <$> return (backStateOf witness))
case md of
Just (Backtrack b []) -> setData $ Backtrack b [cont]
Just (bss@(Backtrack b (bs@((EventF _ x' _ _ _ _ _ _ _ _ _):_)))) ->
when (isNothing b) $ do
addrx <- addr x
addrx' <- addr x'
setData $ if addrx == addrx' then bss else Backtrack b (cont:bs)
Nothing -> setData $ Backtrack mwit [cont]
runTrans f
where
mwit= Nothing `asTypeOf` (Just witness)
addr x = liftIO $ return . hashStableName =<< (makeStableName $! x)
registerUndo :: TransientIO a -> TransientIO a
registerUndo f= registerBack () f
forward :: (Typeable b, Show b) => b -> TransIO ()
forward reason= noTrans $ do
Backtrack _ stack <- getData `onNothing` ( return $ backStateOf reason)
setData $ Backtrack(Nothing `asTypeOf` Just reason) stack
retry= forward ()
noFinish= continue
back :: (Typeable b, Show b) => b -> TransIO a
back reason = do
bs <- getData `onNothing` return (backStateOf reason)
goBackt bs
where
runClosure :: EventF -> TransIO a
runClosure EventF { xcomp = x } = unsafeCoerce x
runContinuation :: EventF -> a -> TransIO b
runContinuation EventF { fcomp = fs } = (unsafeCoerce $ compose fs)
goBackt (Backtrack _ [] )= empty
goBackt (Backtrack b (stack@(first : bs)) )= do
setData $ Backtrack (Just reason) stack
x <- runClosure first
Backtrack back bs' <- getData `onNothing` return (backStateOf reason)
case back of
Nothing -> runContinuation first x
justreason -> do
setData $ Backtrack justreason bs
goBackt $ Backtrack justreason bs
empty
backStateOf :: (Show a, Typeable a) => a -> Backtrack a
backStateOf reason= Backtrack (Nothing `asTypeOf` (Just reason)) []
data BackPoint a = BackPoint (IORef [a -> TransIO()])
backPoint :: (Typeable reason,Show reason) => TransIO (BackPoint reason)
backPoint = do
point <- liftIO $ newIORef []
return () `onBack` (\e -> do
rs <- liftIO $ readIORef point
mapM_ (\r -> r e) rs)
return $ BackPoint point
onBackPoint (BackPoint ref) handler= liftIO $ atomicModifyIORef ref $ \rs -> (handler:rs,())
undo :: TransIO a
undo= back ()
newtype Finish= Finish String deriving Show
instance Exception Finish
onFinish :: (Finish ->TransIO ()) -> TransIO ()
onFinish f= onException' (return ()) f
onFinish' ::TransIO a ->(Finish ->TransIO a) -> TransIO a
onFinish' proc f= proc `onException'` f
initFinish = cutExceptions
finish :: String -> TransIO ()
finish reason= (throwt $ Finish reason) <|> return()
checkFinalize v=
case v of
SDone -> stop
SLast x -> return x
SError e -> throwt e
SMore x -> return x
onException :: Exception e => (e -> TransIO ()) -> TransIO ()
onException exc= return () `onException'` exc
exceptionPoint :: Exception e => TransIO (BackPoint e)
exceptionPoint = do
point <- liftIO $ newIORef []
return () `onException'` (\e -> do
rs<- liftIO $ readIORef point
mapM_ (\r -> r e) rs)
return $ BackPoint point
onExceptionPoint :: Exception e => BackPoint e -> (e -> TransIO()) -> TransIO ()
onExceptionPoint= onBackPoint
onException' :: Exception e => TransIO a -> (e -> TransIO a) -> TransIO a
onException' mx f= onAnyException mx $ \e -> do
return () !> "EXCEPTION HANDLER EXEC"
case fromException e of
Nothing -> do
Backtrack r stack <- getData `onNothing` return (backStateOf e)
setData $ Backtrack r $ tail stack
back e
empty
Just e' -> f e'
where
onAnyException :: TransIO a -> (SomeException ->TransIO a) -> TransIO a
onAnyException mx exc= ioexp `onBack` exc
ioexp = Transient $ do
st <- get
(mr,st') <- liftIO $ (runStateT
(do
case event st of
Nothing -> do
r <- runTrans mx
modify $ \s -> s{event= Just $ unsafeCoerce r}
runCont st
was <- getData `onNothing` return NoRemote
when (was /= WasRemote) $ setData WasParallel
return Nothing
Just r -> do
modify $ \s -> s{event=Nothing}
return $ unsafeCoerce r) st)
`catch` exceptBack st
put st'
return mr
exceptBack st = \(e ::SomeException) -> do
runStateT ( runTrans $ back e ) st
`catch` exceptBack st
cutExceptions :: TransIO ()
cutExceptions= backCut (undefined :: SomeException)
continue :: TransIO ()
continue = forward (undefined :: SomeException)
catcht :: Exception e => TransIO b -> (e -> TransIO b) -> TransIO b
catcht mx exc= do
rpassed <- liftIO $ newIORef False
sandbox $ do
r <- onException' mx (\e -> do
passed <- liftIO $ readIORef rpassed
if not passed then continue >> exc e else do
Backtrack r stack <- getData `onNothing` return (backStateOf e)
setData $ Backtrack r $ tail stack
back e
empty )
liftIO $ writeIORef rpassed True
return r
where
sandbox mx= do
exState <- getState <|> return (backStateOf (undefined :: SomeException))
mx
<*** do setState exState
throwt :: Exception e => e -> TransIO a
throwt = back . toException