fix crashes from Socket.connect race conditions - #260
Conversation
e1b65e0 to
39eb8b5
Compare
Co-authored-by: Eric Jensen <jensen39@gmail.com>
|
@ejensen would you please re-review this PR? |
dsrees
left a comment
There was a problem hiding this comment.
Thank you for taking this into your own hands and submitting a PR.
- The
weak selfref seems like the appropriate solution - I need to test your demo app sample you provided in an Issue before I better understand whats going on with the
abnormalError - I've left a comment regarding your change to the connect function
| paramsClosure: self.paramsClosure, | ||
| vsn: vsn) | ||
|
|
||
| self.connection?.disconnect(code: CloseCode.normal.rawValue, reason: "connect called") |
There was a problem hiding this comment.
The connect call is already checking that the socket is not connected before attempting to connect again.
// Do not attempt to reconnect if the socket is currently connected
guard !isConnected else { return }So in theory, this should never be used. Looking at the JS client (that you linked to in your PR), they only check if the connection exists, not if its been fully connected. So Maybe a better approach here is to update
- guard !isConnected else { return }
+ guard self.connection == nil else { return }
There was a problem hiding this comment.
So in theory, this should never be used.
Where I encountered issues with only guard !isConnected else { return } is calling socket.connect() from both onAppear and onChange(of: scenePhase) when scenePhase is active in a SwiftUI app. On occasion, the socket would be in the connecting state from the first call to connect() and pass the existing guard.
We've since removed the call to connect in onAppear to avoid this race condition entirely.
One concern about guard self.connection == nil else { return } is if the existing connection's state is closing or closed, I would probably expect a new connection to still be opened rather than be left with the existing closed one.
There was a problem hiding this comment.
Then I'd advocate for adding an isConnecting var and checking that as well
public var isConnecting: Bool {
return self.connectionState == .connecting
}
public func connect() {
// Do not attempt to reconnect if the socket is currently connected
guard !isConnecting && !isConnected else { return }
...
}| // if this was caused by an error. | ||
| guard let err = error else { return } | ||
|
|
||
| if let urlError = err as? URLError, urlError.code == .cancelled { |
There was a problem hiding this comment.
From my testing, error is nil when cancelling the task.
|
5.3.4 |
Includes fixes for two flavors of issues we've encountered in our SwiftUI application.
Socket.sendHeartbeat() EXC_BAD_ACCESS (KERN_INVALID_ADDRESS) #253
This one I'm having trouble reproducing locally, but the
Object 0x303285e60 of class HeartbeatTimer deallocated with non-zero retain count 3. This object's deinit, or something called from it, may have created a strong reference to self which outlived deinit, resulting in a dangling referenceerror especially had me thinking it was an issue with a strong reference in HeartbeatTimer. Please let me know if there is a good way to test this!"Socket is not connected" error after backgrounding #258 -
Error when receiving Error Domain=NSPOSIXErrorDomain Code=57 "Socket is not connected"Error messagesfailureerror messages ifselfis not nil inPhoenixTransport.receiveSocket.connectso that old connections are cleaned up.Tested these changes in an app that calls
socket.connect()twice whenever returning from the background and confirmedonOpencallback only invoked once andonErrornot invoked.