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

Potential Memory Management Issue with emitWithAck and timingOut(after:) #1470

Open
mahyarq opened this issue Dec 23, 2023 · 1 comment
Open

Comments

@mahyarq
Copy link

mahyarq commented Dec 23, 2023

Environment:

Library Version: 16.1.0
Xcode: tested on Xcode 14 and Xcode 15
Platform: iOS 16 and iOS 17

Description:

I've encountered an issue with the emitWithAck function in the Socket.IO-Client-Swift library, specifically when using the timingOut(after:) closure. The OnAckCallback object returned by emitWithAck seems to get deallocated prematurely if not explicitly retained in the client code.

In addition to the premature deallocation of the OnAckCallback instance, a critical part of the issue is that the callback code socket.ackHandlers.timeoutAck(self.ackNumber) within the timingOut(after:) function is never executed as expected. This is because self (referring to the OnAckCallback instance) becomes nil after the timeout period elapses. This behavior results in the failure of the timeout handling logic, as the necessary callback for a timeout situation is not called.

Steps to Reproduce:

  1. Call emitWithAck().timingOut(after:) on a SocketIOClient instance. Make sure server doesnt send any callbacks.
  2. Observe that the OnAckCallback instance is deallocated before the timeout closure is executed, resulting in self being nil within the closure.

Expected Behavior:

The OnAckCallback instance should be retained internally by the library until the timingOut(after:) closure is executed or the timeout occurs.

Actual Behavior:

The OnAckCallback instance is deallocated prematurely unless explicitly retained in the client code.

My hacky fix

In my current SocketHandler class, i've added a var emitAckCallBack: OnAckCallback? which i assign the value of socket.emitWithAck() and afterwards i call emitAckCallBack?.timingOut(after:) and thereafter set the emitAckCallBack to nil inside the completion block. This seems to hold a reference to OnAckCallback which prevents it from getting deallocated prematurely.

Code example:

var emitAckCallBack: OnAckCallback?

func send() {
    emitAckCallBack = self.socket.emitWithAck("send_event", "test")
    
    emitAckCallBack?.timingOut(after: 2) {  data in
        print("Ack received with data: \(data)")
    }
}
@mahyarq mahyarq mentioned this issue Dec 23, 2023
@xmollv
Copy link

xmollv commented Feb 1, 2024

I've been going crazy trying to figure out why the timingOut would never fire. And indeed, the issue is that without retaining it it's being deallocated. You can easily see the issue on OnAckCallback.swift:140. self is nil when the dispatch fires and therefore the callback is never called!

image

// @nuclearace

jonstaff added a commit to jonstaff/socket.io-client-swift that referenced this issue Feb 9, 2024
See this issue for more information: socketio#1470
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants