-
Notifications
You must be signed in to change notification settings - Fork 147
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
fix crashes from Socket.connect
race conditions
#260
Conversation
e1b65e0
to
39eb8b5
Compare
Co-authored-by: Eric Jensen <[email protected]>
@ejensen would you please re-review this PR? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for taking this into your own hands and submitting a PR.
- The
weak self
ref 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
@@ -260,6 +260,7 @@ public class Socket: PhoenixTransportDelegate { | |||
paramsClosure: self.paramsClosure, | |||
vsn: vsn) | |||
|
|||
self.connection?.disconnect(code: CloseCode.normal.rawValue, reason: "connect called") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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 }
...
}
@@ -266,6 +266,11 @@ open class URLSessionTransport: NSObject, PhoenixTransport, URLSessionWebSocketD | |||
// The task has terminated. Inform the delegate that the transport has closed abnormally | |||
// 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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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 reference
error 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 messagesfailure
error messages ifself
is not nil inPhoenixTransport.receive
Socket.connect
so that old connections are cleaned up.Tested these changes in an app that calls
socket.connect()
twice whenever returning from the background and confirmedonOpen
callback only invoked once andonError
not invoked.