diff --git a/lib/protocol/protocol.go b/lib/protocol/protocol.go index 76565f05b..116807f52 100644 --- a/lib/protocol/protocol.go +++ b/lib/protocol/protocol.go @@ -332,8 +332,11 @@ func (c *rawConnection) Request(folder string, name string, offset int64, size i // ClusterConfig sends the cluster configuration message to the peer. // It must be called just once (as per BEP), otherwise it will panic. func (c *rawConnection) ClusterConfig(config ClusterConfig) { - c.clusterConfigBox <- &config - close(c.clusterConfigBox) + select { + case c.clusterConfigBox <- &config: + close(c.clusterConfigBox) + case <-c.closed: + } } func (c *rawConnection) Closed() bool { diff --git a/lib/protocol/protocol_test.go b/lib/protocol/protocol_test.go index 0783f2fda..386600975 100644 --- a/lib/protocol/protocol_test.go +++ b/lib/protocol/protocol_test.go @@ -757,3 +757,26 @@ func TestSha256OfEmptyBlock(t *testing.T) { } } } + +// TestClusterConfigAfterClose checks that ClusterConfig does not deadlock when +// ClusterConfig is called on a closed connection. +func TestClusterConfigAfterClose(t *testing.T) { + m := newTestModel() + + c := NewConnection(c0ID, &testutils.BlockingRW{}, &testutils.BlockingRW{}, m, "name", CompressAlways).(wireFormatConnection).Connection.(*rawConnection) + c.Start() + + c.internalClose(errManual) + + done := make(chan struct{}) + go func() { + c.ClusterConfig(ClusterConfig{}) + close(done) + }() + + select { + case <-done: + case <-time.After(time.Second): + t.Fatal("timed out before Cluster Config returned") + } +}