| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110 | 
							- -- vim: ft=haskell
 
- {-# LANGUAGE RecordWildCards #-}
 
- {-# LANGUAGE OverloadedStrings #-}
 
- module $module_path$.Base where
 
- import           Data.Word (Word32)
 
- import           Data.Map.Strict (Map)
 
- import qualified Data.Map.Strict as Map
 
- import           Control.Applicative ((<|>))
 
- import           Control.Concurrent (forkIO)
 
- import           Control.Concurrent.STM
 
-                      (atomically,STM,TVar,newTVar,writeTVar,readTVar
 
-                      ,TQueue,readTQueue,writeTQueue
 
-                      ,TMVar,newEmptyTMVarIO,takeTMVar,putTMVar,newTVarIO
 
-                      ,modifyTVar)
 
- import           Control.Monad (forever)
 
- import           Snap.Core (Snap)
 
- import qualified Snap.Http.Server as HTTP
 
- import           Snap.Util.FileServe (serveDirectory)
 
- data Config = Config { cfgPort :: !Int
 
-                        -- ^ The port to run on
 
-                      , cfgStaticDir :: Maybe FilePath
 
-                        -- ^ Content to be served off of the root, relative to
 
-                        -- the directory that the server was started in
 
-                      } deriving (Show)
 
- -- | A default @Config@ value that will produce a server that runs on port 8080,
 
- -- and serves no static content.
 
- defaultConfig :: Config
 
- defaultConfig  = Config { cfgPort = 8080, cfgStaticDir = Nothing }
 
- -- | Spawn a snap server, and run the given RPC action.
 
- runServer :: Config -> Snap () -> IO ()
 
- runServer Config { .. } serveRpc =
 
-   do let snapCfg :: HTTP.Config Snap ()
 
-          snapCfg  = HTTP.setPort cfgPort HTTP.defaultConfig
 
-      HTTP.simpleHttpServe snapCfg body
 
-   where
 
-   body = case cfgStaticDir of
 
-     Just path -> serveDirectory path <|> serveRpc
 
-     Nothing -> serveRpc
 
- data Request req resp = ReadRequest req (resp -> IO ())
 
-                       | WriteRequest req
 
- -- Sample Variables ------------------------------------------------------------
 
- -- | A TVar that blocks when it's empty, but allows writes even when it's full.
 
- newtype TSampleVar a = TSampleVar (TVar (Maybe a))
 
- newTSampleVar :: STM (TSampleVar a)
 
- newTSampleVar  = TSampleVar `fmap` newTVar Nothing
 
- newTSampleVarIO :: IO (TSampleVar a)
 
- newTSampleVarIO  = atomically (TSampleVar `fmap` newTVar Nothing)
 
- writeTSampleVar :: TSampleVar a -> a -> STM ()
 
- writeTSampleVar (TSampleVar tv) a = writeTVar tv (Just a)
 
- readTSampleVar :: TSampleVar a -> STM (Maybe a)
 
- readTSampleVar (TSampleVar tv) = readTVar tv
 
- -- Response Handling -----------------------------------------------------------
 
- data Conn req resp = Conn { connRequests :: TQueue req
 
-                           , connWaiting  :: TVar (Map Word32 (TMVar resp))
 
-                           , connSeqNum   :: TVar Word32
 
-                           }
 
- -- | Fork a handler thread that will apply handlers to incoming messages.  If
 
- -- the handler queue is empty when a response arrives, the response is dropped.
 
- newConn :: TQueue req -> TQueue resp -> (resp -> Word32) -> IO (Conn req resp)
 
- newConn connRequests connResps toSeqNum =
 
-   do connWaiting <- newTVarIO Map.empty
 
-      connSeqNum  <- newTVarIO 0
 
-      _ <- forkIO (forever
 
-         (do resp <- atomically (readTQueue connResps)
 
-             let snum = toSeqNum resp
 
-             mb   <- atomically (do
 
-                      m <- readTVar connWaiting
 
-                      let (mb, m') = Map.updateLookupWithKey (\\_ _ -> Nothing) snum m
 
-                      writeTVar connWaiting m'
 
-                      return mb)
 
-             case mb of
 
-               Just var -> atomically (putTMVar var resp)
 
-               Nothing  -> return ()))
 
-      return Conn { .. }
 
- -- | Send a request, and block until a response is received.
 
- sendRequest :: Conn req resp -> (Word32 -> req) -> IO resp
 
- sendRequest Conn { .. } req =
 
-   do var <- newEmptyTMVarIO
 
-      atomically (do snum <- readTVar connSeqNum
 
-                     writeTVar connSeqNum (snum + 1)
 
-                     modifyTVar connWaiting (Map.insert snum var)
 
-                     writeTQueue connRequests (req snum))
 
-      atomically (takeTMVar var)
 
 
  |