@@ -26,7 +26,11 @@ type environment struct {
2626 network * Network
2727 listeners []chan listenerItem
2828 lastCompleteAndConcluded [2 ]uint64
29- mtx sync.Mutex
29+ // roundIn holds the inbound channels handed to the voter, per round, so they
30+ // can be closed once the round concludes. RoundData is called more than once
31+ // for a round number, hence a slice.
32+ roundIn map [uint64 ][]chan SignedMessageError [string , uint32 , Signature , ID ]
33+ mtx sync.Mutex
3034
3135 concludedCalled chan struct {}
3236}
@@ -36,6 +40,7 @@ func newEnvironment(network *Network, localID ID) environment {
3640 chain : newDummyChain (),
3741 localID : localID ,
3842 network : network ,
43+ roundIn : make (map [uint64 ][]chan SignedMessageError [string , uint32 , Signature , ID ]),
3944 concludedCalled : make (chan struct {}),
4045 }
4146}
@@ -84,6 +89,12 @@ func (e *environment) RoundData(
8489 outgoing := make (Output [string , uint32 ])
8590 incoming := e .network .MakeRoundComms (round , e .localID , outgoing )
8691
92+ // Remember it so Concluded can close it: the voter reads this channel through
93+ // a forwarding goroutine that ends only when the channel does.
94+ e .mtx .Lock ()
95+ e .roundIn [round ] = append (e .roundIn [round ], incoming )
96+ e .mtx .Unlock ()
97+
8798 var outgoingFunc = func (m Message [string , uint32 ]) error {
8899 outgoing <- m
89100 return nil
@@ -123,8 +134,16 @@ func (e *environment) Concluded(
123134 _ HistoricalVotes [string , uint32 , Signature , ID ],
124135) error {
125136 e .mtx .Lock ()
126- defer e .mtx .Unlock ()
127137 e .lastCompleteAndConcluded [1 ] = round
138+ incoming := e .roundIn [round ]
139+ delete (e .roundIn , round )
140+ e .mtx .Unlock ()
141+
142+ // The round is over, so release the inbound channels handed out for it.
143+ for _ , in := range incoming {
144+ e .network .StopRoundComms (round , in )
145+ }
146+
128147 go func () {
129148 e .concludedCalled <- struct {}{}
130149 }()
@@ -259,13 +278,29 @@ func (bm *BroadcastNetwork[M, N]) AddNode(f func(N) M, out chan N) (in chan M) {
259278func (bm * BroadcastNetwork [M , N ]) route () {
260279 defer bm .routeWG .Done ()
261280 for msg := range bm .receiver {
281+ // Under the lock: RemoveNode closes a node's channel, and closing one a
282+ // producer is about to send on panics. Senders are buffered, so holding it
283+ // across the delivery does not block.
262284 bm .mu .Lock ()
263285 bm .history = append (bm .history , msg )
264- senders := append ([]chan M (nil ), bm .senders ... )
265- bm .mu .Unlock ()
266- for _ , sender := range senders {
286+ for _ , sender := range bm .senders {
267287 sender <- msg
268288 }
289+ bm .mu .Unlock ()
290+ }
291+ }
292+
293+ // RemoveNode deregisters a node's inbound channel and closes it, shutting down
294+ // the voter reading it. Held under bm.mu so it cannot race a delivery in route.
295+ func (bm * BroadcastNetwork [M , N ]) RemoveNode (in chan M ) {
296+ bm .mu .Lock ()
297+ defer bm .mu .Unlock ()
298+ for i , sender := range bm .senders {
299+ if sender == in {
300+ bm .senders = append (bm .senders [:i ], bm .senders [i + 1 :]... )
301+ close (in )
302+ return
303+ }
269304 }
270305}
271306
@@ -402,6 +437,31 @@ func (n *Network) MakeGlobalComms(
402437 }, out )
403438}
404439
440+ // StopRoundComms closes one inbound channel handed out by MakeRoundComms. Only
441+ // that node's channel: the round network is shared, and other voters may still
442+ // be in this round.
443+ func (n * Network ) StopRoundComms (
444+ roundNumber uint64 ,
445+ in chan SignedMessageError [string , uint32 , Signature , ID ],
446+ ) {
447+ n .mtx .Lock ()
448+ round , ok := n .rounds [roundNumber ]
449+ n .mtx .Unlock ()
450+
451+ if ok {
452+ round .RemoveNode (in )
453+ }
454+ }
455+
456+ // StopGlobalComms closes the inbound channel handed to a voter by
457+ // MakeGlobalComms, which is how that voter is shut down.
458+ func (n * Network ) StopGlobalComms (in chan GlobalInItem [string , uint32 , Signature , ID ]) {
459+ n .mtx .Lock ()
460+ defer n .mtx .Unlock ()
461+
462+ n .globalMessages .RemoveNode (in )
463+ }
464+
405465func (n * Network ) SendMessage (message CommunicationIn [string , uint32 , Signature , ID ]) {
406466 n .globalMessages .SendMessage (GlobalInItem [string , uint32 , Signature , ID ]{message , nil })
407467}
0 commit comments