{-# OPTIONS_GHC -fno-warn-missing-signatures #-} import Control.Concurrent --import Control.Concurrent.Chan --import Control.Concurrent.MVar import Control.Monad import Control.Monad.Fix import qualified Data.IntMap as IM import Data.IORef import Network import Profiling.Heap.Read import Profiling.Heap.Process import Profiling.Heap.Network import Profiling.Heap.Types import System.IO import HandleArgs -- Start up a process to profile and a server to broadcast the stream -- to multiple clients. main = withSocketsDo $ do (portNum,exec,dir,params) <- relayArgs let procData = processToProfile exec dir params [] port = PortNumber $ fromIntegral portNum profChan <- newChan stopServer <- newEmptyMVar names <- newIORef IM.empty cbres <- profileCallback (Local procData) $ \p -> do -- Broadcasting... writeChan profChan p -- Cleaning up the master channel that's not read by -- anyone. _ <- readChan profChan case p of SinkId ccid ccname -> modifyIORef names (IM.insert ccid ccname) SinkStop -> putMVar stopServer () _ -> return () case cbres of Nothing -> putStrLn "Error starting profile reader thread. Did you enable heap profiling?" Just _ -> runServer port (takeMVar stopServer) $ \chdl -> do -- A rather lazy way of avoiding the need to maintain an explicit -- client list and perform additional synchronisation... ownChan <- dupChan profChan -- Start by sending the currently known name mapping. ccmap <- readIORef names mapM_ (sendMsg chdl . putStream . uncurry SinkId) (IM.toList ccmap) -- Forward stream to the client. fix $ \sendLoop -> do prof <- readChan ownChan ok <- flip catch (const (return False)) $ do sendMsg chdl . putStream $ prof return (prof /= SinkStop) when ok sendLoop return () -- Start a loop accepting connections and running arbitrary code on -- them in separate threads, and wait for a stopping action. runServer port waitForStop act = do sock <- listenOn port tid <- forkIO $ forever $ do (hdl,_host,_cport) <- accept sock hSetBuffering hdl LineBuffering forkIO (act hdl) -- There might be some ugly race conditions here, where clients might -- be left without a message before leaving the runServer subroutine. _ <- waitForStop killThread tid