Thread-safe state with Warp/WAI

concurrency, haskell, haskell-wai, haskell-warp, state

Solution

If your interaction with the state can be expressed with a single call to `atomicModifyIORef`, you can use that, and you don't need to explicitly serialise access to the state.

import Data.IORef

main = do state <- newIORef 42
          run 3000 (app' state)

app' :: IORef Int -> Application
app' ref req
   = return . text . pack . show `liftM` atomicModifyIORef ref (\st -> (st + 1, st + 1))

If your interaction is more complex and you need to enforce full serialisation of requests, you can use an `MVar` in conjunction with `StateT`.

import Control.Concurrent.MVar
import Control.Monad.State.Strict

main = do state <- newMVar 42
          run 3000 (app' state)

app' :: MVar Int -> Application
app' ref request
   = do state <- takeMVar ref
        (response, newState) <- runStateT (application request) state
        putMVar newState --TODO: ensure putMVar happens even if an exception is thrown
        return response

application :: Request -> StateT Int (ResourceT IO) Response
application request = modify (+1) >>= return . text . pack . show

Problem

I want to write a web server which stores its state in a `State` monad with `wai`/`warp`. Something like this: ``` {-# LANGUAGE OverloadedStrings #-} import Network.Wai import Network.Wai.Handler.Warp import Network.HTTP.Types import Control.Monad.State import Data.ByteString.Lazy.Char8 main = run 3000 app text x = responseLBS status200 [("Content-Type", "text/plain")] x app req = return $ text "Hello World" app1 req = modify (+1) >>= return . text . pack . show -- main1 = runStateT (run 3000 app1) 0 ``` The commented line doesn't work, of course. The intent is to store a counter in a state monad and display its increasing value on every request. Also, how do I get thread safety? Does warp run my middleware sequentially or in parallel? What options are available for the state - is there anything at all besides `IORef` I can use in this scenario? I understand that State gives safety but it seems wai doesn't allow State. I only need a dead-simple single-threaded RPC I can call from somewhere else. `Haxr` package requires a separate web server which is an overkill. See Calling Haskell from Node.JS - it didn't have any suggestions so I wrote a simple server using Wai/Warp and Aeson. But it seems that WAI was designed to support concurrent implementatons so it complicates things.

Original source