{-# LANGUAGE CPP #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE ConstraintKinds #-}
module Transient.Internals where
import Control.Applicative
import Control.Monad.State
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 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 qualified Data.ByteString.Lazy.Char8 as BSL
import Data.Typeable
import Control.Monad.Fail
import System.Directory
#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 (unsafePerformIO myThreadId, y)) x
infixr 0 !>
#else
tshow :: a -> x -> x
tshow _ y= y
{-# INLINE (!>) #-}
(!>) :: a -> b -> a
(!>) = const
#endif
tr x= return () !> x
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)
, parseContext :: ParseContext
, execMode :: ExecMode
} deriving Typeable
data ParseContext = ParseContext { more :: TransIO (StreamData BSL.ByteString)
, buffer :: BSL.ByteString
, done :: IORef Bool} deriving Typeable
class MonadState EventF m => TransMonad m
instance MonadState EventF m => TransMonad m
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
liftTrans :: StateIO (Maybe b) -> TransIO b
liftTrans mx= do
r <- noTrans mx
case r of
Nothing -> empty
Just x -> return x
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
, parseContext = ParseContext (return SDone) mempty undefined
, execMode = Serial}
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 :: TransMonad 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
liftIO $ (writeIORef r1 $ Just f)
mr <- liftIO (readIORef r2)
case mr of
Nothing -> empty
Just x -> return $ f x
xparallel r1 r2 = do
mr <- liftIO (readIORef r1)
case mr of
Nothing -> do
p <- gets execMode
if p== Serial then empty else do
x <- mx
liftIO $ (writeIORef r2 $ Just x)
mr <- liftIO (readIORef r1)
case mr of
Nothing -> empty
Just f -> return $ f x
Just f -> do
x <- mx
liftIO $ (writeIORef r2 $ Just x)
return $ f x
data ExecMode = Remote | Parallel | Serial
deriving (Typeable, Eq, Show)
fullStop :: TransIO stop
fullStop= do modify $ \s ->s{execMode= Remote} ; 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 <- gets execMode
if was == Remote
then return Nothing
else case mx of
Nothing -> runTrans y
justx -> return justx
instance MonadFail TransIO where
fail _ = mzero
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
instance Show ParseError where
show (ParseError s)= "ParseError " ++ s
instance Exception ParseError
read' s= case readsPrec' 0 s of
[(x,"")] -> x
_ -> throw $ ParseError $ "reading " ++ s
readsPrec' n = unsafePerformIO . readWithErr n
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
infixl 4 <***, <**, **>
(<|) :: 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 onemore sem = atomicModifyIORef sem $ \n ->
let one = if onemore then 1 else 0
in if n + one > 0 then(n - 1, True) else (n, False)
signalQSemB sem = atomicModifyIORef 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
cloneInChild name= do
st <- get
rchs <- liftIO $ newMVar []
label <- liftIO $ newIORef (Alive, if not $ null name then BS.pack name else mempty)
let st' = st { parent = Just st
, children = rchs
, labelth = label }
liftIO $ do
atomicModifyIORef (labelth st) $ \(_, label) -> ((Parent,label),())
hangThread st st'
return st'
removeChild :: (MonadIO m,TransMonad m) => m ()
removeChild = do
st <- get
let mparent = parent st
case mparent of
Nothing -> return ()
Just parent -> do
sts <- liftIO $ modifyMVar (children parent) $ \ths -> do
let (xs,sts)= partition (\st' -> threadId st' /= threadId st) ths
ys <- case sts of
[] -> return []
st':_ -> readMVar $ children st'
return (xs ++ ys,sts)
put parent
case sts of
[] -> return()
st':_ -> do
(status,_) <- liftIO $ readIORef $ labelth st'
if status == Listener || threadId parent == threadId st then return () else liftIO $ (killThread . threadId) st'
oneThread :: TransIO a -> TransIO a
oneThread comp = do
st <- cloneInChild "oneThread"
let rchs= children st
x <- comp
th <- liftIO myThreadId
chs <- liftIO $ readMVar rchs
liftIO $ mapM_ (killChildren1 th) chs
return x
where
killChildren1 :: ThreadId -> EventF -> IO ()
killChildren1 th state = do
forkIO $ 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'
return()
labelState :: (MonadIO m,TransMonad m) => BS.ByteString -> m ()
labelState l = do
st <- get
liftIO $ atomicModifyIORef (labelth st) $ \(status,prev) -> ((status, prev <> BS.pack "," <> l), ())
threadState thid= do
st <- findState match =<< topState
return $ threadId st :: TransIO ThreadId
where
match st= do
(_,lab) <-liftIO $ readIORef $ labelth st
return $ if lab == thid then True else False
killState thid= do
st <- findState match =<< topState
liftIO $ killBranch' st
where
match st= do
(_,lab) <-liftIO $ readIORef $ labelth st
return $ if lab == thid then True else False
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 :: TransMonad 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
forkIO $ 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)
return ()
getData :: (TransMonad 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 :: (TransMonad m, Typeable a) => a -> m ()
setData x = modify $ \st -> st { mfData = M.insert t (unsafeCoerce x) (mfData st) }
where t = typeOf x
modifyData :: (TransMonad 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' :: (TransMonad 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 :: (TransMonad m, Typeable a) => (Maybe a -> Maybe a) -> m ()
modifyState = modifyData
setState :: (TransMonad m, Typeable a) => a -> m ()
setState = setData
delData :: (TransMonad m, Typeable a) => a -> m ()
delData x = modify $ \st -> st { mfData = M.delete (typeOf x) (mfData st) }
delState :: (TransMonad m, Typeable a) => a -> m ()
delState = delData
newtype Ref a = Ref (IORef a)
newRState:: (MonadIO m,TransMonad m, Typeable a) => a -> m (IORef a)
newRState x= do
ref@(Ref rx) <- Ref <$> liftIO (newIORef x)
setData ref
return rx
setRState:: (MonadIO m,TransMonad m, Typeable a) => a -> m ()
setRState x= do
Ref ref <- getData `onNothing` do
ref <- Ref <$> liftIO (newIORef x)
setData ref
return ref
liftIO $ atomicModifyIORef ref $ const (x,())
getRData :: (MonadIO m, TransMonad 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
s <- get
mx <|> (modify (const s) >> 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 $ atomicModifyIORef rglobalId $ \n -> (n +1,n)
rglobalId= unsafePerformIO $ newIORef (0 :: Int)
genId :: TransMonad m => m Int
genId = do
st <- get
let n = mfSequence st
put st { mfSequence = n + 1 }
return n
getPrevId :: TransMonad 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
was <- gets execMode
r <- x <** modify (\s ->s{execMode= Remote})
modify $ \s -> s{execMode= was}
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) <|> 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'
abduce = async $ return ()
fork :: TransIO () -> TransIO ()
fork proc= (abduce >> proc >> empty) <|> return()
parallel :: IO (StreamData b) -> TransIO (StreamData b)
parallel ioaction = Transient $ do
modify $ \s -> s{execMode=let rs= execMode s in if rs /= Remote then Parallel else rs}
cont <- get
case event cont of
j@(Just _) -> do
put cont { event = Nothing }
return $ unsafeCoerce j
Nothing -> do
liftIO $ atomicModifyIORef (labelth cont) $ \(_, lab) -> ((Parent, lab), ())
liftIO $ loop cont ioaction
return Nothing
loop :: EventF -> IO (StreamData t) -> IO ()
loop parentc rec = forkMaybe True parentc $ \cont -> do
liftIO $ atomicModifyIORef (labelth cont) $ \(_,label) -> ((Listener,label),())
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 False cont $ iocont more
loop'
where
setworker cont= liftIO $ atomicModifyIORef (labelth cont) $ \(_,lab) -> ((Alive,lab),())
iocont dat cont = do
let cont'= cont{event= Just $ unsafeCoerce dat}
runStateT (runCont cont') cont'
return ()
loop'
return ()
where
{-# INLINABLE forkMaybe #-}
forkMaybe :: Bool -> EventF -> (EventF -> IO ()) -> IO ()
forkMaybe onemore parent proc = do
case maxThread parent of
Nothing -> forkIt parent proc
Just sem -> do
dofork <- waitQSemB onemore sem
if dofork then forkIt parent proc
else proc parent
`catch` \e ->exceptBack parent e >> return()
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 ())
_ -> return ()
case maxThread cont of
Just sem -> signalQSemB sem
Nothing -> return ()
when(not $ freeTh parent ) $ do
th <- myThreadId
(can,label) <- atomicModifyIORef (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
forkIO $ do
ths <- modifyMVar childs $ \ths -> return ([],ths)
mapM_ (killChildren . children) ths
mapM_ (\th -> do
(status,_) <- readIORef $ labelth th
when (status /= Listener && status /= Parent) $ killThread $ threadId th !> ("killChildren",threadId th, status)) ths >> return ()
return ()
react
:: ((eventdata -> IO response) -> IO ())
-> IO response
-> TransIO eventdata
react setHandler iob= do
st <- cloneInChild "react"
liftIO $ atomicModifyIORef (labelth st) $ \(_,label) -> ((Listener,label),())
Transient $ do
modify $ \s -> s{execMode=let rs= execMode s in if rs /= Remote then Parallel else rs}
cont <- get
case event cont of
Nothing -> do
liftIO $ setHandler $ \dat ->do
runStateT (runCont cont) st{event= Just $ unsafeCoerce dat} `catch` exceptBack cont
iob
return Nothing
j@(Just _) -> do
put cont{event=Nothing}
return $ unsafeCoerce j
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
let msg= "Enter "++sret++"\t\tto: " ++ message++"\n"
inputf flag sret msg Nothing ( == sret)
liftIO $ putStr "\noption: " >> putStrLn sret
return ret
inputf :: (Show a, Read a,Typeable a) => Bool -> String -> String -> Maybe a -> (a -> Bool) -> TransIO a
inputf remove ident message mv cond = do
let loop= do
liftIO $ putStr message >> hFlush stdout
str <- react (addConsoleAction ident message) (return ())
when remove $ do removeChild; liftIO $ delConsoleAction ident
c <- liftIO $ readIORef rconsumed
if c then returnm mv else do
let rr = read1 str
case (rr,str) of
(Nothing,_) -> do (liftIO $ when (isJust mv) $ putStrLn ""); returnm mv
(Just x,"") -> do (liftIO $ do writeIORef rconsumed True; print x); returnm mv
(Just x,_) -> if cond x
then liftIO $ do
writeIORef rconsumed True
print x
return x
else do liftIO $ when (isJust mv) $ putStrLn ""
returnm mv
loop
where
returnm (Just x)= return x
returnm _ = empty
read1 s= r
where
typ= typeOf $ fromJust r
r = if typ == typeOf "" then Just $ unsafeCoerce s
else if typ == typeOf (BS.pack "") then Just $ unsafeCoerce $ BS.pack s
else if typ == typeOf (BSL.pack "") then Just $ unsafeCoerce $ BSL.pack 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
inputf True "input" prompt mv cond
rcb= unsafePerformIO $ newIORef [] :: IORef [ (String,String,String -> IO())]
addConsoleAction :: String -> String -> (String -> IO ()) -> IO ()
addConsoleAction name message cb= atomicModifyIORef rcb $ \cbs ->
((name,message, cb) : filter ((/=) name . fst) cbs ,())
where
fst (x,_,_)= x
delConsoleAction :: String -> IO ()
delConsoleAction name= atomicModifyIORef rcb $ \cbs -> (filter ((/=) name . fst) cbs,())
where
fst (x,_,_)= x
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
prompt <- readIORef rprompt
when (not $ null prompt) $ do putStr prompt ; hFlush stdout
line <- getLine
processLine line
inputLoop
`catch` \(SomeException _) -> 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 (\(_,_,p)-> p) mbs
mapM' _ []= 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==1
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 -> (not $ elem 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
void $ forkIO $ do
let logFile= "trans.log"
void $ runTransient $ do
liftIO $ removeFile logFile `catch` \(e :: IOError) -> return ()
onException $ \(e :: SomeException) -> do
liftIO $ print e
empty
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
do
option "options" "show all options"
mbs <- liftIO $ readIORef rcb
liftIO $ mapM_ (\c ->do putStr c; putStr "|") $ map (\(fst,_,_) -> fst)mbs
d <- input' (Just "n") (\x -> x=="y" || x =="n" || x=="Y" || x=="N") "\nDetails? N/y "
when (d == "y") $
let line (x,y,_)= putStr y
in liftIO $ mapM_ line mbs
liftIO $ putStrLn ""
empty
<|> do
option "ps" "show threads"
liftIO $ showThreads st
empty
<|> do
option "errs" "show exceptions log"
c <- liftIO $ readFile logFile `catch` \(e:: IOError) -> return ""
liftIO . putStrLn $ if null c then "no errors logged" else c
empty
<|> do
option "end" "exit"
liftIO $ putStrLn "exiting..."
abduce
killChilds
liftIO $ putMVar rexit Nothing
empty
<|> mx
#ifndef ghcjs_HOST_OS
<|> do
abduce
liftIO $ execCommandLine
labelState (fromString "input")
liftIO inputLoop
empty
#endif
return ()
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
void $ forkIO $ do
void $ runTransient $ do
onException $ \(e :: SomeException ) -> do
top <- topState
liftIO $ do
th <- myThreadId
putStr $ show th
putStr ": "
print e
putStrLn "Threads:"
showThreads top
empty
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
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
tr "onBack"
Backtrack mreason stack <- getData `onNothing` (return $ backStateOf (typeof bac))
runTrans $ case mreason of
Nothing -> ac
Just reason -> 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
tr "registerBack"
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) $
setData $ Backtrack b (cont:bs)
Nothing -> setData $ Backtrack mwit [cont]
runTrans $ return () >> f
where
mwit= Nothing `asTypeOf` (Just witness)
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
backtrack :: TransIO a
backtrack= return $ error "backtrack should be called at the end of an exception handler with no `forward`, `continue` or `retry` on it"
retry= forward ()
noFinish= continue
back :: (Typeable b, Show b) => b -> TransIO a
back reason = do
tr "back"
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) bs
x <- runClosure first !> ("RUNCLOSURE",length stack)
Backtrack back bs' <- getData `onNothing` return (backStateOf reason)
case back of
Nothing -> do
setData $ Backtrack (Just reason) stack
st <- get
runContinuation first x `catcht` (\e -> liftIO(exceptBack st e) >> empty) !> "FORWARD EXEC"
justreason -> do
goBackt $ Backtrack justreason bs !> ("BACK AGAIN",back)
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 :: MonadIO m => BackPoint t -> (t -> TransIO ()) -> m ()
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
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
modify $ \s -> s{execMode=let rs= execMode s in if rs /= Remote then Parallel else rs}
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
tr "catched"
runStateT ( runTrans $ back e ) st
whileException :: Exception e => TransIO b -> (e -> TransIO()) -> TransIO b
whileException mx fixexc = mx `catcht` \e -> do fixexc e; whileException mx fixexc
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
st <- get
(mx,st') <- liftIO $ runStateT ( runTrans $ mx ) st `catch` \e -> runStateT ( runTrans $ exc e ) st
put st'
case mx of
Just x -> return x
Nothing -> empty
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
return () !> ("CATCHT passed", passed)
if not passed then continue >> exc e else do
Backtrack r stack <- getData `onNothing` return (backStateOf e)
setData $ Backtrack r $ tail stack
back e
return () !> "AFTER BACK"
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