diff --git a/net/conn/multiconn.go b/net/conn/multiconn.go index f4915db2bdbce9318973777baed2bcf4a5d88cc6..1c4533d72fd64cd4601482ee013e33c1da35d84c 100644 --- a/net/conn/multiconn.go +++ b/net/conn/multiconn.go @@ -10,6 +10,9 @@ import ( u "github.com/jbenet/go-ipfs/util" ) +// MultiConnMap is for shorthand +type MultiConnMap map[u.Key]*MultiConn + // Duplex is a simple duplex channel type Duplex struct { In chan []byte @@ -160,10 +163,15 @@ func (c *MultiConn) fanInSingle(child Conn) { // in case it still is in the map, remove it. c.Lock() delete(c.conns, child.ID()) + connLen := len(c.conns) c.Unlock() c.Children().Done() child.Children().Done() + + if connLen == 0 { + c.Close() // close self if all underlying children are gone? + } }() for { diff --git a/net/conn/multiconn_test.go b/net/conn/multiconn_test.go index 0fde058d36ec1f8f360bbd051ba8e1e4e9505a58..bc0b59bf24041d0e596fc83fb99e52171e547691 100644 --- a/net/conn/multiconn_test.go +++ b/net/conn/multiconn_test.go @@ -293,3 +293,32 @@ func TestMulticonnSendUnderlying(t *testing.T) { msgsFrom1.CheckDone(t) msgsFrom2.CheckDone(t) } + +func TestMulticonnClose(t *testing.T) { + // t.Skip("fooo") + + log.Info("TestMulticonnSendUnderlying") + ctx := context.Background() + c1, c2 := setupMultiConns(t, ctx) + + for _, c := range c1.conns { + c.Close() + } + + for _, c := range c2.conns { + c.Close() + } + + timeout := time.After(100 * time.Millisecond) + select { + case <-c1.Closed(): + case <-timeout: + t.Fatal("timeout") + } + + select { + case <-c2.Closed(): + case <-timeout: + t.Fatal("timeout") + } +} diff --git a/net/conn/secure_conn_test.go b/net/conn/secure_conn_test.go index 5a78870d0ee6bf71aa9dec0e7928cd5a3ad3dd71..17c0f4bd905200023d2859fe48ff29882d979119 100644 --- a/net/conn/secure_conn_test.go +++ b/net/conn/secure_conn_test.go @@ -50,7 +50,7 @@ func TestSecureClose(t *testing.T) { select { case <-c1.Closed(): default: - t.Fatal("not done after cancel") + t.Fatal("not done after close") } c2.Close() @@ -58,7 +58,7 @@ func TestSecureClose(t *testing.T) { select { case <-c2.Closed(): default: - t.Fatal("not done after cancel") + t.Fatal("not done after close") } cancel() // close the listener :P