summaryrefslogtreecommitdiff
path: root/src/Network
diff options
context:
space:
mode:
authorSam Truzjan <pxqr.sta@gmail.com>2014-02-22 02:35:27 +0400
committerSam Truzjan <pxqr.sta@gmail.com>2014-02-22 02:35:27 +0400
commit279ea9c29e0f61541ea0281678412d6dc6586d60 (patch)
tree172610c92d693292ac109e4f54e4ac349b1c2c05 /src/Network
parent8d721087ca4e7878f602cc6e4ddcaa68c5274350 (diff)
Deterministic transaction id allocation
Diffstat (limited to 'src/Network')
-rw-r--r--src/Network/BitTorrent/Tracker/RPC/UDP.hs47
1 files changed, 37 insertions, 10 deletions
diff --git a/src/Network/BitTorrent/Tracker/RPC/UDP.hs b/src/Network/BitTorrent/Tracker/RPC/UDP.hs
index 1ea47100..4fea7d17 100644
--- a/src/Network/BitTorrent/Tracker/RPC/UDP.hs
+++ b/src/Network/BitTorrent/Tracker/RPC/UDP.hs
@@ -226,7 +226,7 @@ initialConnectionId = ConnectionId 0x41727101980
226 226
227-- | Transaction Id is used within a UDP RPC. 227-- | Transaction Id is used within a UDP RPC.
228newtype TransactionId = TransactionId Word32 228newtype TransactionId = TransactionId Word32
229 deriving (Eq, Ord, Serialize) 229 deriving (Eq, Ord, Enum, Bounded, Serialize)
230 230
231instance Show TransactionId where 231instance Show TransactionId where
232 showsPrec _ (TransactionId tid) = showString "0x" <> showHex tid 232 showsPrec _ (TransactionId tid) = showString "0x" <> showHex tid
@@ -377,6 +377,35 @@ isExpired Connection {..} = do
377-- Transactions 377-- Transactions
378-----------------------------------------------------------------------} 378-----------------------------------------------------------------------}
379 379
380-- | Sometimes 'genTransactionId' may return already used transaction
381-- id. We use a good entropy source but the issue /still/ (with very
382-- small probabality) may happen. If the collision happen then this
383-- function tries to find nearest unused slot, otherwise pending
384-- transactions table is full.
385firstUnused :: SockAddr -> TransactionId -> PendingQueries -> TransactionId
386firstUnused addr rid m = do
387 case M.splitLookup rid <$> M.lookup addr m of
388 Nothing -> rid
389 Just (_ , Nothing, _ ) -> rid
390 Just (lt, Just _ , gt) ->
391 case backwardHole (keys lt) rid <|> forwardHole rid (keys gt) of
392 Nothing -> error "firstUnused: table is full" -- impossible
393 Just tid -> tid
394 where
395 forwardHole a []
396 | a == maxBound = Nothing
397 | otherwise = Just (succ a)
398 forwardHole a (b : xs)
399 | succ a == b = forwardHole b xs
400 | otherwise = Just (succ a)
401
402 backwardHole [] a
403 | a == minBound = Nothing
404 | otherwise = Just (pred a)
405 backwardHole (b : xs) a
406 | b == pred a = backwardHole xs b
407 | otherwise = Just (pred a)
408
380register :: SockAddr -> TransactionId -> PendingResponse 409register :: SockAddr -> TransactionId -> PendingResponse
381 -> PendingQueries -> PendingQueries 410 -> PendingQueries -> PendingQueries
382register addr tid ares = M.alter insertId addr 411register addr tid ares = M.alter insertId addr
@@ -394,15 +423,13 @@ unregister addr tid = M.update deleteId addr
394 where 423 where
395 m' = M.delete tid m 424 m' = M.delete tid m
396 425
397-- | Generate a new unused transaction id. 426-- | Generate a new unused transaction id and register as pending.
398allocTransaction :: Manager -> SockAddr -> MVar Response -> IO TransactionId 427allocTransaction :: Manager -> SockAddr -> PendingResponse -> IO TransactionId
399allocTransaction Manager {..} addr ares = modifyMVar pendingResps bindId 428allocTransaction Manager {..} addr ares =
400 where 429 modifyMVar pendingResps $ \ m -> do
401 bindId m = do 430 rndId <- genTransactionId
402 tid <- genTransactionId 431 let tid = firstUnused addr rndId m
403 case M.lookup tid =<< M.lookup addr m of 432 return (register addr tid ares m, tid)
404 Just _ -> bindId m -- already used, retry
405 Nothing -> return (register addr tid ares m, tid)
406 433
407-- | Wake up blocked thread and return response back. 434-- | Wake up blocked thread and return response back.
408commitTransaction :: Manager -> SockAddr -> TransactionId -> Response -> IO () 435commitTransaction :: Manager -> SockAddr -> TransactionId -> Response -> IO ()