diff --git a/server.go b/server.go index a87a3647..b3c85543 100644 --- a/server.go +++ b/server.go @@ -98,7 +98,7 @@ type baseServer struct { protocol.Version, ) quicConn - closeOnce sync.Once + closeMx sync.Mutex errorChan chan struct{} // is closed when the server is closed closeErr error running chan struct{} // closed as soon as run() returns @@ -338,15 +338,19 @@ func (s *baseServer) Close() error { } func (s *baseServer) close(e error, notifyOnClose bool) { - s.closeOnce.Do(func() { - s.closeErr = e - close(s.errorChan) + s.closeMx.Lock() + if s.closeErr != nil { + s.closeMx.Unlock() + return + } + s.closeErr = e + close(s.errorChan) + <-s.running + s.closeMx.Unlock() - <-s.running - if notifyOnClose { - s.onClose() - } - }) + if notifyOnClose { + s.onClose() + } } // Addr returns the server's network address diff --git a/transport_test.go b/transport_test.go index 4e89dcfb..aeeb1153 100644 --- a/transport_test.go +++ b/transport_test.go @@ -122,6 +122,27 @@ var _ = Describe("Transport", func() { tr.Close() }) + It("closes transport concurrently with listener", func() { + // try 10 times to trigger race conditions + for i := 0; i < 10; i++ { + packetChan := make(chan packetToRead) + tr := &Transport{Conn: newMockPacketConn(packetChan)} + ln, err := tr.Listen(&tls.Config{}, nil) + Expect(err).ToNot(HaveOccurred()) + ch := make(chan bool) + // Close transport and listener concurrently. + go func() { + ch <- true + Expect(ln.Close()).To(Succeed()) + ch <- true + }() + <-ch + close(packetChan) + Expect(tr.Close()).To(Succeed()) + <-ch + } + }) + It("drops unparseable QUIC packets", func() { addr := &net.UDPAddr{IP: net.IPv4(9, 8, 7, 6), Port: 1234} packetChan := make(chan packetToRead)