packages feed

SoOSiM 0.1 → 0.2.0.0

raw patch · 11 files changed

+1211/−427 lines, 11 filesdep +concurrent-supplydep −ghcPVP ok

version bump matches the API change (PVP)

Dependencies added: concurrent-supply

Dependencies removed: ghc

API changes (from Hackage documentation)

- SoOSiM: ComponentMsg :: ComponentId -> Dynamic -> ComponentInput
- SoOSiM: Deinitialize :: ComponentInput
- SoOSiM: Initialize :: ComponentInput
- SoOSiM: NodeMsg :: NodeId -> Dynamic -> ComponentInput
- SoOSiM: class ComponentIface s
- SoOSiM: data ComponentInput
- SoOSiM: data SimM a
- SoOSiM: invokeNoWait :: Maybe ComponentId -> ComponentId -> Dynamic -> SimM ()
- SoOSiM: registerComponent :: ComponentIface s => s -> SimM ()
- SoOSiM: runIO :: IO a -> SimM a
- SoOSiM.Simulator: componentNode :: ComponentId -> SimMonad NodeId
- SoOSiM.Simulator: execStep :: SimState -> IO SimState
- SoOSiM.Simulator: execStepSmall :: SimState -> IO SimState
- SoOSiM.Simulator: incrSendCounter :: ComponentId -> ComponentId -> Node -> SimMonad ()
- SoOSiM.Simulator: modifyNode :: NodeId -> (Node -> Node) -> SimMonad ()
- SoOSiM.Simulator: modifyNodeM :: NodeId -> (Node -> SimMonad ()) -> SimMonad ()
- SoOSiM.Simulator: updateMsgBuffer :: ComponentId -> ComponentInput -> Node -> SimMonad ()
- SoOSiM.Simulator: updateTraceBuffer :: ComponentId -> String -> Node -> Node
- SoOSiM.Types: ComponentMsg :: ComponentId -> Dynamic -> ComponentInput
- SoOSiM.Types: Deinitialize :: ComponentInput
- SoOSiM.Types: Idle :: ComponentStatus a
- SoOSiM.Types: Initialize :: ComponentInput
- SoOSiM.Types: NodeMsg :: NodeId -> Dynamic -> ComponentInput
- SoOSiM.Types: Running :: ComponentStatus a
- SoOSiM.Types: SC :: s -> StateContainer
- SoOSiM.Types: SimM :: Coroutine (RequestOrYield Unique Dynamic) SimMonad a -> SimM a
- SoOSiM.Types: WaitingForMsg :: ComponentId -> (Dynamic -> SimM a) -> ComponentStatus a
- SoOSiM.Types: class ComponentIface s
- SoOSiM.Types: componentMap :: SimState -> Map String StateContainer
- SoOSiM.Types: data ComponentInput
- SoOSiM.Types: data StateContainer
- SoOSiM.Types: instance Functor SimM
- SoOSiM.Types: instance Monad SimM
- SoOSiM.Types: instance Show ComponentInput
- SoOSiM.Types: instance Typeable Unique
- SoOSiM.Types: newtype SimM a
- SoOSiM.Types: runSimM :: SimM a -> Coroutine (RequestOrYield Unique Dynamic) SimMonad a
+ SoOSiM: Message :: a -> ReturnAddress -> Input a
+ SoOSiM: class ComponentInterface s where type family Send s type family Receive s type family State s
+ SoOSiM: class Typeable a
+ SoOSiM: componentLookupN :: ComponentInterface iface => Maybe NodeId -> iface -> Sim (Maybe ComponentId)
+ SoOSiM: createComponentN :: (ComponentInterface iface, Typeable (Receive iface)) => iface -> NodeId -> Sim ComponentId
+ SoOSiM: createComponentNP :: (ComponentInterface iface, Typeable (Receive iface)) => Maybe NodeId -> Maybe ComponentId -> iface -> Sim ComponentId
+ SoOSiM: data Dynamic :: *
+ SoOSiM: data Input a
+ SoOSiM: data Sim a
+ SoOSiM: ignore :: a -> Sim ()
+ SoOSiM: invokeAsync :: (ComponentInterface iface, Typeable (Receive iface), Typeable (Send iface)) => iface -> ComponentId -> Receive iface -> (Send iface -> Sim ()) -> Sim ()
+ SoOSiM: invokeAsyncS :: (ComponentInterface iface, Typeable (Receive iface), Typeable (Send iface)) => iface -> Maybe ComponentId -> ComponentId -> (Receive iface) -> (Send iface -> Sim ()) -> Sim ()
+ SoOSiM: invokeS :: (ComponentInterface iface, Typeable (Receive iface), Typeable (Send iface)) => iface -> Maybe ComponentId -> ComponentId -> Receive iface -> Sim (Send iface)
+ SoOSiM: readMemoryN :: Maybe NodeId -> Int -> Sim Dynamic
+ SoOSiM: respond :: (ComponentInterface iface, Typeable (Send iface)) => iface -> ReturnAddress -> (Send iface) -> Sim ()
+ SoOSiM: respondS :: (ComponentInterface iface, Typeable (Send iface)) => iface -> Maybe ComponentId -> ReturnAddress -> (Send iface) -> Sim ()
+ SoOSiM: returnAddress :: ReturnAddress -> ComponentId
+ SoOSiM: runSTM :: STM a -> Sim a
+ SoOSiM: tick :: SimState -> IO SimState
+ SoOSiM: unmarshall :: Typeable a => String -> Dynamic -> a
+ SoOSiM: writeMemoryN :: Typeable a => Maybe NodeId -> Int -> a -> Sim ()
+ SoOSiM.Types: Message :: a -> ReturnAddress -> Input a
+ SoOSiM.Types: RA :: (ComponentId, TVar Dynamic) -> ReturnAddress
+ SoOSiM.Types: ReadyToIdle :: ComponentStatus a
+ SoOSiM.Types: ReadyToRun :: ComponentStatus a
+ SoOSiM.Types: Sim :: SimInternal a -> Sim a
+ SoOSiM.Types: WaitingFor :: ComponentId -> (() -> Sim (State a)) -> ComponentStatus a
+ SoOSiM.Types: class ComponentInterface s where type family Send s type family Receive s type family State s
+ SoOSiM.Types: componentIface :: ComponentContext -> s
+ SoOSiM.Types: data Input a
+ SoOSiM.Types: instance Functor Sim
+ SoOSiM.Types: instance Monad Sim
+ SoOSiM.Types: instance MonadState SimState Sim
+ SoOSiM.Types: instance MonadState SimState SimInternal
+ SoOSiM.Types: instance MonadUnique Sim
+ SoOSiM.Types: instance MonadUnique SimInternal
+ SoOSiM.Types: instance Show (Input a)
+ SoOSiM.Types: newtype ReturnAddress
+ SoOSiM.Types: newtype Sim a
+ SoOSiM.Types: runSim :: Sim a -> SimInternal a
+ SoOSiM.Types: type SimInternal = Coroutine (RequestOrYield Unique ()) SimMonad
+ SoOSiM.Types: type Unique = Int
+ SoOSiM.Types: unRA :: ReturnAddress -> (ComponentId, TVar Dynamic)
- SoOSiM: Tick :: ComponentInput
+ SoOSiM: Tick :: Input a
- SoOSiM: componentBehaviour :: ComponentIface s => s -> ComponentInput -> SimM s
+ SoOSiM: componentBehaviour :: ComponentInterface s => s -> State s -> Input (Receive s) -> Sim (State s)
- SoOSiM: componentCreator :: SimM ComponentId
+ SoOSiM: componentCreator :: Sim ComponentId
- SoOSiM: componentLookup :: Maybe NodeId -> ComponentName -> SimM (Maybe ComponentId)
+ SoOSiM: componentLookup :: ComponentInterface iface => iface -> Sim (Maybe ComponentId)
- SoOSiM: componentName :: ComponentIface s => s -> ComponentName
+ SoOSiM: componentName :: ComponentInterface s => s -> ComponentName
- SoOSiM: createComponent :: Maybe NodeId -> Maybe ComponentId -> String -> SimM ComponentId
+ SoOSiM: createComponent :: (ComponentInterface iface, Typeable (Receive iface)) => iface -> Sim ComponentId
- SoOSiM: createNode :: SimM NodeId
+ SoOSiM: createNode :: Sim NodeId
- SoOSiM: getComponentId :: SimM ComponentId
+ SoOSiM: getComponentId :: Sim ComponentId
- SoOSiM: getNodeId :: SimM NodeId
+ SoOSiM: getNodeId :: Sim NodeId
- SoOSiM: initState :: ComponentIface s => s
+ SoOSiM: initState :: ComponentInterface s => s -> State s
- SoOSiM: invoke :: Maybe ComponentId -> ComponentId -> Dynamic -> SimM Dynamic
+ SoOSiM: invoke :: (ComponentInterface iface, Typeable (Receive iface), Typeable (Send iface)) => iface -> ComponentId -> Receive iface -> Sim (Send iface)
- SoOSiM: readMemory :: Maybe NodeId -> Int -> SimM Dynamic
+ SoOSiM: readMemory :: Int -> Sim Dynamic
- SoOSiM: traceMsg :: String -> SimM ()
+ SoOSiM: traceMsg :: String -> Sim ()
- SoOSiM: writeMemory :: Maybe NodeId -> Int -> Dynamic -> SimM ()
+ SoOSiM: writeMemory :: Typeable a => Int -> a -> Sim ()
- SoOSiM: yield :: ComponentIface s => s -> SimM s
+ SoOSiM: yield :: a -> Sim a
- SoOSiM.Types: CC :: ComponentId -> TVar (ComponentStatus s) -> TVar s -> ComponentId -> TVar [ComponentInput] -> [String] -> TVar SimMetaData -> ComponentContext
+ SoOSiM.Types: CC :: s -> ComponentId -> ComponentId -> TVar (ComponentStatus s) -> TVar (State s) -> TVar [Input Dynamic] -> [String] -> TVar SimMetaData -> ComponentContext
- SoOSiM.Types: SimState :: ComponentId -> NodeId -> IntMap Node -> UniqSupply -> Map String StateContainer -> SimState
+ SoOSiM.Types: SimState :: ComponentId -> NodeId -> IntMap Node -> Supply -> SimState
- SoOSiM.Types: Tick :: ComponentInput
+ SoOSiM.Types: Tick :: Input a
- SoOSiM.Types: componentBehaviour :: ComponentIface s => s -> ComponentInput -> SimM s
+ SoOSiM.Types: componentBehaviour :: ComponentInterface s => s -> State s -> Input (Receive s) -> Sim (State s)
- SoOSiM.Types: componentName :: ComponentIface s => s -> ComponentName
+ SoOSiM.Types: componentName :: ComponentInterface s => s -> ComponentName
- SoOSiM.Types: componentState :: ComponentContext -> TVar s
+ SoOSiM.Types: componentState :: ComponentContext -> TVar (State s)
- SoOSiM.Types: initState :: ComponentIface s => s
+ SoOSiM.Types: initState :: ComponentInterface s => s -> State s
- SoOSiM.Types: msgBuffer :: ComponentContext -> TVar [ComponentInput]
+ SoOSiM.Types: msgBuffer :: ComponentContext -> TVar [Input Dynamic]
- SoOSiM.Types: type SimMonad = StateT SimState IO
+ SoOSiM.Types: type SimMonad = StateT SimState STM
- SoOSiM.Types: uniqueSupply :: SimState -> UniqSupply
+ SoOSiM.Types: uniqueSupply :: SimState -> Supply

Files

+ README.md view
@@ -0,0 +1,476 @@+SoOSiM - Abstract Full System Simulator+=======================================++Installation+------------++* Download the latest Haskell Platform from: http://hackage.haskell.org/platform/+* Execute on the command-line: `cabal update`+* Execute on the command-line: `cabal install SoOSiM`++Creating OS Components+----------------------++We jump straight into some code, by showing the description of the *Memory Manager* (http://www.soos-project.eu/wiki/index.php/Application_Cases#Memory_Manager)++#### ./examples/MemoryManager.hs+```haskell+{-# LANGUAGE TypeFamilies #-}+module MemoryManager where++import SoOSiM++import MemoryManager.Types+import MemoryManager.Util++memoryManager :: MemState -> Input MemCommand -> Sim MemState+memoryManager s (Message content retAddr) = do+  case content of+    (Register memorySource) -> do+      yield $ s {addressLookup = memorySource:(addressLookup s)}++    (Read addr) -> do+      let src = checkAddress (addressLookup s) addr+      case (sourceId src) of+        Nothing -> do+          addrVal <- readMemory addr+          respond MemoryManager retAddr addrVal+          yield s+        Just remote -> do+          response <- invoke MemoryManager remote content+          respond MemoryManager retAddr response+          yield s++    (Write addr val) -> do+      let src = checkAddress (addressLookup s) addr+      case (sourceId src) of+        Nothing -> do+          addrVal <- writeMemory addr val+          yield s+        Just remote -> do+          invokeAsync MemoryManager remote content ignore+          yield s++memoryManager s _ = yield s++instance ComponentInterface MemoryManager where+  type State   MemoryManager = MemState+  type Receive MemoryManager = MemCommand+  type Send    MemoryManager = Dynamic+  initState _                = (MemState [])+  componentName _            = "MemoryManager"+  componentBehaviour _       = memoryManager+```++#### ./examples/MemoryManager/Types.hs+```haskell+{-# LANGUAGE DeriveDataTypeable        #-}+{-# LANGUAGE ExistentialQuantification #-}+module MemoryManager.Types where++import SoOSiM++data MemorySource+  = MemorySource+  { baseAddress :: Int+  , scope       :: Int+  , sourceId    :: Maybe ComponentId+  }+++data MemState =+  MemState { addressLookup :: [MemorySource]+           }++data MemCommand = Register Int Int (Maybe ComponentId)+                | Read     Int+                | forall a . Typeable a => Write Int a+  deriving Typeable++data MemoryManager = MemoryManager+```++#### ./examples/MemoryManager/Util.hs+```haskell+module MemoryManager.Util where++import MemoryManager.Types++checkAddress ::+  [MemorySource]+  -> Int+  -> MemorySource+checkAddress sources addr = case (filter containsAddr sources) of+    []    -> error ("address unknown: " ++ show addr)+    (x:_) -> x+  where+    containsAddr (MemorySource base sc _) = base <= addr && addr < sc+```++### Component definition Step-by-Step+We will now walk through the code step-by-step:++```haskell+module MemoryManager where+```++We start by defining the name of our Haskell module, in this case `MemoryManager`.+Make sure the name of file matches the name of the module, where haskell src files use the `.hs` file-name extension.++We continue with importing modules that we require to build our component:++```haskell+import SoOSiM++import MemoryManager.Types+import MemoryManager.Util+```++The `SoOSiM` module defines all the simulator API functions.+Besides the *external* module, we also import two *local* module called `MemoryManager.Types` and `MemoryManager.Util`, which we define in `./MemoryManager/Types.hs` and `./MemoryManager/Util.hs` respectively.++We start our description with a datatype definition describing the internal state of our memory manager component, and the datatype encoding the messages our memory manager will receive:++```haskell+data MemorySource+  = MemorySource+  { baseAddress :: Int+  , scope       :: Int+  , sourceId    :: Maybe ComponentId+  }++data MemState =+  MemState { addressLookup :: [MemorySource]+           }++data MemCommand = Register MemorySource+                | Read     Int+                | forall a . Typeable a => Write Int a+  deriving Typeable++data MemoryManager = MemoryManager+```++We define two record datatypes [1]; and with three fields (`baseAddress`, `scope`, and `sourceId`) and another with one field (`addressLookup`).+The first record type defines an address range (`baseAddress` and `scope`) and an indication which memory manager is responsible for tht memory range.+The second record type, which has only one field, which defines a dynamically-sized list of `MemorySource` elements.++The third datatype is an algebraic datatype defining the kind of messages that can be send to the memory manager: registering a memory range, reading, and writing.++The fourth datatype is a singleton datatype, which will act as the label/name for the interface defining our memory manager.++We now start defining the actual behaviour of our memory manager, starting with its type annotation:++```haskell+memoryManager :: MemState -> Input MemCommand -> Sim MemState+```++The type definition tells us that the first argument has the type of our internal component state, and the second argument a value of type `Input a`, where the `a` is instantiate to the `MemCommand` datatype.+The possible values of the `Input a` type are enumerated in the *OS Component API* section.+The value of the result is of type `Sim MemState`.+This tells us two things:++* The `memoryManager` function is executed within the `Sim` monad.+* The actual value that is returned is of type `MemState`.++A *monad* is many wonderful things [2], way too much to explain here, so for the rest of this README we see it as an execution environment.+Only inside this execution environment will we have access to the SoOSiM API functions.++Although we know the types of the arguments and the result of the function, we don't know their actual meaning.+The SoOSiM simulator will call your component behaviour, passing as the first argument its current internal state.+The second argument is an event that triggered the execution of your component: for example a message send to you by another component.+The result that you must ultimately return is the, potentially updated, internal state of your component.++We now turn to the first line of the actual function definition:++```haskell+memoryManager s (Message content retAddr) = do+```++Where `memoryManager` is the name of the function, `s` the first argument (of type `MemState`).+We pattern-match on the second argument, meaning this function definition clause only works for values whose constructor is `Message`.+By pattern matching we get access to the fields of the datatype, where we bind the names `content` and `retAddr` to the values of these fields.++The `do` *keyword* after the `=` sign indicates that the function executes within a monadic environment, the `Sim` environment in our case.+The semantics in a monadic environment are different from those in a normal Haskell functions.+A monadic environment has a more imperative feel, in which your function definition interacts with the environment step-by-step, statement after statements.+This also gives rise to the scoping rules familiar to the imperative programmer: names cannot be used before they are declared.++Next we define a nested `case`-statement that contains most of the actual behaviour of our memory manager component:++```haskell+case content of+  (Register memorySource) -> do+    yield $ s {addressLookup = memorySource:(addressLookup s)}++  (Read addr) -> do+    let src = checkAddress (addressLookup s) addr+    case (sourceId src) of+      Nothing -> do+        addrVal <- readMemory addr+        respond MemoryManager retAddr addrVal+        yield s+      Just remote -> do+        response <- invoke MemoryManager remote content+        respond MemoryManager retAddr response+        yield s++  (Write addr val) -> do+    let src = checkAddress (addressLookup s) addr+    case (sourceId src) of+      Nothing -> do+        addrVal <- writeMemory addr val+        yield s+      Just remote -> do+        invokeAsync MemoryManager remote content ignore+        yield s+```++In the first alternative of our case-statement we handle a `Register` message, by updating our address lookup table with an additional memory source.+We `yield` to the simulator with our updated internal state.++In the second alternative we handle a `Read` request.+The next line in our function definition, which checks which specific memory manager is responsible for the address, is:++```haskell+let src = checkAddress (addressLookup s) addr+```++Haskell is white-space sensitive, so make sure that you have a good editor that does automatic indenting.+We use the `let` construct to bind the expression `checkAddress (addressLookup s) addr` to the name `src`.+We use these let-bindings to bind *pure* expressions to names, where *pure* means that the expression has no side-effects [3].+We can now just use the name `src` instead of having to type `checkAddress (addressLookup s) addr` every time.+Don't worry about efficiency, the evaluation mechanics of Haskell will ensure that the actual expression is only calculated once, even when we use the `src` name multiple times.++In the next case-statement we check if the current or a remote memory manager is responsible for handling the address.+In either alternative we must use the `do` keyword again because we will be executing multiple statements.+We will now finally use some of the API functions, the first we encounter is:++```haskell+addrVal <- readMemory addr+```++The `readMemory` function accesses the simulator environment, retrieving the value of the memory location specified by `addr`.+We use the left-arrow `<-` to indicate that this is a side-effecting expression (we are accessing the simulator environment), and that `addrVal` is not bound to the expression itself, but the value belonging to the execution of this statement.++After reading the memory, we send the value back to the module that initially requested the memory access.+We send the read value as a response to the return address (`retAddr`).+Having serviced the request, we use the `yield` function to give the (unaltered) internal state back to the simulation environment.++If a remote memory manager is responsible for the address:++```haskell+Just remote -> do+  response <- invoke MemoryManager remote content+  respond MemoryManager retAddr response+  yield s+```++We then synchronously invoke the remote memory manager with the original read request, and forward the received response to the component making the original memory request.++The third alternative, handling a write request, is analogous to handling a read request.++In the situations which we didn't handle explicitly, such as receiving a `Tick`, we simply disregard the simulator event, and return our unaltered internal state to the simulator.++#### ComponentInterface Instance+At the bottom of our `MemoryManager` module we see the following code:++```haskell+instance ComponentInterface MemoryManager where+  type State   MemoryManager = MemState+  type Receive MemoryManager = MemCommand+  type Send    MemoryManager = Dynamic+  initState _                = (MemState [])+  componentName _            = "MemoryManager"+  componentBehaviour _       = memoryManager+```++Here we define a so-called type-class instance.+At this moment you do not need to know what a type-class is, just that you need to define this instance if you want your component to be able to be used by the SoOSiM simulator.++We use our singleton datatype, `MemoryManager`, as the label/name for our ComponentInterface instance.+All (type-)functions in this interface receive the interface label as their first argument.+For the type-functions (such as `State s`) we must explicitly mention the label, for normal function we can just use the underscore (`_`) as a place holder.++The instance must always contain the definitions for `State`, `Receive`, `Send`, `initState`, `componentName` and `componentBehaviour`.+The `State` indicates the datatype representing the internal state of a module.+The `Receive` indicates the datatype of messages that this component is expecting to receive.+The `Send` indicates the datatype of messages this component will send as responses to invocation.+The `initState` function returns a minimal internal state of your component.+The `componentName` is a function returning the globally unique name of your component.+Finally `componentBehaviour` is a function returning the behaviour of your component.+The behaviour of your component must always have the type:++```haskell+(State iface) -> Input (Receive iface) -> Sim (State iface)+```++Where `State iface` is the datatype of your component's internal state, and `Receive iface` is the datetype of the type of messages the component handles.++## SoOSiM API++#### ComponentInterface Type Class+```haskell+-- | Type class that defines every OS component+class ComponentInterface s where+  -- | Type of messages send by the component+  type Send    s+  -- | Type of messages received by the component+  type Receive s+  -- | Type of internal state of the component+  type State   s+  -- | The minimal internal state of your component+  initState          :: s -> State s+  -- | A function returning the unique global name of your component+  componentName      :: s -> ComponentName+  -- | The function defining the behaviour of your component+  componentBehaviour :: s -> State s -> Input (Receive s) -> Sim (State s)+```++#### Simulator Events+```haskell+data Input a+  = Message a ReturnAddress -- ^ A message send by another component: the+                            --   first field is the message content, the+                            --   second field is the address to send+                            --   responses to+  | Tick                    -- ^ Event send every simulation round+```++#### Accessing the simulator+```haskell+-- | Create a new component+createComponent ::+  (ComponentInterface iface, Typeable (Receive iface))+  => iface+  -- ^ Component Interface+  -> Sim ComponentId+  -- ^ 'ComponentId' of the created component+```++```haskell+-- | Synchronously invoke another component+invoke ::+  (ComponentInterface iface, Typeable (Receive iface), Typeable (Send iface))+  => iface+  -- ^ Interface type+  -> ComponentId+  -- ^ ComponentId of callee+  -> Receive iface+  -- ^ Argument+  -> Sim (Send iface)+  -- ^ Response from callee+```++```haskell+-- | Invoke another component, handle response asynchronously+invokeAsync ::+  (ComponentInterface iface, Typeable (Receive iface), Typeable (Send iface))+  => iface+  -- ^ Interface type+  -> ComponentId+  -- ^ ComponentId of callee+  -> Receive iface+  -- ^ Argument+  -> (Send iface -> Sim ())+  -- ^ Response Handler+  -> Sim ()+  -- ^ Call returns immediately+```++```haskell+-- | Respond to an invocation+respond ::+  (ComponentInterface iface, Typeable (Send iface))+  => iface+  -- ^ Interface type+  -> ReturnAddress+  -- ^ Return address to send response to+  -> (Send iface)+  -- ^ Value to send as response+  -> Sim ()+  -- ^ Call returns immediately+```++```haskell+-- | Yield internal state to the simulator scheduler+yield ::+  a+  -> Sim a+```++```haskell+-- | Get the component id of your component+getComponentId ::+  Sim ComponentId+```++```haskell+-- | Get the node id of of the node your component is currently running on+getNodeId ::+  SimM NodeId+```++```haskell+-- | Create a new node+createNode ::+  Sim NodeId -- ^ NodeId of the created node+```++```haskell+-- | Write memory of local node+writeMemory ::+  Typeable a+  => Int+  -- ^ Address to write+  -> a+  -- ^ Value to write+  -> Sim ()+```++```haskell+-- | Read memory of local node+readMemory ::+  Int+  -- ^ Address to read+  -> Sim Dynamic+```++```haskell+-- | Return the component Id of the component that created the current+--   component+componentCreator ::+  Sim ComponentId+```++```haskell+-- | Get the unique 'ComponentId' of a component implementing an interface+componentLookup ::+  ComponentInterface iface+  => iface+  -- ^ Interface type of the component you are looking for+  -> Sim (Maybe ComponentId)+  -- ^ 'Just' 'ComponentID' if a component is found, 'Nothing' otherwise+```++#### Handling `Dynamic` Values++```haskell+-- | Converts a 'Dynamic' object back into an ordinary Haskell value of the+--   correct type.+unmarshall ::+  Typeable a+  => Dynamic  -- ^ The dynamically-typed object+  -> a        -- ^ Returns: the value of the first argument, if it has the+              -- correct type, otherwise it gives an error.+```++References+----------++[1] Here is a chapter from a book that introduces the correspondence between Haskell types and C types:+http://book.realworldhaskell.org/read/defining-types-streamlining-functions.html++[2] Some resources that discuss monads: http://book.realworldhaskell.org/read/monads.html and http://learnyouahaskell.com/a-fistful-of-monads++[3] A more elaborate explanation of purity can be found here: http://learnyouahaskell.com/introduction#so-whats-haskell
SoOSiM.cabal view
@@ -1,5 +1,5 @@ Name:                SoOSiM-Version:             0.1+Version:             0.2.0.0 Synopsis:            Abstract full system simulator  Description:@@ -25,24 +25,30 @@  Cabal-version:       >=1.6 +Extra-source-files: README.md+                    examples/MemoryManager.hs+                    examples/MemoryManager/Types.hs+                    examples/MemoryManager/Util.hs+ Library   HS-Source-Dirs:   src    Exposed-modules:  SoOSiM,-                    SoOSiM.Simulator,                     SoOSiM.Types    ghc-options:      -Wall -  Build-depends:    base             >= 4.3.1.0 && < 4.6,-                    containers       >= 0.4.0.0 && < 0.5,-                    transformers     >= 0.2.2.0 && < 2.3,-                    mtl              >= 2.0.1.0 && < 2.1,-                    monad-coroutine  >= 0.7.1   && < 0.8,-                    ghc              >= 7.0.3   && < 7.5,-                    stm              >= 2.3     && < 2.4+  Build-depends:    base              >= 4.3.1.0 && < 4.6,+                    concurrent-supply >= 0.1.1   && < 0.2,+                    containers        >= 0.4.0.0 && < 0.5,+                    monad-coroutine   >= 0.7.1   && < 0.8,+                    mtl               >= 2.0.1.0 && < 2.1,+                    stm               >= 2.3     && < 2.4,+                    transformers      >= 0.2.2.0 && < 2.3    Other-modules:    SoOSiM.SimMonad,+                    SoOSiM.Simulator,+                    SoOSiM.Simulator.Util,                     SoOSiM.Util  source-repository head
+ examples/MemoryManager.hs view
@@ -0,0 +1,45 @@+{-# LANGUAGE TypeFamilies #-}+module MemoryManager where++import SoOSiM++import MemoryManager.Types+import MemoryManager.Util++memoryManager :: MemState -> Input MemCommand -> Sim MemState+memoryManager s (Message content retAddr) = do+  case content of+    (Register memorySource) -> do+      yield $ s {addressLookup = memorySource:(addressLookup s)}++    (Read addr) -> do+      let src = checkAddress (addressLookup s) addr+      case (sourceId src) of+        Nothing -> do+          addrVal <- readMemory addr+          respond MemoryManager retAddr addrVal+          yield s+        Just remote -> do+          response <- invoke MemoryManager remote content+          respond MemoryManager retAddr response+          yield s++    (Write addr val) -> do+      let src = checkAddress (addressLookup s) addr+      case (sourceId src) of+        Nothing -> do+          addrVal <- writeMemory addr val+          yield s+        Just remote -> do+          invokeAsync MemoryManager remote content ignore+          yield s++memoryManager s _ = yield s++instance ComponentInterface MemoryManager where+  type State   MemoryManager = MemState+  type Receive MemoryManager = MemCommand+  type Send    MemoryManager = Dynamic+  initState _                = (MemState [])+  componentName _            = "MemoryManager"+  componentBehaviour _       = memoryManager
+ examples/MemoryManager/Types.hs view
@@ -0,0 +1,23 @@+{-# LANGUAGE DeriveDataTypeable        #-}+{-# LANGUAGE ExistentialQuantification #-}+module MemoryManager.Types where++import SoOSiM++data MemorySource+  = MemorySource+  { baseAddress :: Int+  , scope       :: Int+  , sourceId    :: Maybe ComponentId+  }++data MemState =+  MemState { addressLookup :: [MemorySource]+           }++data MemCommand = Register MemorySource+                | Read     Int+                | forall a . Typeable a => Write Int a+  deriving Typeable++data MemoryManager = MemoryManager
+ examples/MemoryManager/Util.hs view
@@ -0,0 +1,13 @@+module MemoryManager.Util where++import MemoryManager.Types++checkAddress ::+  [MemorySource]+  -> Int+  -> MemorySource+checkAddress sources addr = case (filter containsAddr sources) of+    []    -> error ("address unknown: " ++ show addr)+    (x:_) -> x+  where+    containsAddr (MemorySource base sc _) = base <= addr && addr < sc
src/SoOSiM.hs view
@@ -1,17 +1,29 @@ module SoOSiM-  ( SimM+  ( module SoOSiM.SimMonad+  , Sim   , ComponentId   , NodeId-  , ComponentIface (..)-  , ComponentInput (..)-  , module SoOSiM.SimMonad-  , module Data.Dynamic-  , module Unique+  , ComponentInterface (..)+  , Input (..)+  , Typeable+  , Dynamic+  , ignore+  , tick+  , unmarshall+  , returnAddress   ) where -import Data.Dynamic-import Unique--import SoOSiM.Types+import Data.Dynamic          (Dynamic)+import Data.Typeable         (Typeable) import SoOSiM.SimMonad+import SoOSiM.Simulator      (tick)+import SoOSiM.Simulator.Util (returnAddress)+import SoOSiM.Types          (ComponentId,ComponentInterface(..),Input(..)+                             ,NodeId,Sim)+import SoOSiM.Util           (unmarshall)++ignore ::+  a+  -> Sim ()+ignore = const (return ())
src/SoOSiM/SimMonad.hs view
@@ -1,172 +1,321 @@-{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE FlexibleContexts    #-}+{-# LANGUAGE RecordWildCards     #-}+{-# LANGUAGE ScopedTypeVariables #-} module SoOSiM.SimMonad where -import Control.Concurrent.STM-import Control.Monad.Coroutine-import Control.Monad.State-import Control.Monad.Trans.Class ()-import Data.IntMap as IntMap-import Data.Map    as Map-import Data.Maybe+import           Control.Concurrent.STM  (STM,newTVar,readTVar,writeTVar)+import           Control.Monad.Coroutine (suspend)+import           Control.Monad.State     (gets,lift,modify)+import           Data.Dynamic            (Dynamic,Typeable,toDyn)+import qualified Data.IntMap             as IM+import qualified Data.Map                as Map+import           Data.Maybe              (fromMaybe) -import SoOSiM.Simulator+import SoOSiM.Simulator.Util import SoOSiM.Types import SoOSiM.Util-import Unique-import UniqSupply --- | Register a component interface with the simulator-registerComponent ::-  ComponentIface s-  => s-  -> SimM ()-registerComponent cstate = SimM $ do-  lift $ modify (\s -> s {componentMap = Map.insert (componentName cstate) (SC cstate) (componentMap s)})- -- | Create a new component createComponent ::-  Maybe NodeId         -- ^ Node to create component on, leave to 'Nothing' to create on current node-  -> Maybe ComponentId -- ^ ComponentId to set as parent, set to 'Nothing' to use own ComponentId-  -> String            -- ^ Name of the registered component-  -> SimM ComponentId  -- ^ 'ComponentId' of the created component-createComponent nodeId_maybe parentId_maybe cname = SimM $ do-    curNodeId     <- lift $ gets currentNode-    let nId       = fromMaybe curNodeId nodeId_maybe-    pId           <- lift $ gets currentComponent-    let parentId  = fromMaybe pId parentId_maybe-    cId           <- lift getUniqueM+  (ComponentInterface iface, Typeable (Receive iface))+  => iface+  -- ^ Component Interface+  -> Sim ComponentId+  -- ^ 'ComponentId' of the created component+createComponent = createComponentNP Nothing Nothing -    (SC cstate)   <- fmap (fromJust . Map.lookup cname) $ lift $ gets componentMap-    cstateTV      <- (lift . lift) $ newTVarIO cstate+-- | Create a new component+createComponentN ::+  (ComponentInterface iface, Typeable (Receive iface))+  => iface+  -- ^ Component Interface+  -> NodeId+  -- Node to create component on+  -> Sim ComponentId+createComponentN iface nId = createComponentNP (Just nId) Nothing iface -    statusTV      <- (lift . lift) $ newTVarIO Idle-    bufferTV      <- (lift . lift) $ newTVarIO []+-- | Create a new component+createComponentNP ::+  (ComponentInterface iface, Typeable (Receive iface))+  => Maybe NodeId+  -- ^ Node to create component on, leave to 'Nothing' to create on current+  -- node+  -> Maybe ComponentId+  -- ^ ComponentId to set as parent, set to 'Nothing' to use own ComponentId+  -> iface+  -- ^ Component Interface+  -> Sim ComponentId+  -- ^ 'ComponentId' of the created component+createComponentNP nodeIdM parentIdM iface = Sim $ do+    nodeId    <- fmap (`fromMaybe` nodeIdM) $ gets currentNode+    parentId  <- fmap (`fromMaybe` parentIdM) $ gets currentComponent+    compId    <- getUniqueM -    let emptyMeta = SimMetaData 0 0 0 Map.empty Map.empty-    emptyMetaTV   <- (lift . lift) $ newTVarIO emptyMeta+    statusTV  <- (lift . lift) $ newTVar ReadyToRun+    stateTV   <- (lift . lift) $ newTVar (initState iface)+    msgBufTV  <- (lift . lift) $ newTVar []+    let meta  = SimMetaData 0 0 0 Map.empty Map.empty+    metaTV    <- (lift . lift) $ newTVar meta -    lift $ modifyNode nId (addComponent cId (CC cId statusTV cstateTV parentId bufferTV [] emptyMetaTV))-    return cId+    let component = (CC iface compId parentId statusTV stateTV msgBufTV [] metaTV)++    lift $ modifyNode nodeId (addComponent compId component)++    return compId   where-    addComponent cId cc n@(Node {..}) =-      n { nodeComponents      = IntMap.insert (getKey cId) cc nodeComponents+    cname = componentName iface++    addComponent cId comp n@(Node {..}) =+      n { nodeComponents = IM.insert cId comp nodeComponents         , nodeComponentLookup = Map.insert cname cId nodeComponentLookup-        , nodeComponentOrder  = nodeComponentOrder ++ [cId]         }  -- | Synchronously invoke another component invoke ::-  Maybe ComponentId -- ^ Caller, leave 'Nothing' to set to current module-  -> ComponentId    -- ^ Callee-  -> Dynamic        -- ^ Argument-  -> SimM Dynamic   -- ^ Response from recipient-invoke senderMaybe recipient content = SimM $ do-  nId <- lift $ componentNode recipient-  mId <- lift $ gets currentComponent-  let senderId = fromMaybe mId senderMaybe-  senderNodeId <- lift $ componentNode senderId-  lift $ modifyNodeM senderNodeId (incrSendCounter recipient senderId)-  lift $ modifyNodeM nId (updateMsgBuffer recipient (ComponentMsg senderId content))+  (ComponentInterface iface, Typeable (Receive iface), Typeable (Send iface))+  => iface+  -- ^ Interface type+  -> ComponentId+  -- ^ ComponentId of callee+  -> Receive iface+  -- ^ Argument+  -> Sim (Send iface)+  -- ^ Response from callee+invoke iface recipient content = invokeS iface Nothing recipient content++-- | Synchronously invoke another component+invokeS ::+  forall iface+  . (ComponentInterface iface+    , Typeable (Receive iface)+    , Typeable (Send iface))+  => iface+  -- ^ Interface type+  -> Maybe ComponentId+  -- ^ Caller, leave 'Nothing' to set to current module+  -> ComponentId+  -- ^ Callee+  -> Receive iface+  -- ^ Argument+  -> Sim (Send iface)+  -- ^ Response from recipient+invokeS _ senderM recipient content = Sim $ do+  sender       <- fmap (`fromMaybe` senderM) $ gets currentComponent+  responseTV   <- lift . lift . newTVar $ toDyn (undefined :: Send iface)+  let response = RA (sender,responseTV)+  let message  = Message (toDyn content) response++  rNodeId <- lift $ componentNode recipient+  sNodeId <- lift $ componentNode sender+  lift $ modifyNodeM rNodeId (updateMsgBuffer recipient message)+  lift $ modifyNodeM sNodeId (incrSendCounter recipient sender)+   suspend (Request recipient return)+  fmap (unmarshall "invoke") . lift . lift $ readTVar responseTV --- | Invoke another component, don't wait for a response-invokeNoWait ::-  Maybe ComponentId -- ^ Caller, leave 'Nothing' to set to current module-  -> ComponentId    -- ^ Callee-  -> Dynamic        -- ^ Argument-  -> SimM ()        -- ^ Call returns immediately-invokeNoWait senderMaybe recipient content = SimM $ do-  nId <- lift $ componentNode recipient-  mId <- lift $ gets currentComponent-  let senderId = fromMaybe mId senderMaybe-  senderNodeId <- lift $ componentNode senderId-  lift $ modifyNodeM senderNodeId (incrSendCounter recipient senderId)-  lift $ modifyNodeM nId (updateMsgBuffer recipient (ComponentMsg senderId content))+-- | Invoke another component, handle response asynchronously+invokeAsync ::+  (ComponentInterface iface, Typeable (Receive iface), Typeable (Send iface))+  => iface+  -- ^ Interface type+  -> ComponentId+  -- ^ ComponentId of callee+  -> Receive iface+  -- ^ Argument+  -> (Send iface -> Sim ())+  -- ^ Response Handler+  -> Sim ()+  -- ^ Call returns immediately+invokeAsync iface recipient content handler =+  invokeAsyncS iface Nothing recipient content handler --- | Yield to the simulator scheduler+-- | Invoke another component, handle response asynchronously+invokeAsyncS ::+  forall iface+  . (ComponentInterface iface+    , Typeable (Receive iface)+    , Typeable (Send iface))+  => iface+  -- ^ Interface type+  -> Maybe ComponentId+  -- ^ Caller, leave 'Nothing' to set to current module+  -> ComponentId+  -- ^ Callee+  -> (Receive iface)+  -- ^ Argument+  -> (Send iface -> Sim ())+  -- ^ Handler+  -> Sim ()+  -- ^ Call returns immediately+invokeAsyncS _ senderM recipient content _ = Sim $ do+  sender       <- fmap (`fromMaybe` senderM) $ gets currentComponent+  responseTV   <- lift . lift . newTVar $ toDyn (undefined :: Send iface)+  let response = RA (sender,responseTV)+  let message  = Message (toDyn content) response++  rNodeId <- lift $ componentNode recipient+  sNodeId <- lift $ componentNode sender+  lift $ modifyNodeM rNodeId (updateMsgBuffer recipient message)+  lift $ modifyNodeM sNodeId (incrSendCounter recipient sender)++-- | Respond to an invocation+respond ::+  (ComponentInterface iface, Typeable (Send iface))+  => iface+  -- ^ Interface type+  -> ReturnAddress+  -- ^ Return address to send response to+  -> (Send iface)+  -- ^ Value to send as response+  -> Sim ()+  -- ^ Call returns immediately+respond iface retAddr content = respondS iface Nothing retAddr content++-- | Respond to an invocation+respondS ::+  forall iface+  . ( ComponentInterface iface+    , Typeable (Send iface))+  => iface+  -- ^ Interface type+  -> Maybe ComponentId+  -- ^ Callee Id, leave 'Nothing' to set to current module+  -> ReturnAddress+  -- ^ Return address+  -> (Send iface)+  -- ^ Value to send as response+  -> Sim ()+  -- ^ Call returns immediately+respondS _ senderM (RA (recipient,respTV)) content = Sim $ do+  sender <- fmap (`fromMaybe` senderM) $ gets currentComponent+  lift . lift $ writeTVar respTV (toDyn content)++  let message = Message undefined (RA (sender,undefined))+  rNodeId <- lift $ componentNode recipient+  sNodeId <- lift $ componentNode sender+  lift $ modifyNodeM rNodeId (updateMsgBuffer recipient message)+  lift $ modifyNodeM sNodeId (incrSendCounter recipient sender)++-- | Yield internal state to the simulator scheduler yield ::-  ComponentIface s-  => s-  -> SimM s-yield s = SimM $ suspend (Yield (return s))+  a+  -> Sim a+yield s = Sim $ suspend (Yield (return s))  -- | Get the component id of your component getComponentId ::-  SimM ComponentId-getComponentId = SimM $ lift $ gets currentComponent+  Sim ComponentId+getComponentId = Sim $ gets currentComponent  -- | Get the node id of of the node your component is currently running on getNodeId ::-  SimM NodeId-getNodeId = SimM $ lift $ gets currentNode+  Sim NodeId+getNodeId = Sim $ gets currentNode  -- | Create a new node createNode ::-  SimM NodeId -- ^ NodeId of the created node-createNode = SimM $ do-  nodeId <- lift getUniqueM-  let newNode = Node nodeId NodeInfo Map.empty IntMap.empty IntMap.empty []-  lift $ modify (\s -> s {nodes = IntMap.insert (getKey nodeId) newNode (nodes s)})+  Sim NodeId -- ^ NodeId of the created node+createNode = Sim $ do+  nodeId <- getUniqueM+  let newNode = Node nodeId NodeInfo Map.empty IM.empty IM.empty []+  modify (\s -> s {nodes = IM.insert nodeId newNode (nodes s)})   return nodeId  -- | Write memory of local node writeMemory ::-  Maybe NodeId -- ^ Node you want to write on, leave 'Nothing' to set to current node-  -> Int       -- ^ Address to write-  -> Dynamic   -- ^ Value to write-  -> SimM ()-writeMemory nodeId_maybe i val = SimM $ do-    curNodeId <- lift $ gets currentNode-    let nodeId = fromMaybe curNodeId nodeId_maybe-    lift $ modifyNode nodeId writeVal+  Typeable a+  => Int+  -- ^ Address to write+  -> a+  -- ^ Value to write+  -> Sim ()+writeMemory = writeMemoryN Nothing++-- | Write memory of local node+writeMemoryN ::+  Typeable a+  => Maybe NodeId+  -- ^ Node you want to write on, leave 'Nothing' to set to current node+  -> Int+  -- ^ Address to write+  -> a+  -- ^ Value to write+  -> Sim ()+writeMemoryN nodeM addr val = Sim $ do+    node <- fmap (`fromMaybe` nodeM) $ gets currentNode+    lift $ modifyNode node writeVal   where-    writeVal n@(Node {..}) = n { nodeMemory = IntMap.insert i val nodeMemory }+    writeVal n@(Node {..}) = n { nodeMemory = IM.insert addr (toDyn val)+                                                nodeMemory }  -- | Read memory of local node readMemory ::-  Maybe NodeId -- ^ Node you want to look on, leave 'Nothing' to set to current node-  -> Int       -- ^ Address to read-  -> SimM Dynamic-readMemory nodeId_maybe i = SimM $ do-  curNodeId <- lift $ gets currentNode-  let nodeId = getKey $ fromMaybe curNodeId nodeId_maybe-  memVal <- fmap (IntMap.lookup i . nodeMemory . (IntMap.! nodeId)) $ lift $ gets nodes-  case memVal of+  Int+  -- ^ Address to read+  -> Sim Dynamic+readMemory = readMemoryN Nothing++-- | Read memory of local node+readMemoryN ::+  Maybe NodeId+  -- ^ Node you want to look on, leave 'Nothing' to set to current node+  -> Int+  -- ^ Address to read+  -> Sim Dynamic+readMemoryN nodeM addr = Sim $ do+  node    <- fmap (`fromMaybe` nodeM) $ gets currentNode+  nodeMem <- fmap (nodeMemory . (IM.! node)) $ gets nodes+  case (IM.lookup addr nodeMem) of     Just val -> return val-    Nothing  -> error $ "Trying to read empty memory location: " ++ show i ++ " from Node: " ++ show (fromMaybe curNodeId nodeId_maybe)+    Nothing  -> error $ "Trying to read empty memory location: " +++                        show addr ++ " from Node: " ++ show node --- | Return the 'ComponentId' of the component that created the current component+-- | Return the 'ComponentId' of the component that created the current+-- component componentCreator ::-  SimM ComponentId-componentCreator = SimM $ do-  nId <- fmap getKey $ lift $ gets currentNode-  cId <- fmap getKey $ lift $ gets currentComponent-  ns <- lift $ gets nodes-  let ces       = (nodeComponents (ns IntMap.! nId))-  let ce        = ces IntMap.! cId+  Sim ComponentId+componentCreator = Sim $ do+  nId <- gets currentNode+  cId <- gets currentComponent+  ns  <- gets nodes+  let ces       = (nodeComponents (ns IM.! nId))+  let ce        = ces IM.! cId   let ceCreator = creator ce   return ceCreator --- | Get the unique 'ComponentId' of a certain component+-- | Get the unique 'ComponentId' of a component implementing an interface componentLookup ::-  Maybe NodeId                -- ^ Node you want to look on, leave 'Nothing' to set to current node-  -> ComponentName            -- ^ Name of the component you are looking for-  -> SimM (Maybe ComponentId) -- ^ 'Just' 'ComponentID' if the component is found, 'Nothing' otherwise-componentLookup nodeId_maybe cName = SimM $ do-  curNodeId <- lift $ gets currentNode-  let nId   = getKey $ fromMaybe curNodeId nodeId_maybe-  nsLookup  <- fmap (nodeComponentLookup . (IntMap.! nId)) $ lift $ gets nodes-  return $ Map.lookup cName nsLookup+  ComponentInterface iface+  => iface+  -- ^ Interface type of the component you are looking for+  -> Sim (Maybe ComponentId)+  -- ^ 'Just' 'ComponentID' if a component is found, 'Nothing' otherwise+componentLookup = componentLookupN Nothing -runIO ::-  IO a-  -> SimM a-runIO = SimM . liftIO +-- | Get the unique 'ComponentId' of a component implementing an interface+componentLookupN ::+  ComponentInterface iface+  => Maybe NodeId+  -- ^ Node you want to look on, leave 'Nothing' to set to current node+  -> iface+  -- ^ Interface type of the component you are looking for+  -> Sim (Maybe ComponentId)+  -- ^ 'Just' 'ComponentID' if a component is found, 'Nothing' otherwise+componentLookupN nodeM iface = Sim $ do+  node    <- fmap (`fromMaybe` nodeM) $ gets currentNode+  idCache <- fmap (nodeComponentLookup . (IM.! node)) $ lift $ gets nodes+  return $ Map.lookup (componentName iface) idCache+ traceMsg ::   String-  -> SimM ()-traceMsg msg = SimM $ do-  curNodeId <- lift $ gets currentNode-  curCompId <- lift $ gets currentComponent-  lift $ modifyNode curNodeId (updateTraceBuffer curCompId msg)+  -> Sim ()+traceMsg msg = Sim $ do+  node <- gets currentNode+  comp <- gets currentComponent+  lift $ modifyNode node (updateTraceBuffer comp msg)++runSTM ::+  STM a+  -> Sim a+runSTM = Sim . lift . lift
src/SoOSiM/Simulator.hs view
@@ -1,190 +1,26 @@-{-# LANGUAGE RecordWildCards #-}-{-# LANGUAGE PatternGuards   #-}-{-# LANGUAGE Rank2Types      #-}-module SoOSiM.Simulator-  ( modifyNode-  , modifyNodeM-  , incrSendCounter-  , componentNode-  , updateMsgBuffer-  , updateTraceBuffer-  , execStep-  , execStepSmall-  )-where+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE Rank2Types #-}+module SoOSiM.Simulator where -import Control.Concurrent.STM-import Control.Monad.Coroutine-import Control.Monad.State-import Control.Monad.Trans.Class ()-import Data.IntMap as IM-import Data.Map    as Map-import qualified Data.Traversable as T-import Unique+import           Control.Applicative     ((<$>),(<*>))+import           Control.Concurrent.STM  (TVar,atomically,readTVar,writeTVar)+import           Control.Monad.Coroutine (resume)+import           Control.Monad.State     (execStateT,gets,lift,modify)+import           Data.Dynamic            (Dynamic,Typeable)+import qualified Data.Traversable        as T +import SoOSiM.Simulator.Util import SoOSiM.Types -modifyNode ::-  NodeId            -- ^ ID of the node you want to update-  -> (Node -> Node) -- ^ Update function-  -> SimMonad ()-modifyNode i f =-  modify (\s -> s {nodes = IM.adjust f (getKey i) (nodes s)})--modifyNodeM ::-  NodeId                   -- ^ ID of the node you want to update-  -> (Node -> SimMonad ()) -- ^ Update function-  -> SimMonad ()-modifyNodeM i f = do-  ns <- gets nodes-  f $ ns IM.! (getKey i)--componentNode ::-  ComponentId-  -> SimMonad NodeId-componentNode cId = do-  let key = getKey cId-  ns <- gets nodes-  let (node:_) = IM.elems $ IM.filter (\n -> IM.member key (nodeComponents n)) ns-  return (nodeId node)--updateMsgBuffer ::-  ComponentId       -- ^ Recipient component ID-  -> ComponentInput -- ^ Actual message-  -> Node           -- ^ Node containing the component-  -> SimMonad ()-updateMsgBuffer recipientId msg@(ComponentMsg senderId _) node = do-    let ce = (nodeComponents node) IM.! (getKey recipientId)-    lift $ atomically $ modifyTVar (msgBuffer ce) (\msgs -> msgs ++ [msg])-    lift $ atomically $ modifyTVar (simMetaData ce) (\mData -> mData {msgsReceived = Map.insertWith (+) senderId 1 (msgsReceived mData)})--updateMsgBuffer recipientId msg node = do-    let ce = (nodeComponents node) IM.! (getKey recipientId)-    lift $ atomically $ modifyTVar (msgBuffer ce) (\msgs -> msgs ++ [msg])--incrSendCounter ::-  ComponentId    -- RecipientID-  -> ComponentId -- SenderId-  -> Node        -- Node containing the sender-  -> SimMonad ()-incrSendCounter recipientId senderId node = do-  let ce = (nodeComponents node) IM.! (getKey senderId)-  lift $ atomically $ modifyTVar (simMetaData ce) (\mData -> mData {msgsSend = Map.insertWith (+) recipientId 1 (msgsSend mData)})--updateTraceBuffer ::-  ComponentId-  -> String-  -> Node-  -> Node-updateTraceBuffer componentId msg node =-    node { nodeComponents = f (nodeComponents node)}+tick :: SimState -> IO SimState+tick = atomically . execStateT tick'   where-    f ccs = IM.adjust g (getKey componentId) ccs-    g cc  = cc { traceMsgs = msg:(traceMsgs cc)}---- | Update component context according to simulator event-handleComponent ::-  ComponentIface s-  => TVar SimMetaData-  -> ComponentStatus s -- ^ Current component context-  -> s-  -> ComponentInput    -- ^ Simulator event-  -> SimMonad (ComponentStatus s, s, Maybe ComponentInput) -- ^ Returns tuple of: ((potentially updated) component context, 'Nothing' when event is consumed; 'Just' 'ComponentInput' otherwise)---- If a component receives the message from the sender it was waiting for-handleComponent mDataTV (WaitingForMsg waitingFor f) cstate (ComponentMsg sender content)-  | waitingFor == sender-  = do-    incrRunningCount mDataTV-    -- Run the resumable computation with the message content-    res <- resume $ runSimM (f content)-    case res of-      -- Computation is finished, return to idle state-      Right a                     -> return (Running, a, Nothing)-      -- Computation is waiting for a message, store the resumable computation-      Left (Request o c) -> return (WaitingForMsg o (SimM . c), cstate, Nothing)-      Left (Yield c)     -> do-        res' <- resume c-        case res' of-          Right a -> return (Idle, a, Nothing)-          Left  _ -> error "yield did not return state!"---- Don't change the execution context if we're not getting the message we're waiting for-handleComponent mDataTV st@(WaitingForMsg _ _) s msg-  = incrWaitingCount mDataTV >> return (st, s, Just msg)---- Not in an waiting state, just handle the message-handleComponent mDataTV _ cstate msg = do-  incrRunningCount mDataTV-  res <- resume $ runSimM (componentBehaviour cstate msg)-  case res of-    -- Computation is finished, return to idle state-    Right a            -> return (Running, a, Nothing)-    -- Computation is waiting for a message, store the resumable computation-    Left (Request o c) -> return (WaitingForMsg o (SimM . c), cstate, Nothing)-    Left (Yield c)     -> do-        res' <- resume c-        case res' of-          Right a -> return (Idle, a, Nothing)-          Left  _ -> error "yield did not return state!"--executeComponent ::-  ComponentContext-  -> SimMonad ()-executeComponent (CC cId statusTvar cstateTvar _ bufferTvar _ mDataTV) = do-  modify $ (\s -> s {currentComponent = cId})-  status <- lift $ readTVarIO statusTvar-  cstate <- lift $ readTVarIO cstateTvar-  buffer <- lift $ readTVarIO bufferTvar--  (status',cstate',buffer') <- case (status,buffer) of-        (Running, []) -> do-          incrRunningCount mDataTV-          res <- resume $ runSimM (componentBehaviour cstate Tick)-          case res of-            Right a            -> return (Running, a, [])-            Left (Request o c) -> return (WaitingForMsg o (SimM . c), cstate, [])-            Left (Yield c)     -> do-                res' <- resume c-                case res' of-                  Right a -> return (Idle, a, [])-                  Left  _ -> error "yield did not return state!"-        (Idle, [])-          -> do-            incrIdleCount mDataTV-            return (status,cstate,buffer)-        (WaitingForMsg _ _, [])-          -> do-            incrWaitingCount mDataTV-            return (status,cstate,buffer)-        _ -> mapUntilNothingM (handleComponent mDataTV) status cstate buffer--  lift $ atomically $ writeTVar statusTvar status'-  lift $ atomically $ writeTVar cstateTvar cstate'-  lift $ atomically $ writeTVar bufferTvar buffer'--incrIdleCount, incrWaitingCount, incrRunningCount ::-  TVar SimMetaData-  -> SimMonad ()-incrIdleCount    tv = lift $ atomically $ modifyTVar tv (\mdata -> mdata {cyclesIdling  = cyclesIdling  mdata + 1})-incrWaitingCount tv = lift $ atomically $ modifyTVar tv (\mdata -> mdata {cyclesWaiting = cyclesWaiting mdata + 1})-incrRunningCount tv = lift $ atomically $ modifyTVar tv (\mdata -> mdata {cyclesRunning = cyclesRunning mdata + 1})--mapUntilNothingM ::-  ComponentIface s-  => (ComponentStatus s -> s -> ComponentInput -> SimMonad (ComponentStatus s, s, Maybe ComponentInput))-  -> ComponentStatus s-  -> s-  -> [ComponentInput]-  -> SimMonad (ComponentStatus s, s, [ComponentInput])-mapUntilNothingM _ st s [] = return (st,s,[])-mapUntilNothingM f st s (inp:inps) = do-  (st', s', inp_maybe) <- f st s inp-  case inp_maybe of-    Nothing -> return (st',s',inps)-    Just _  -> do-      (st'',s'',inps') <- mapUntilNothingM f st s inps-      return (st'',s'',inp:inps')+    tick' :: SimMonad ()+    tick' = do+      ns <- gets nodes+      _ <- T.mapM executeNode ns+      return ()  executeNode ::   Node@@ -194,37 +30,95 @@     _ <- T.mapM executeComponent (nodeComponents node)     return () -executeNodeSmall ::-  Node+executeComponent ::+  ComponentContext   -> SimMonad ()-executeNodeSmall node = do-    modify $ (\s -> s {currentNode = nodeId node})-    --_ <- T.mapM executeComponent (nodeComponents node)-    --return ()-    case (nodeComponentOrder node) of-      [] -> return ()-      (c:_) -> do-          executeComponent ((nodeComponents node) IM.! (getKey c))-          modifyNode (nodeId node) (\n -> n {nodeComponentOrder = rotate (nodeComponentOrder n)})-  where-    rotate []     = []-    rotate (x:xs) = xs ++ [x]+executeComponent (CC token cId _ statusTV stateTV bufferTV _ metaTV) = do+  modify $ (\s -> s {currentComponent = cId })+  (status,state,buffer) <- lift $ (,,) <$> readTVar statusTV+                                       <*> readTVar stateTV+                                       <*> readTVar bufferTV -tick :: SimMonad ()-tick = do-  ns <- gets nodes-  _ <- T.mapM executeNode ns-  return ()+  ((status',state'),buffer') <- case (status,buffer) of+    (ReadyToRun, []) -> do+      incrRunningCount metaTV+      r <- handleResult (componentBehaviour token state Tick) state+      return (r,[])+    (ReadyToIdle, []) -> do+      incrIdleCount metaTV+      return ((status,state),buffer)+    (WaitingFor _ _, []) -> do+      incrWaitingCount metaTV+      return ((status,state),buffer)+    _ -> runUntilNothingM handleInput token metaTV status state buffer -tickSmall :: SimMonad ()-tickSmall = do-  ns <- gets nodes-  _ <- T.mapM executeNodeSmall ns-  return ()+  lift $ writeTVar statusTV status' >>+         writeTVar stateTV  state'  >>+         writeTVar bufferTV buffer' -execStep :: SimState -> IO SimState-execStep = execStateT tick+resumeYield ::+  ComponentInterface iface+  => SimInternal (State iface)+  -> SimMonad (ComponentStatus iface, State iface)+resumeYield c = do+  res <- resume c+  case res of+    (Right state') -> return (ReadyToIdle, state')+    (Left _)       -> error "yield did not return state" -execStepSmall :: SimState -> IO SimState-execStepSmall = execStateT tickSmall+handleResult ::+  ComponentInterface iface+  => Sim (State iface)+  -> State iface+  -> SimMonad (ComponentStatus iface, State iface)+handleResult f state = do+  res <- resume $ runSim f+  case res of+    Right state'       -> return (ReadyToRun            , state')+    Left (Request o c) -> return (WaitingFor o (Sim . c), state)+    Left (Yield c)     -> resumeYield c +runUntilNothingM ::+  Monad m+  => (a -> b -> c -> d -> e -> m ((c,d),Maybe e))+  -> a -> b -> c -> d -> [e]+  -> m ((c,d),[e])+runUntilNothingM _ _     _   st s []         = return ((st, s), [])+runUntilNothingM f iface mTV st s (inp:inps) = do+  (r, inpM) <- f iface mTV st s inp+  case inpM of+    Nothing -> return (r,inps)+    Just _ -> do+      (r',inps') <- runUntilNothingM f iface mTV st s inps+      return (r',inp:inps')++-- | Update component context according to simulator event+handleInput ::+  (ComponentInterface iface, Typeable (Receive iface))+  => iface+  -> TVar SimMetaData+  -> ComponentStatus iface+  -- ^ Current component context+  -> State iface+  -> Input Dynamic+  -- ^ Simulator Event+  -> SimMonad ((ComponentStatus iface, State iface), Maybe (Input Dynamic))+  -- ^ Returns tuple of: ((potentially updated) component context,+  -- (potentially update) component state, 'Nothing' when event is consumed;+  -- 'Just' 'ComponentInput' otherwise)+handleInput _ metaTV st@(WaitingFor waitingFor f) state+  msg@(Message _ (RA (sender,_)))+  | waitingFor == sender+  = do+    incrRunningCount metaTV+    r <- handleResult (f ()) state+    return (r,Nothing)+  | otherwise+  = incrWaitingCount metaTV >> return ((st, state), Just msg)++handleInput iface metaTV _ state msg = do+  incrRunningCount metaTV+  r <- handleResult+        (componentBehaviour iface state (fromDynMsg iface msg))+        state+  return (r,Nothing)
+ src/SoOSiM/Simulator/Util.hs view
@@ -0,0 +1,105 @@+{-# LANGUAGE FlexibleContexts #-}+module SoOSiM.Simulator.Util where++import           Control.Concurrent.STM (TVar,modifyTVar)+import           Control.Monad.State    (gets,lift,modify)+import           Data.Dynamic           (Dynamic,Typeable)+import qualified Data.IntMap            as IM+import qualified Data.Map               as Map++import SoOSiM.Types+import SoOSiM.Util++modifyNode ::+  NodeId+  -- ^ ID of the node you want to update+  -> (Node -> Node)+  -- ^ Update function+  -> SimMonad ()+modifyNode i f =+  modify (\s -> s {nodes = IM.adjust f i (nodes s)})++modifyNodeM ::+  NodeId+  -- ^ ID of the node you want to update+  -> (Node -> SimMonad ())+  -- ^ Update function+  -> SimMonad ()+modifyNodeM i f = do+  ns <- gets nodes+  f $ ns IM.! i++componentNode ::+  ComponentId+  -> SimMonad NodeId+componentNode cId = do+  ns <- gets nodes+  let (node:_) = IM.elems $ IM.filter (\n -> IM.member cId (nodeComponents n)) ns+  return (nodeId node)++updateMsgBuffer ::+  ComponentId+  -- ^ Recipient component ID+  -> Input Dynamic+  -- ^ Actual message+  -> Node+  -- ^ Node containing the component+  -> SimMonad ()+updateMsgBuffer recipient msg@(Message _ (RA (sender,_))) node = do+    let ce = (nodeComponents node) IM.! recipient+    lift $ modifyTVar (msgBuffer ce) (\msgs -> msgs ++ [msg])+    lift $ modifyTVar (simMetaData ce)+            (\mData -> mData {msgsReceived = Map.insertWith (+) sender 1+                                              (msgsReceived mData)})++updateMsgBuffer _ _ _ = return ()++incrSendCounter ::+  ComponentId+  -- ^ RecipientID+  -> ComponentId+  -- ^ SenderId+  -> Node+  -- ^ Node containing the sender+  -> SimMonad ()+incrSendCounter recipient sender node = do+  let ce = (nodeComponents node) IM.! sender+  lift $ modifyTVar (simMetaData ce)+          (\mData -> mData {msgsSend = Map.insertWith (+) recipient 1+                                        (msgsSend mData)})++updateTraceBuffer ::+  ComponentId+  -> String+  -> Node+  -> Node+updateTraceBuffer cmpId msg node =+    node { nodeComponents = f (nodeComponents node)}+  where+    f ccs = IM.adjust g cmpId ccs+    g cc  = cc { traceMsgs = msg:(traceMsgs cc)}++incrIdleCount, incrWaitingCount, incrRunningCount ::+  TVar SimMetaData+  -> SimMonad ()+incrIdleCount    tv = lift $ modifyTVar tv (\mdata -> mdata+                              {cyclesIdling = cyclesIdling  mdata + 1})+incrWaitingCount tv = lift $ modifyTVar tv (\mdata -> mdata+                              {cyclesWaiting = cyclesWaiting mdata + 1})+incrRunningCount tv = lift $ modifyTVar tv (\mdata -> mdata+                              {cyclesRunning = cyclesRunning mdata + 1})+++fromDynMsg ::+  (ComponentInterface i, Typeable (Receive i))+  => i+  -> Input Dynamic+  -> Input (Receive i)+fromDynMsg _ (Message content retChan) =+  Message (unmarshall "fromDynMsg" content) retChan+fromDynMsg _ Tick = Tick++returnAddress ::+  ReturnAddress+  -> ComponentId+returnAddress = fst . unRA
src/SoOSiM/Types.hs view
@@ -1,49 +1,66 @@-{-# LANGUAGE Rank2Types                 #-}+{-# LANGUAGE DeriveDataTypeable         #-} {-# LANGUAGE ExistentialQuantification  #-}-{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE FlexibleContexts           #-} {-# LANGUAGE FlexibleInstances          #-}+{-# LANGUAGE TypeFamilies               #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE MultiParamTypeClasses      #-} {-# LANGUAGE StandaloneDeriving         #-}-{-# LANGUAGE DeriveDataTypeable         #-} {-# LANGUAGE TypeSynonymInstances       #-} module SoOSiM.Types where -import Control.Concurrent.STM-import Control.Monad.Coroutine-import Control.Monad.State-import Control.Monad.Trans.Class ()-import Data.Dynamic-import Data.IntMap-import Data.Map-import Unique-import UniqSupply+import           Control.Concurrent.STM     (STM,TVar)+import           Control.Concurrent.Supply  (Supply,freshId)+import           Control.Monad.Coroutine    (Coroutine)+import qualified Control.Monad.State        as State+import           Control.Monad.State        (lift,get,put)+import           Data.Dynamic               (Dynamic,Typeable)+import           Data.IntMap                (IntMap)+import           Data.Map                   (Map) +import           SoOSiM.Util                (MonadUnique(..))++type Unique        = Int type ComponentId   = Unique type ComponentName = String -deriving instance Typeable Unique- -- | Type class that defines every OS component-class ComponentIface s where+class ComponentInterface s where+  -- | Type of messages send by the component+  type Send    s+  -- | Type of messages received by the component+  type Receive s+  -- | Type of internal state of the component+  type State   s   -- | The minimal internal state of your component-  initState          :: s+  initState          :: s -> State s   -- | A function returning the unique global name of your component   componentName      :: s -> ComponentName   -- | The function defining the behaviour of your component-  componentBehaviour :: s -> ComponentInput -> SimM s+  componentBehaviour :: s -> State s -> Input (Receive s) -> Sim (State s)  -- | Context of a running component in the simulator. ----- We need rank-2 types because we need to make a single collection+-- We need existential types because we need to make a single collection -- of several component contexts, each having their own type representing -- their internal state.-data ComponentContext = forall s . ComponentIface s =>-  CC { componentId        :: ComponentId-     , currentStatus      :: TVar (ComponentStatus s) -- ^ Status of the component-     , componentState     :: TVar s                   -- ^ State internal to the component-     , creator            :: ComponentId              -- ^ 'ComponentId' of the component that created this component-     , msgBuffer          :: TVar [ComponentInput]    -- ^ Message waiting to be processed by the component-     , traceMsgs          :: [String]                 -- ^ Trace message buffer-     , simMetaData        :: TVar SimMetaData         -- ^ Statistical information regarding a component+data ComponentContext = forall s . (ComponentInterface s, Typeable (Receive s)) =>+  CC { componentIface     :: s+     -- ^ Interface type+     , componentId        :: ComponentId+     -- ^ 'ComponentId' of this component+     , creator            :: ComponentId+     -- ^ 'ComponentId' of the component that created this component+     , currentStatus      :: TVar (ComponentStatus s)+     -- ^ Status of the component+     , componentState     :: TVar (State s)+     -- ^ State internal to the component+     , msgBuffer          :: TVar [Input Dynamic]+     -- ^ Message waiting to be processed by the component+     , traceMsgs          :: [String]+     -- ^ Trace message buffer+     , simMetaData        :: TVar SimMetaData+     -- ^ Statistical information regarding a component      }  data SimMetaData@@ -51,42 +68,62 @@   { cyclesRunning :: Int   , cyclesWaiting :: Int   , cyclesIdling  :: Int-  , msgsReceived  :: Map ComponentId Int -- ^ Key: senderId; Value: number of messages-  , msgsSend      :: Map ComponentId Int -- ^ Key: receiverId: Value: number of messages+  , msgsReceived  :: Map ComponentId Int+  -- ^ Key: senderId; Value: number of messages+  , msgsSend      :: Map ComponentId Int+  -- ^ Key: receiverId: Value: number of messages   }  -- | Status of a running component data ComponentStatus a-  = Idle                                          -- ^ Component is doing nothing-  | WaitingForMsg ComponentId (Dynamic -> SimM a) -- ^ Component is waiting for a message from 'ComponentId', will continue with computation ('Dynamic' -> 'SimM' a) once received-  | Running                                       -- ^ Component is busy doing computations+  = ReadyToIdle+  -- ^ Component is doing nothing+  | WaitingFor ComponentId (() -> Sim (State a))+  -- ^ Component is waiting for a message from 'ComponentId', will continue+  -- with computation ('(' -> 'SimM' a) once received+  | ReadyToRun+  -- ^ Component is busy doing computations  -- | Events send to components by the simulator-data ComponentInput = ComponentMsg ComponentId Dynamic -- ^ A message send another component: the field argument is the 'ComponentId' of the sender, the second field the message content-                    | NodeMsg NodeId Dynamic           -- ^ A message send by a node: the first field is the 'NodeId' of the sending node, the second field the message content-                    | Initialize                       -- ^ Event send when a component is first created-                    | Deinitialize                     -- ^ Event send when a component is about to be removed-                    | Tick                             -- ^ Event send every simulation round-  deriving Show+data Input a+  = Message a ReturnAddress+  -- ^ A message send another component: the field argument is the+  -- 'ComponentId' of the sender, the second field the message content+  | Tick+  -- ^ Event send every simulation round +newtype ReturnAddress = RA { unRA :: (ComponentId, TVar Dynamic) }++instance Show (Input a) where+  show (Message _ (RA (sender,_))) = "Mesage from: " ++ show sender+  show Tick                        = "Tick"+ type NodeId   = Unique--- | Meta-data describing the functionaly of the computing node, currently just a singleton type.+-- | Meta-data describing the functionaly of the computing node, currently+-- just a singleton type. data NodeInfo = NodeInfo  -- | Nodes represent computing entities in the simulator, -- and host the OS components and application threads-data Node =-  Node { nodeId              :: NodeId                        -- ^ Globally Unique ID of the node-       , nodeInfo            :: NodeInfo                      -- ^ Meta-data describing the node-       , nodeComponentLookup :: Map ComponentName ComponentId -- ^ Lookup table of OS components running on the node, key: the 'ComponentName', value: unique 'ComponentId'-       , nodeComponents      :: IntMap ComponentContext       -- ^ Map of component contexts, key is the 'ComponentId'-       , nodeMemory          :: IntMap Dynamic                -- ^ Node-local memory-       , nodeComponentOrder  :: [ComponentId]-       }+data Node+  = Node+  { nodeId              :: NodeId+  -- ^ Globally Unique ID of the node+  , nodeInfo            :: NodeInfo+  -- ^ Meta-data describing the node+  , nodeComponentLookup :: Map ComponentName ComponentId+  -- ^ Lookup table of OS components running on the node, key: the+  -- 'ComponentName', value: unique 'ComponentId'+  , nodeComponents      :: IntMap ComponentContext+  -- ^ Map of component contexts, key is the 'ComponentId'+  , nodeMemory          :: IntMap Dynamic+  -- ^ Node-local memory+  , nodeComponentOrder  :: [ComponentId]+  } --- The simulator monad used by the OS components offers resumable computations--- in the form of coroutines. These resumable computations expect a value of--- type 'Dynamic', and return a value of type 'a'.+-- | The simulator monad used by the OS components offers resumable+-- computations in the form of coroutines. These resumable computations+-- expect a value of type 'Dynamic', and return a value of type 'a'. -- -- We need resumable computations to simulate synchronous messaging between -- two components. When a component synchronously sends a message to another@@ -101,9 +138,18 @@ -- message from. The execute a resumeable computation you simply do: --   'resume <comp>' ---newtype SimM a = SimM { runSimM :: Coroutine (RequestOrYield Unique Dynamic) SimMonad a }-  deriving (Functor, Monad)+newtype Sim a = Sim { runSim :: SimInternal a }+  deriving (Functor, Monad, State.MonadState SimState, MonadUnique) +type SimInternal = Coroutine (RequestOrYield Unique ()) SimMonad++instance State.MonadState SimState SimInternal where+  get   = lift get+  put x = lift (put x)++instance MonadUnique SimInternal where+  getUniqueM = lift getUniqueM+ data RequestOrYield request response x   = Request request (response -> x)   | Yield   x@@ -112,25 +158,27 @@   fmap f (Request x g) = Request x (f . g)   fmap f (Yield y)     = Yield (f y) --- | The internal monad of the simulator is currently a simple state-monad wrapping IO-type SimMonad  = StateT SimState IO+-- | The internal monad of the simulator is currently a simple state-monad+-- wrapping STM+type SimMonad  = State.StateT SimState STM  -- | The internal simulator state-data SimState =-  SimState { currentComponent :: ComponentId  -- ^ The 'ComponentId' of the component currently under evaluation-           , currentNode      :: NodeId       -- ^ The 'NodeId' of the node containing the component currently under evaluation-           , nodes            :: IntMap Node  -- ^ The set of nodes comprising the entire system-           , uniqueSupply     :: UniqSupply   -- ^ Unlimited supply of unique values-           , componentMap     :: Map String StateContainer-           }--data StateContainer = forall s . ComponentIface s => SC s+data SimState+  = SimState+  { currentComponent :: ComponentId+  -- ^ The 'ComponentId' of the component currently under evaluation+  , currentNode      :: NodeId+  -- ^ The 'NodeId' of the node containing the component currently under+  -- evaluation+  , nodes            :: IntMap Node+  -- ^ The set of nodes comprising the entire system+  , uniqueSupply     :: Supply+  -- ^ Unlimited supply of unique values+  }  instance MonadUnique SimMonad where-  getUniqueSupplyM = gets uniqueSupply-  getUniqueM       = do-    supply <- gets uniqueSupply-    let (supply'',supply') = splitUniqSupply supply-        unique             = uniqFromSupply supply''-    modify (\s -> s {uniqueSupply = supply'})+  getUniqueM = do+    supply <- State.gets uniqueSupply+    let (unique,supply') = freshId supply+    State.modify (\s -> s {uniqueSupply = supply'})     return unique
src/SoOSiM/Util.hs view
@@ -1,13 +1,13 @@-module SoOSiM.Util-  ( module SoOSiM.Util-  , module Data.Dynamic-  )-where+{-# LANGUAGE ScopedTypeVariables #-}+module SoOSiM.Util where -import Data.Dynamic-import Data.IntMap-import Data.Monoid+import Data.Dynamic (Typeable,Dynamic,fromDyn,toDyn)+import Data.IntMap  (IntMap,Key,member,adjust,insert)+import Data.Monoid  (Monoid (..)) +class MonadUnique m where+  getUniqueM :: m Int+ adjustForce :: Monoid a => (a -> a) -> Key -> IntMap a -> IntMap a adjustForce f k m = case (member k m) of   True  -> adjust f k m@@ -19,3 +19,16 @@   (a',y) <- f a x   (a'',ys) <- mapAccumLM f a' xs   return (a'',y:ys)++unmarshall :: forall a . Typeable a => String -> Dynamic -> a+unmarshall msg d = fromDyn d+                 (error $  "unmarshal failed: expected value of type: "+                        ++ show resDyn+                        ++ ", received value of type: "+                        ++ show d+                        ++ "\nmessage:\n"+                        ++ msg+                 )+  where+    resDyn :: Dynamic+    resDyn = toDyn (undefined :: a)