Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

lib/connections, lib/model: Refactor connection close handling (fixes #3466) #3490

Closed
wants to merge 2 commits into from

Conversation

calmh
Copy link
Member

@calmh calmh commented Aug 10, 2016

Purpose

So there were some issues here. The main problem was that model.Close(deviceID) was overloaded to mean "the connection was closed by the protocol layer" and "i want to close this connection". That meant it could get called twice - once to close the connection and then once more when the connection was closed.

After this refactor there is instead a Closed(conn) method that is the callback. I didn't need to change the parameter in the end, but I think it's clearer what it means when it takes the connection that was closed instead of a device ID. To close a connection, the new close(deviceID) method is used instead, which only closes the underlying connection and leaves the cleanup to the Closed() callback.

I also changed how we do connection switching. Instead of the connection service calling close and then adding the connection, it just adds the new connection. The model knows that it already has a connection and makes sure to close and clean out that one before adding the new connection.

To make sure to sequence this properly I added a new map of channels that get created on connection add and closed by Closed(), so that AddConnection() can do the close and wait for the cleanup to happen before proceeding.

Testing

Our testing capabilities here really suck and we should work on that.

What I did was remove all of the already-connected and priority checks in the connection service, so that the connection loop ran continuously and every new connection became a connection switch. This crashed instantly on the old code, but runs for at least a few minutes with a connection switch every five seconds on the new code. I also run the TestRestart* integration things and they still work.

I might have missed a possible deadlock somewhere, but hey... :/

…3466)

So there were some issues here. The main problem was that
model.Close(deviceID) was overloaded to mean "the connection was closed
by the protocol layer" and "i want to close this connection". That meant
it could get called twice - once *to* close the connection and then once
more when the connection *was* closed.

After this refactor there is instead a Closed(conn) method that is the
callback. I didn't need to change the parameter in the end, but I think
it's clearer what it means when it takes the connection that was closed
instead of a device ID. To close a connection, the new close(deviceID)
method is used instead, which only closes the underlying connection and
leaves the cleanup to the Closed() callback.

I also changed how we do connection switching. Instead of the connection
service calling close and then adding the connection, it just adds the
new connection. The model knows that it already has a connection and
makes sure to close and clean out that one before adding the new
connection.

To make sure to sequence this properly I added a new map of channels
that get created on connection add and closed by Closed(), so that
AddConnection() can do the close and wait for the cleanup to happen
before proceeding.
@@ -729,7 +729,7 @@ func (c *rawConnection) close(err error) {
}
c.awaitingMut.Unlock()

go c.receiver.Close(c.id, err)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm beginning to realize that "go" for these kinds of things as a workaround against locking issues is a quite bad code smell.

@AudriusButkevicius
Copy link
Member

@st-review merge

@st-review
Copy link

👌 Merged as e52be3d. Thanks, @calmh!

st-review pushed a commit that referenced this pull request Aug 10, 2016
…3466)

So there were some issues here. The main problem was that
model.Close(deviceID) was overloaded to mean "the connection was closed
by the protocol layer" and "i want to close this connection". That meant
it could get called twice - once *to* close the connection and then once
more when the connection *was* closed.

After this refactor there is instead a Closed(conn) method that is the
callback. I didn't need to change the parameter in the end, but I think
it's clearer what it means when it takes the connection that was closed
instead of a device ID. To close a connection, the new close(deviceID)
method is used instead, which only closes the underlying connection and
leaves the cleanup to the Closed() callback.

I also changed how we do connection switching. Instead of the connection
service calling close and then adding the connection, it just adds the
new connection. The model knows that it already has a connection and
makes sure to close and clean out that one before adding the new
connection.

To make sure to sequence this properly I added a new map of channels
that get created on connection add and closed by Closed(), so that
AddConnection() can do the close and wait for the cleanup to happen
before proceeding.

GitHub-Pull-Request: #3490
@st-review st-review closed this Aug 10, 2016
@calmh calmh deleted the fix-3466 branch August 10, 2016 09:40
@st-review st-review added the frozen-due-to-age Issues closed and untouched for a long time, together with being locked for discussion label Aug 10, 2017
@syncthing syncthing locked and limited conversation to collaborators Aug 10, 2017
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
frozen-due-to-age Issues closed and untouched for a long time, together with being locked for discussion
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants