Base.hs.template 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. -- vim: ft=haskell
  2. {-# LANGUAGE RecordWildCards #-}
  3. {-# LANGUAGE OverloadedStrings #-}
  4. module $module_path$.Base where
  5. import Control.Concurrent (forkIO)
  6. import Control.Concurrent.STM
  7. (atomically,STM,retry,TVar,newTVar,writeTVar,readTVar
  8. ,TQueue,readTQueue,writeTQueue,newTQueueIO,tryReadTQueue
  9. ,TMVar,newEmptyTMVarIO,takeTMVar,putTMVar)
  10. import Control.Monad (forever)
  11. import Snap.Core (Snap,route)
  12. import qualified Snap.Http.Server as HTTP
  13. import Snap.Util.FileServe (serveDirectory)
  14. data Config = Config { cfgPort :: !Int
  15. -- ^ The port to run on
  16. , cfgStaticDir :: Maybe FilePath
  17. -- ^ Content to be served off of the root, relative to
  18. -- the directory that the server was started in
  19. } deriving (Show)
  20. -- | A default @Config@ value that will produce a server that runs on port 8080,
  21. -- and serves no static content.
  22. defaultConfig :: Config
  23. defaultConfig = Config { cfgPort = 8080, cfgStaticDir = Nothing }
  24. -- | Spawn a snap server, and run the given RPC action.
  25. runServer :: Config -> Snap () -> IO ()
  26. runServer Config { .. } serveRpc =
  27. do let snapCfg :: HTTP.Config Snap ()
  28. snapCfg = HTTP.setPort cfgPort HTTP.defaultConfig
  29. HTTP.simpleHttpServe snapCfg body
  30. where
  31. body =
  32. do serveRpc
  33. case cfgStaticDir of
  34. Just path -> route [ ("", serveDirectory path) ]
  35. Nothing -> return ()
  36. data Request req resp = ReadRequest req (resp -> IO ())
  37. | WriteRequest req
  38. -- Sample Variables ------------------------------------------------------------
  39. -- | A TVar that blocks when it's empty, but allows writes even when it's full.
  40. newtype TSampleVar a = TSampleVar (TVar (Maybe a))
  41. newTSampleVar :: STM (TSampleVar a)
  42. newTSampleVar = TSampleVar `fmap` newTVar Nothing
  43. newTSampleVarIO :: IO (TSampleVar a)
  44. newTSampleVarIO = atomically (TSampleVar `fmap` newTVar Nothing)
  45. writeTSampleVar :: TSampleVar a -> a -> STM ()
  46. writeTSampleVar (TSampleVar tv) a = writeTVar tv (Just a)
  47. readTSampleVar :: TSampleVar a -> STM a
  48. readTSampleVar (TSampleVar tv) =
  49. do mb <- readTVar tv
  50. case mb of
  51. Just a -> return a
  52. Nothing -> retry
  53. -- Response Handling -----------------------------------------------------------
  54. data Conn req resp = Conn { connRequests :: TQueue req
  55. , connWaiting :: TQueue (TMVar resp)
  56. }
  57. -- | Fork a handler thread that will apply handlers to incoming messages. If
  58. -- the handler queue is empty when a response arrives, the response is dropped.
  59. newConn :: TQueue req -> TQueue resp -> IO (Conn req resp)
  60. newConn connRequests connResps =
  61. do connWaiting <- newTQueueIO
  62. _ <- forkIO (forever
  63. (do resp <- atomically (readTQueue connResps)
  64. mb <- atomically (tryReadTQueue connWaiting)
  65. case mb of
  66. Just var -> atomically (putTMVar var resp)
  67. Nothing -> return ()))
  68. return Conn { .. }
  69. -- | Send a request that doesn't expect a response.
  70. sendRequest_ :: Conn req resp -> req -> IO ()
  71. sendRequest_ Conn { .. } req =
  72. atomically (writeTQueue connRequests req)
  73. -- | Send a request, and block until a response is received.
  74. sendRequest :: Conn req resp -> req -> IO resp
  75. sendRequest Conn { .. } req =
  76. do var <- newEmptyTMVarIO
  77. atomically (do writeTQueue connWaiting var
  78. writeTQueue connRequests req)
  79. atomically (takeTMVar var)