oficina / meet Public

Clone
git clone https://amadan.net/oficina/meet

Plain git — no account needed to clone.

Download

Download this file

1package hub
2
3import (
4 "testing"
5 "time"
6)
7
8// terminate runs with the hub's one lock held, and the stream's reader
9// is not excluded by that lock: it drains the channel on its own
10// goroutine, whenever it is scheduled. So a failed non-blocking send
11// proves the buffer was full at that instant and nothing more, and the
12// eviction that follows must not be a blocking receive — on an empty
13// channel it would hold h.mu against every room in the process, waiting
14// for an event that only h.mu's holder could send (Codex S2-4).
15//
16// The window is a few instructions wide, so this is a probabilistic
17// test run many times rather than a single deterministic one. Against
18// the unfixed code it hangs a few thousand rounds in, with and without
19// -race; against the fixed code the whole run costs a quarter of a
20// second (a second and a third under -race). A round that has not
21// finished within the deadline is the deadlock, and the test says so
22// rather than hanging the package.
23func TestTerminateNeverBlocksOnAChannelTheReaderDrained(t *testing.T) {
24 const rounds = 50000
25 for i := 0; i < rounds; i++ {
26 s := newSub()
27 for j := 0; j < subBuffer; j++ {
28 s.ch <- Message{Type: TypePeer}
29 }
30 // The reader: a stream handler, draining as fast as it is given
31 // events, exactly as serveStream does.
32 drained := make(chan struct{})
33 go func() {
34 defer close(drained)
35 for range s.C() {
36 }
37 }()
38 done := make(chan struct{})
39 go func() {
40 defer close(done)
41 s.terminate(Message{Type: TypeLost, Reason: ReasonRemoved})
42 }()
43 select {
44 case <-done:
45 case <-time.After(5 * time.Second):
46 t.Fatalf("round %d: terminate blocked on a drained channel with the hub lock held", i)
47 }
48 <-drained
49 }
50}
51