Skip to content

Commit effb92e

Browse files
committed
Messages are now interleaved when multiplex-mini-protocols: true
1 parent 6a6671a commit effb92e

File tree

5 files changed

+106
-31
lines changed

5 files changed

+106
-31
lines changed

simulation/ouroboros-leios-sim.cabal

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ library
139139
, io-classes
140140
, io-sim
141141
, kdt
142+
, lens
142143
, leios-trace-hs
143144
, linear
144145
, mtl

simulation/src/Chan.hs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ import Control.Monad.Class.MonadFork (MonadFork)
3333
import Control.Tracer (Contravariant (contramap), Tracer)
3434
import Data.Maybe (fromMaybe)
3535
import GHC.Generics
36-
import ModelTCP (kibibytes, mkTcpConnProps)
36+
import ModelTCP (kibibytes, lensTcpEvent, mkTcpConnProps)
3737
import TimeCompat (DiffTime, MonadDelay, MonadMonotonicTimeNSec, MonadTime)
3838

3939
data ConnectionConfig = ConnectionConfig
@@ -64,14 +64,14 @@ newConnectionBundle tracer = \case
6464
newUnMuxedConnectionBundle $ \tofrom ->
6565
newConnectionSimple (traceAsBundleMsg @bundle tofrom tracer) simpleConnProps
6666
ConnectionConfig (TransportSimple simpleConnProps) _mux@True -> do
67-
let tracer' = contramap ((fmap . fmap) fromBearerMsg) tracer
67+
let tracer' = mapBearerTracer (lensLabelTcpDir . lensTcpEvent) tracer
6868
(mA, mB) <- newConnectionSimple tracer' simpleConnProps
6969
(,) <$> newMuxChan mA <*> newMuxChan mB
7070
ConnectionConfig (TransportTcp tcpConnProps) _mux@False ->
7171
newUnMuxedConnectionBundle $ \tofrom ->
7272
newConnectionTCP (traceAsBundleMsg @bundle tofrom tracer) tcpConnProps
7373
ConnectionConfig (TransportTcp tcpConnProps) _mux@True -> do
74-
let tracer' = contramap ((fmap . fmap) fromBearerMsg) tracer
74+
let tracer' = mapBearerTracer (lensLabelTcpDir . lensTcpEvent) tracer
7575
(mA, mB) <- newConnectionTCP tracer' tcpConnProps
7676
(,) <$> newMuxChan mA <*> newMuxChan mB
7777

simulation/src/Chan/Mux.hs

Lines changed: 92 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
{-# LANGUAGE FlexibleContexts #-}
66
{-# LANGUAGE FlexibleInstances #-}
77
{-# LANGUAGE GADTSyntax #-}
8+
{-# LANGUAGE LambdaCase #-}
89
{-# LANGUAGE RankNTypes #-}
910
{-# LANGUAGE RecordWildCards #-}
1011
{-# LANGUAGE ScopedTypeVariables #-}
@@ -14,18 +15,22 @@
1415
module Chan.Mux (
1516
ToFromBundleMsg (..),
1617
ConnectionBundle (..),
17-
fromBearerMsg,
18+
mapBearerTracer,
1819
newMuxChan,
1920
) where
2021

2122
import Chan.Core (Chan (..))
22-
import Chan.TCP (MessageSize (..))
23+
import Chan.TCP (Bytes, MessageSize (..))
2324
import qualified Control.Category as Cat
2425
import Control.Concurrent.Class.MonadMVar (MonadMVar (..))
25-
import Control.Monad (forever)
26+
import qualified Control.Lens as Lens
27+
import Control.Monad (forM_, forever)
2628
import Control.Monad.Class.MonadFork (MonadFork (forkIO))
29+
import Control.Tracer (Tracer (Tracer), traceWith)
2730
import Data.Array (Array, listArray, (!))
2831
import Data.Kind
32+
import Data.Foldable (traverse_)
33+
import Data.Functor.Const (Const (Const), getConst)
2934
import STMCompat
3035

3136
class ConnectionBundle bundle where
@@ -56,66 +61,86 @@ instance Cat.Category ToFromBundleMsg where
5661
-- dynToFromBundleMsg :: Typeable a => ToFromBundleMsg Dynamic a
5762
-- dynToFromBundleMsg = ToFromBundleMsg toDyn (fromJust . fromDynamic)
5863

59-
data BearerMsg a = BearerMsg !Int a
64+
data BorneMsg a = BorneMsg !Int a
6065

61-
fromBearerMsg :: BearerMsg a -> a
62-
fromBearerMsg (BearerMsg _ a) = a
63-
64-
instance MessageSize a => MessageSize (BearerMsg a) where
65-
messageSizeBytes (BearerMsg _ a) = 1 + messageSizeBytes a
66+
-- | Each bearer message is some slices of various 'BorneMsg's
67+
--
68+
-- The mini protocols never see this, so this type is not exported. It does
69+
-- occur in the argument types of some exported functions, but the caller
70+
-- should be using parametric functions to generate those arguments.
71+
data BearerMsg a = BearerMsg !Bytes [BorneMsg a]
72+
-- ^ the cumulative size of the slices the borne messages whose /final/ slice
73+
-- is in this message
74+
75+
instance MessageSize (BearerMsg a) where
76+
messageSizeBytes (BearerMsg sz _) = 1 + sz
77+
78+
mapBearerTracer ::
79+
Applicative m =>
80+
Lens.Lens s t (BearerMsg a) a ->
81+
Tracer m t ->
82+
Tracer m s
83+
mapBearerTracer lens tracer = Tracer $ \x -> do
84+
let BearerMsg _ msgs = getConst $ lens Const x -- why doesn't Lens.view lens x type check?
85+
flip traverse_ msgs $ \(BorneMsg _ a) -> do
86+
traceWith tracer $ Lens.set lens a x
6687

6788
newMuxChan ::
6889
forall bundle m.
69-
(ConnectionBundle bundle, MonadMVar m, MonadSTM m, MonadFork m) =>
90+
(ConnectionBundle bundle, MonadMVar m, MonadSTM m, MonadFork m, MessageSize (BundleMsg bundle)) =>
7091
Chan m (BearerMsg (BundleMsg bundle)) ->
7192
m (bundle (Chan m))
7293
newMuxChan bearer = do
73-
sendLock <- newMVar ()
7494
-- Bit of a hack to use these TVars, could run the traverseConnectionBundle
7595
-- in a reader+state monad instead. That'd be cleaner.
7696
recvQueuesAccum <- newTVarIO []
7797
recvQueuesIx <- newTVarIO (0 :: Int)
98+
sendQueue <- newTQueueIO
7899
chans <-
79100
traverseConnectionBundle
80101
( newMuxChanSingle @bundle
81-
bearer
82-
sendLock
102+
sendQueue
83103
recvQueuesIx
84104
recvQueuesAccum
85105
)
86106
toFromBundleMsgBundle
87107
recvQueues <- reverse <$> readTVarIO recvQueuesAccum
88108
let recvQueues' = listArray (0, length recvQueues - 1) recvQueues
89109
_ <- forkIO $ demuxer @bundle bearer recvQueues'
110+
_ <- forkIO $ muxer @bundle bearer sendQueue
90111
return chans
91112

92113
newMuxChanSingle ::
93114
forall bundle m a.
94-
(MonadMVar m, MonadSTM m) =>
95-
Chan m (BearerMsg (BundleMsg bundle)) ->
96-
MVar m () ->
115+
(MonadMVar m, MonadSTM m, MessageSize (BundleMsg bundle)) =>
116+
TQueue m (MVar m (), Bytes, BorneMsg (BundleMsg bundle)) ->
97117
TVar m Int ->
98118
TVar m [RecvQueue m (BundleMsg bundle)] ->
99119
ToFromBundleMsg (BundleMsg bundle) a ->
100120
m (Chan m a)
101121
newMuxChanSingle
102-
bearer
103-
sendLock
122+
sendQueue
104123
recvQueuesIx
105124
recvQueuesAccum
106125
ToFromBundleMsg{..} = do
107-
queue <- newTQueueIO
126+
recvQueue <- newTQueueIO
127+
-- A mini protocol can have at most one message in the send buffer.
128+
sendLock <- newMVar ()
108129
i <- atomically $ do
109-
modifyTVar recvQueuesAccum (RecvQueue fromBundleMsg queue :)
130+
modifyTVar recvQueuesAccum (RecvQueue fromBundleMsg recvQueue :)
110131
i <- readTVar recvQueuesIx
111132
writeTVar recvQueuesIx $! (i + 1)
112133
return i
113134
return
114135
Chan
115-
{ readChan = atomically (readTQueue queue)
116-
, writeChan = \msg ->
117-
let !muxmsg = BearerMsg i (toBundleMsg msg)
118-
in withMVar sendLock $ \_ -> writeChan bearer muxmsg
136+
{ readChan = atomically (readTQueue recvQueue)
137+
, writeChan = \msg -> do
138+
let !bundleMsg = toBundleMsg msg
139+
!muxmsg = BorneMsg i bundleMsg
140+
takeMVar sendLock
141+
atomically $
142+
writeTQueue sendQueue $
143+
(sendLock, messageSizeBytes bundleMsg, muxmsg)
119144
}
120145

121146
data RecvQueue m mm where
@@ -129,10 +154,49 @@ demuxer ::
129154
m ()
130155
demuxer bearer queues =
131156
forever $ do
132-
BearerMsg i msg <- readChan bearer
133-
case queues ! i of
134-
RecvQueue convert queue ->
135-
atomically $ writeTQueue queue $! convert msg
157+
BearerMsg _ msgs <- readChan bearer
158+
forM_ msgs $ \(BorneMsg i msg) ->
159+
case queues ! i of
160+
RecvQueue convert queue ->
161+
atomically $ writeTQueue queue $! convert msg
162+
163+
muxer ::
164+
forall bundle m.
165+
(MonadMVar m, MonadSTM m) =>
166+
Chan m (BearerMsg (BundleMsg bundle)) ->
167+
TQueue m (MVar m (), Bytes, BorneMsg (BundleMsg bundle)) ->
168+
m ()
169+
muxer bearer sendQueue =
170+
forever $ do
171+
x <- atomically (readTQueue sendQueue)
172+
(muxmsg, locks) <- go 0 [] [] x
173+
mapM_ (flip putMVar ()) locks
174+
writeChan bearer muxmsg
175+
where
176+
--- from ouroboros-network's @Network.Mux.Bearer.makeSocketBearer'@
177+
sliceBytes = 12288
178+
loafBytes = 131072
179+
180+
go !accBytes acc locks (lock, bytes, msg) = do
181+
let !accBytes' = accBytes + min sliceBytes bytes
182+
(acc', locks') <-
183+
if bytes <= sliceBytes
184+
then do
185+
-- We do not release the lock before finalizing the loaf because a
186+
-- single loaf should include slices from at most one borne message
187+
-- per protocol.
188+
pure (msg : acc, lock : locks)
189+
else do
190+
-- reenqueue the rest of the message
191+
let !bytes' = bytes - sliceBytes
192+
atomically $ writeTQueue sendQueue (lock, bytes', msg)
193+
pure (acc, locks)
194+
195+
let result = (BearerMsg accBytes' acc', locks')
196+
if accBytes' >= loafBytes then pure result else do
197+
atomically (tryReadTQueue sendQueue) >>= \case
198+
Nothing -> pure result
199+
Just x -> go accBytes' acc' locks' x
136200

137201
data ExampleBundle f = ExampleBundle
138202
{ exampleFoo :: f Int

simulation/src/Chan/TCP.hs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
{-# LANGUAGE DeriveFunctor #-}
2+
{-# LANGUAGE LambdaCase #-}
23
{-# LANGUAGE NamedFieldPuns #-}
34
{-# LANGUAGE ScopedTypeVariables #-}
45

@@ -35,6 +36,11 @@ import TimeCompat
3536
data LabelTcpDir e = DirClientToServer e | DirServerToClient e
3637
deriving (Eq, Ord, Show, Functor)
3738

39+
lensLabelTcpDir :: Functor f => (a -> f b) -> LabelTcpDir a -> f (LabelTcpDir b)
40+
lensLabelTcpDir f = \case
41+
DirClientToServer x -> DirClientToServer <$> f x
42+
DirServerToClient x -> DirServerToClient <$> f x
43+
3844
-- | Class for messages to be sent over a simulated TCP connection.
3945
-- To correctly model the timing of the messages sent over the connection we
4046
-- need to know a reasonable approximation of the message size. This does not

simulation/src/ModelTCP.hs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ module ModelTCP (
1616
TcpMsgForecast (..),
1717
forecastTcpMsgSend,
1818
TcpEvent (..),
19+
lensTcpEvent,
1920
traceTcpSend,
2021
mkTcpConnProps,
2122
kibibytes,
@@ -366,6 +367,9 @@ data TcpEvent msg
366367
[TcpMsgForecast] -- tcp internal activity
367368
deriving (Show, Functor)
368369

370+
lensTcpEvent :: Functor f => (a -> f b) -> TcpEvent a -> f (TcpEvent b)
371+
lensTcpEvent f (TcpSendMsg x y z) = (\x' -> TcpSendMsg x' y z) <$> f x
372+
369373
traceTcpSend ::
370374
TcpConnProps ->
371375
-- | message sizes to send eagerly, back-to-back

0 commit comments

Comments
 (0)