@@ -1,9 +1,16 @@
1{-# LANGUAGE ExistentialQuantification #-}
1{-# LANGUAGE ExistentialQuantification #-}
2{-# LANGUAGE FlexibleContexts #-}
2{-# LANGUAGE FlexibleContexts #-}
3+{-# LANGUAGE RecordWildCards #-}
4−module Duct.Event
5+module Duct.Context
5 ( Context(..)
6 ( Context(..)
6 , initContext
7 , initContext
9 , modifyData
16 , modifyData
@@ -11,33 +18,39 @@ module Duct.Event
21+import Control.Applicative (Alternative(..))
14import Control.Concurrent (ThreadId)
22import Control.Concurrent (ThreadId)
15import Control.Concurrent.STM (TChan)
23import Control.Concurrent.STM (TChan)
16−import Control.Monad.State (MonadState, gets, modify)
24+import Control.Monad.State (MonadState, gets, modify, StateT, MonadPlus)
25+import qualified Control.Monad.Trans.State.Lazy as Lazy (get, gets, modify, put)
17import Data.Dynamic (TypeRep, Typeable, typeOf)
26import Data.Dynamic (TypeRep, Typeable, typeOf)
18import Data.IORef (IORef)
27import Data.IORef (IORef)
19import qualified Data.Map as M
28import qualified Data.Map as M
20import Unsafe.Coerce (unsafeCoerce)
29import Unsafe.Coerce (unsafeCoerce)
22------------------------------------------------------------------------------
32------------------------------------------------------------------------------
23-- State of a continuation
33-- State of a continuation
24------------------------------------------------------------------------------
34------------------------------------------------------------------------------
28-- | Describes the context of a computation.
36-- | Describes the context of a computation.
29−data Context = forall m a b. Context
37+data Context = Context
30− { event :: Maybe SData -- untyped, XXX rename to mailbox - can we do
38+ { event :: Maybe () -- untyped, XXX rename to mailbox - can we do
31 -- away with this?
39 -- away with this?
32 -- ^ event data to use in a continuation in a new thread
40 -- ^ event data to use in a continuation in a new thread
34 -- the 'm' and 'f' in an 'm >>= f' operation of the monad
42 -- the 'm' and 'f' in an 'm >>= f' operation of the monad
35 -- In nested binds we store the current m only, but the whole stack of fs
43 -- In nested binds we store the current m only, but the whole stack of fs
36− , currentm :: m a
44+ , currentm :: Any -- untyped, the real type is: m a
37− , fstack :: [a -> m b]
45+ , fstack :: [Any] -- untyped, the real type is: [a -> m b]
38 -- ^ List of continuations
46 -- ^ List of continuations
48+ -- log only when there is a restore or if we are teleporting
49+ -- We can use a HasLog constraint to statically enable/disable logging
50+ -- , journal :: Maybe Log
51+ , location :: Location
40− , mfData :: M.Map TypeRep SData
53+ , mfData :: M.Map TypeRep Any -- untyped, type coerced
41 -- ^ State data accessed with get or put operations
54 -- ^ State data accessed with get or put operations
43 -- XXX All of the following can be removed
56 -- XXX All of the following can be removed
@@ -92,15 +105,68 @@ initContext
92 -> IORef Int
105 -> IORef Int
93 -> Context
106 -> Context
94initContext x childChan pending credit =
107initContext x childChan pending credit =
95− Context { event = mempty
108+ Context { event = mempty
96− , currentm = x
109+ , currentm = unsafeCoerce x
97 , fstack = []
110 , fstack = []
98 , mfData = mempty
112 , mfData = mempty
99 , parentChannel = Nothing
113 , parentChannel = Nothing
100 , childChannel = childChan
114 , childChannel = childChan
101 , pendingThreads = pending
115 , pendingThreads = pending
102 , threadCredit = credit }
116 , threadCredit = credit }
118+------------------------------------------------------------------------------
119+-- Where is the computation running?
120+------------------------------------------------------------------------------
122+data Location = Local | ForkedThread | RemoteNode
123+ deriving (Typeable, Eq, Show)
125+getLocation :: Monad m => StateT Context m Location
126+getLocation = Lazy.gets location
128+setLocation :: Monad m => Location -> StateT Context m ()
129+setLocation loc = Lazy.modify $ \Context { .. } -> Context { location = loc, .. }
131+------------------------------------------------------------------------------
132+-- Saving and restoring the context of a computation
133+------------------------------------------------------------------------------
135+-- Save the 'm' and 'f', in case we migrate to a new thread before the current
136+-- bind operation completes, we run the operation manually in the new thread
137+-- using this context.
138+saveContext :: Monad m => f a -> (a -> f b) -> StateT Context m ()
140+ Lazy.modify $ \Context { fstack = fs, .. } ->
141+ Context { currentm = unsafeCoerce m
142+ , fstack = unsafeCoerce f : fs
145+-- pop the top function from the continuation stack, create the next closure,
146+-- set it as the current closure and return it.
147+restoreContext :: (Alternative f, Monad m) => Maybe a -> StateT Context m (f b)
148+restoreContext x = do
149+ -- XXX fstack must be non-empty when this is called.
150+ ctx@Context { fstack = f:fs } <- Lazy.get
152+ let mres = case x of
154+ Just y -> (unsafeCoerce f) y
156+ Lazy.put ctx { currentm = unsafeCoerce mres, fstack = fs }
160+continueContext :: MonadPlus m => Context -> m a
161+continueContext Context { currentm = m, fstack = fs } =
162+ unsafeCoerce m >>= runfStack (unsafeCoerce fs)
166+ -- runfStack :: Monad m => [a -> AsyncT m a] -> a -> AsyncT m b
167+ runfStack [] _ = empty
168+ runfStack (f:ff) x = f x >>= runfStack ff
104------------------------------------------------------------------------------
170------------------------------------------------------------------------------
105-- * Extensible State: Session Data Management
171-- * Extensible State: Session Data Management
106------------------------------------------------------------------------------
172------------------------------------------------------------------------------