33{-# LANGUAGE DataKinds #-}
44{-# LANGUAGE FlexibleContexts #-}
55{-# LANGUAGE GeneralizedNewtypeDeriving #-}
6+ {-# LANGUAGE LambdaCase #-}
67{-# LANGUAGE NamedFieldPuns #-}
78{-# LANGUAGE PackageImports #-}
89{-# LANGUAGE RankNTypes #-}
@@ -29,7 +30,7 @@ import Data.Bits
2930import Data.ByteString.Lazy qualified as BL
3031import Data.ByteString.Lazy.Char8 qualified as BL8 (pack )
3132import Data.Functor.Contravariant ((>$<) )
32- import Data.List (dropWhileEnd , nub )
33+ import Data.List (dropWhileEnd , group , nub )
3334import Data.List qualified as List
3435import Data.Map qualified as M
3536import Data.Maybe (isNothing )
@@ -314,6 +315,33 @@ instance Arbitrary DummyCapability where
314315 ]
315316
316317
318+ newtype MiniProtocolWeights = MiniProtocolWeights (MiniProtocolWeight , MiniProtocolWeight )
319+ deriving (Eq , Show )
320+
321+ instance Arbitrary MiniProtocolWeights where
322+ arbitrary = do
323+ wt1 <- arbitrary
324+ wt2 <- arbitrary
325+ let mkWt = MiniProtocolWeight
326+ if wt1 == wt2
327+ then pure $ MiniProtocolWeights (mkWt 1 , mkWt 1 )
328+ else pure $ MiniProtocolWeights (wt1, wt2)
329+
330+ shrink (MiniProtocolWeights (wt1, wt2)) =
331+ let wt1' = shrink wt1
332+ wt2' = shrink wt2
333+ mkWt = MiniProtocolWeight
334+ in MiniProtocolWeights (mkWt 1 , mkWt 1 ) : nub (zipWith (curry MiniProtocolWeights ) wt1' wt2')
335+
336+
337+ newtype MiniProtocolWeight = MiniProtocolWeight Word8
338+ deriving (Eq , Show )
339+
340+ instance Arbitrary MiniProtocolWeight where
341+ arbitrary = MiniProtocolWeight <$> choose (1 , 4 )
342+ shrink (MiniProtocolWeight wt) = MiniProtocolWeight <$> filter (> 0 ) (shrink wt)
343+
344+
317345-- | A pair of two bytestrings which lengths are unevenly distributed
318346--
319347data Uneven = Uneven DummyPayload DummyPayload
@@ -1008,8 +1036,10 @@ prop_mux_2_minis_Socket_buf cap a b = ioProperty $
10081036-- The Mux bearer should alternate between sending data for the two responders.
10091037--
10101038prop_mux_starvation :: Uneven
1039+ -> MiniProtocolWeights
10111040 -> Property
1012- prop_mux_starvation (Uneven response0 response1) =
1041+ prop_mux_starvation (Uneven response0 response1)
1042+ (MiniProtocolWeights (MiniProtocolWeight wt1, MiniProtocolWeight wt2)) =
10131043 let sduLen = Mx. SDUSize 1280 in
10141044 (BL. length (unDummyPayload response0) > 2 * fromIntegral (Mx. getSDUSize sduLen)) &&
10151045 (BL. length (unDummyPayload response1) > 2 * fromIntegral (Mx. getSDUSize sduLen)) ==>
@@ -1072,14 +1102,14 @@ prop_mux_starvation (Uneven response0 response1) =
10721102 miniProtocolDir = Mx. ResponderDirectionOnly ,
10731103 miniProtocolLimits = defaultMiniProtocolLimits,
10741104 miniProtocolCapability = Nothing ,
1075- miniProtocolWeight = 1
1105+ miniProtocolWeight = wt1
10761106 }
10771107 serverApp3 = MiniProtocolInfo {
10781108 miniProtocolNum = Mx. MiniProtocolNum 3 ,
10791109 miniProtocolDir = Mx. ResponderDirectionOnly ,
10801110 miniProtocolLimits = defaultMiniProtocolLimits,
10811111 miniProtocolCapability = Nothing ,
1082- miniProtocolWeight = 1
1112+ miniProtocolWeight = wt2
10831113 }
10841114
10851115 serverMux <- Mx. new serverTracer [serverApp2, serverApp3]
@@ -1119,28 +1149,29 @@ prop_mux_starvation (Uneven response0 response1) =
11191149
11201150 -- Then look at the message trace to check for starvation.
11211151 trace <- atomically $ readTVar traceHeaderVar
1122- let es = map Mx. mhNum (take 100 (reverse trace))
1123- ls = dropWhile (\ e -> e == head es) es
1124- fair = verifyStarvation ls
1152+ let es = map Mx. mhNum (take 100 (reverse trace))
1153+ -- We can't make 100% sure that both servers start responding at the same
1154+ -- time but once they are both up and running messages should alternate
1155+ -- between ReqResp2 and ReqResp3, so we drop the prefix of the protocol
1156+ -- which goes first, and trim the suffix when the first protocol finishes
1157+ ls = dropWhile (\ e -> e == head es) es
1158+ ls' = dropWhileEnd (\ e -> e == last ls) ls
1159+ fair = label (" shrinkage " ++ labelPr_ ((length ls' * 100 ) `div` length es) ++ " %" ) $
1160+ verifyStarvation ls' (\ case (Mx. MiniProtocolNum 2 ) -> wt1; _otherwise -> wt2)
11251161 return $ res_short .&&. res_long .&&. fair
11261162 where
1127- -- We can't make 100% sure that both servers start responding at the same
1128- -- time but once they are both up and running messages should alternate
1129- -- between ReqResp2 and ReqResp3
1130- verifyStarvation :: Eq a => [a ] -> Property
1131- verifyStarvation [] = property True
1132- verifyStarvation ms =
1133- let ms' = dropWhileEnd (\ e -> e == last ms)
1134- (head ms : dropWhile (\ e -> e == head ms) ms)
1135- ++ [last ms]
1136- in
1137- label (" length " ++ labelPr_ ((length ms' * 100 ) `div` length ms) ++ " %" )
1138- $ label (" length " ++ label_ (length ms')) $ alternates ms'
1163+ verifyStarvation :: Eq a => [a ] -> (a -> Word8 ) -> Property
1164+ verifyStarvation [] _atowt = property True
1165+ verifyStarvation ms atowt = label (" length " ++ label_ (length ms)) .
1166+ label (" groups " ++ label_ (length (group ms))) .
1167+ alternates $ group ms
11391168
11401169 where
11411170 alternates [] = True
11421171 alternates (_: [] ) = True
1143- alternates (a : b : as) = a /= b && alternates (b : as)
1172+ alternates (a : b : [] ) = length a <= fromIntegral (atowt (head a))
1173+ && length b <= fromIntegral (atowt (head b))
1174+ alternates (a : b : as) = (length a == fromIntegral (atowt (head a))) && alternates (b : as)
11441175
11451176 label_ :: Int -> String
11461177 label_ n = mconcat
0 commit comments