common/interrupt: close connections outside group lock

This commit is contained in:
embogomolov
2026-07-22 13:41:53 +03:00
parent 7c19b660b4
commit d2805ffcc9
3 changed files with 75 additions and 5 deletions

View File

@@ -0,0 +1,66 @@
package interrupt
import (
"net"
"sync"
"testing"
"time"
)
type closeBarrierConn struct {
net.Conn
barrier *sync.WaitGroup
}
func (c *closeBarrierConn) Close() error {
c.barrier.Done()
c.barrier.Wait()
return c.Conn.Close()
}
func TestNestedGroupsInterruptWithoutDeadlock(t *testing.T) {
groupA := NewGroup()
groupB := NewGroup()
barrier := &sync.WaitGroup{}
barrier.Add(2)
barrierA, barrierAPeer := net.Pipe()
barrierB, barrierBPeer := net.Pipe()
t.Cleanup(func() {
barrierAPeer.Close()
barrierBPeer.Close()
})
groupA.NewConn(&closeBarrierConn{Conn: barrierA, barrier: barrier}, true, false)
groupB.NewConn(&closeBarrierConn{Conn: barrierB, barrier: barrier}, true, false)
connA, connAPeer := net.Pipe()
connB, connBPeer := net.Pipe()
t.Cleanup(func() {
connAPeer.Close()
connBPeer.Close()
})
wrapperA := groupA.NewConn(connA, true, false)
wrapperB := groupB.NewConn(connB, true, false)
groupA.NewConn(wrapperB, true, false)
groupB.NewConn(wrapperA, true, false)
done := make(chan struct{}, 2)
go func() {
groupA.Interrupt(true)
done <- struct{}{}
}()
go func() {
groupB.Interrupt(true)
done <- struct{}{}
}()
timeout := time.NewTimer(time.Second)
defer timeout.Stop()
for range 2 {
select {
case <-done:
case <-timeout.C:
t.Fatal("nested group interrupt deadlocked")
}
}
}