Global state in multithreaded server

178 views Asked by At

i'm implementing simple http server and i want my responses depend on some global state. For example if i get request 'get_settings' from the same client for the first time i will send large settings json, and for the second time i will just send 'Not-modified' http response.

Something like that

import Network.Simple.TCP

main = withSocketsDo $ do 
    let settings_state = 0 -- flag for settings response
    serve (Host "127.0.0.1") "23980" $ \(conn_sock, remote_addr) -> do
        putStrLn $ "TCP connection established from " ++ show remote_addr
        (Just inp) <- recv conn_sock 1024
        send conn_sock (process inp settings_state)

process :: B.ByteString -> Int -> B.ByteString
process inp flag
    | flag == 0 = ... -- return full response and change global flag
    | otherwise = ... -- return 'Not-modified'

And the question is how can i implement it? And i would like to do it as simple as possible, manually, without any Monad Transformers and so on. Let the code be ugly, but simple. Thanks

1

There are 1 answers

0
Zeta On BEST ANSWER

Since changing the flag clearly has some side effects, the result of process will be in IO:

process :: B.ByteString -> Int -> IO B.ByteString

Since you don't want to use any kind of monad transformer, you need to exchange the Int with some mutable reference. Yes, you've read correctly: There are several types that are mutable, such as IORef, MVar, TVar, MVector, STRef …. To stay simple, lets stick to IORef.

process :: B.ByteString -> IORef Int -> IO B.ByteString
process inp flag = do
    oldFlag <- readIORef flag
    if oldFlag == 0 
       then do modifyIORef' flag (+1)
               return bigJSONObject
       else return notModified

Note that you didn't provide any logic for the flag, so I simply increased the value, but you probably want to do something else (or change the flag to IORef Bool). Note that you also want to use atomicModifyIORef' if you want to use the IORef safely in a multithreaded program:

    oldFlag <- atomicModifyIORef' flag (\o -> (o+1,o))

Either way, you need to create the IORef with newIORef value, so your code snippets becomes something like

main = withSocketsDo $ do 
    settings_state <- newIORef 0
    serve (Host "127.0.0.1") "23980" $ \(conn_sock, remote_addr) -> do
        -- ...