[Haskell] Help needed interrupting accepting a network connection
Chris Kuklewicz
haskell at list.mightyreason.com
Sat Dec 2 20:22:05 EST 2006
Cat Dancer wrote:
> On 12/2/06, Chris Kuklewicz <haskell at list.mightyreason.com> wrote:
>> Hi, I have taken a crack at this. The best thing would be not to use the
>> asynchronous exceptions to signal the thread that calls accept.
>
> I'd certainly be most happy not to use asynchronous exceptions as the
> signalling mechanism, but how would you break out of the accept,
> except by receiving an asynchronous exception?
>
Short Version: You trigger a graceful exit using a TVar...
...and then you use killThread to break out of accept.
Long Version:
{-
The main accepting thread spawns this a slave thread to run accept and
stuffs the result into a TMVar. The main loop then atomically checks
the TVar used for graceful shutdown and the TMVar. These two checks
are combined by `orElse` which gives the semantics one wants: on each
loop either the TVar has been set to True or the the slave thread has
accepted a client into the TMVar.
There is still the possibility that a busy server could accept a
connection from the last client and put it in the TMVar where the main
loop will miss it when it exits. This is handled by the finally
action which waits for the slave thread to be well and truly dead and
then looks for that last client in the TMVar.
No uses of block or unblock are required.
-}
-- Example using STM and orElse to compose a solution
import Control.Concurrent
import Control.Exception
import Control.Concurrent.STM
import Network
import System.IO
runExampleFor socket seconds = do
tv <- newTVarIO False -- Set to True to indicate graceful exit requested
sInfo <- startServer socket tv
threadDelay (1000*1000*seconds)
shutdownServer tv sInfo
startServer socket tv = do
childrenList <- newMVar []
tInfo <- fork (acceptUntil socket exampleReceiver childrenList (retry'until'true tv))
return (tInfo,childrenList)
-- Capture idiom of notifying a new MVar when a thread is finished
fork todo = do
doneMVar <- newEmptyMVar
tid <- forkIO $ finally todo (putMVar doneMVar ())
return (doneMVar,tid)
acceptUntil socket receiver childrenList checker = do
chan <- newEmptyTMVarIO
(mv,tid) <- fork (forever (accept socket >>= syncTMVar chan))
let loop = do
result <- atomically (fmap Left checker `orElse` fmap Right (takeTMVar chan))
case result of
Left _ -> return ()
Right client -> spawn client >> loop
spawn client@(handle,_,_) = do
cInfo <- fork (finally (receiver client) (hClose handle))
modifyMVar_ childrenList (return . (cInfo:))
end = do
killThread tid
takeMVar mv
maybeClient <- atomically (tryTakeTMVar chan)
maybe (return ()) spawn maybeClient
finally (handle (\e -> throwTo tid e >> throw e) loop) end
forever x = x >> forever x
-- Pass item to another thread and wait for pickup
syncTMVar tmv item = do
atomically (putTMVar tmv item)
atomically (do empty <- isEmptyTMVar tmv
if empty then return () else retry)
retry'until'true tv = do
val <- readTVar tv
if val then return ()
else retry
exampleReceiver (handle,_,_) = do
hPutStrLn handle "Hello."
hPutStrLn handle "Goodbye."
shutdownServer tv ((acceptLoopDone,_),childrenList) = do
atomically (writeTVar tv True)
readMVar acceptLoopDone
withMVar childrenList (mapM_ (readMVar . fst))
More information about the Haskell
mailing list