Combinatorrent-0.2.0: src/Process/Peer/SenderQ.hs
module Process.Peer.SenderQ
( SenderQMsg(..)
, start
)
where
import Control.Concurrent
import Control.Concurrent.STM
import Control.Monad.Reader
import Control.Monad.State
import Prelude hiding (catch, log)
import qualified Data.ByteString as B
import Channels
import Process
import qualified Data.Queue as Q
import Supervisor
import Torrent
import Protocol.Wire
-- | Messages we can send to the Send Queue
data SenderQMsg = SenderQCancel PieceNum Block -- ^ Peer requested that we cancel a piece
| SenderQM Message -- ^ We want to send the Message to the peer
| SenderOChoke -- ^ We want to choke the peer
| SenderQRequestPrune PieceNum Block -- ^ Prune SendQueue of this (pn, blk) pair
data CF = CF { sqIn :: TChan SenderQMsg
, sqOut :: TMVar B.ByteString
, bandwidthCh :: BandwidthChannel
}
data ST = ST { outQueue :: Q.Queue Message
, bytesTransferred :: Integer
}
instance Logging CF where
logName _ = "Process.Peer.SendQueue"
-- | sendQueue Process, simple version.
-- TODO: Split into fast and slow.
start :: TChan SenderQMsg -> TMVar B.ByteString -> BandwidthChannel
-> SupervisorChan
-> IO ThreadId
start inC outC bandwC supC = spawnP (CF inC outC bandwC) (ST Q.empty 0)
(catchP (foreverP pgm)
(defaultStopHandler supC))
pgm :: Process CF ST ()
pgm = {-# SCC "Peer.SendQueue" #-} do
q <- gets outQueue
l <- gets bytesTransferred
-- Gather together events which may trigger
when (l > 0) rateUpdateEvent
ic <- asks sqIn
ov <- asks sqOut
r <- case Q.first q of
Nothing -> liftIO $ atomically (readTChan ic >>= return . Right)
Just p -> do let bs = encodePacket p
sz = fromIntegral $ B.length bs
liftIO . atomically $
(putTMVar ov bs >> return (Left sz)) `orElse`
(readTChan ic >>= return . Right)
case r of
Left sz ->
modify (\s -> s { bytesTransferred = bytesTransferred s + sz
, outQueue = Q.remove (outQueue s)})
Right m ->
case m of
SenderQM msg -> modifyQ (Q.push msg)
SenderQCancel n blk -> modifyQ (Q.filter (filterPiece n (blockOffset blk)))
SenderOChoke -> do modifyQ (Q.filter filterAllPiece)
modifyQ (Q.push Choke)
SenderQRequestPrune n blk ->
modifyQ (Q.filter (filterRequest n blk))
rateUpdateEvent :: Process CF ST ()
rateUpdateEvent = {-# SCC "Peer.SendQ.rateUpd" #-} do
l <- gets bytesTransferred
bwc <- asks bandwidthCh
liftIO . atomically $ writeTChan bwc l
modify (\s -> s { bytesTransferred = 0 })
filterAllPiece :: Message -> Bool
filterAllPiece (Piece _ _ _) = True
filterAllPiece _ = False
filterPiece :: PieceNum -> Int -> Message -> Bool
filterPiece n off (Piece n1 off1 _) | n == n1 && off == off1 = False
| otherwise = True
filterPiece _ _ _ = True
filterRequest :: PieceNum -> Block -> Message -> Bool
filterRequest n blk (Request n1 blk1) | n == n1 && blk == blk1 = False
| otherwise = True
filterRequest _ _ _ = True
modifyQ :: (Q.Queue Message -> Q.Queue Message) -> Process CF ST ()
modifyQ f = modify (\s -> s { outQueue = f (outQueue s) })