Creating channel blocks main thread
danipralea opened this issue · comments
Hello again @danielrhodes
I found out that creating a Channel
sometime blocks the main thread.
My code:
webSocketsRoomChannel = SocketRouter.shared.client.create(WebSocketEventsChannel.channel, identifier: channelIdPayload, autoSubscribe: true, bufferActions: true)
I will come up with a fix if I find one
Update:
Is this an infinite cycle?
Have you retained the channel anywhere?
it's a simple variable on the conversations view controller
var webSocketsRoomChannel: Channel?
What version are you using? Something similar to the above used to happen, especially if the the channel had not been retained anywhere. But that should be fixed now.
- ActionCableClient (0.2.2):
- Starscream (~> 2.0.1)
The only problem is that the issue is not consistent. It's very rare for me, but of course - very often for the testers
Do you have some sample code for how you are setting up everything and in the view controller. Would be happy to take that and test it on my end.
class ConversationViewController: UIViewController {
var webSocketsRoomChannel: Channel?
func subscribeToWebsocketChannel() {
guard SocketRouter.shared.isConnected, let channel = viewModel.channel, let identifier = channel.identifier else {
print("socket is not connected or data is not sufficient")
return
}
let channelIdPayload = ["channel_id" : identifier]
webSocketsRoomChannel = SocketRouter.shared.client.create(WebSocketEventsChannel.channel, identifier: channelIdPayload, autoSubscribe: true, bufferActions: true)
// Receive a message from the server. Typically a Dictionary.
webSocketsRoomChannel?.onReceive = { (JSONResponse : Any?, error : Error?) in
print("Received", JSONResponse ?? "N/A", error ?? "no error")
}
// A channel has successfully been subscribed to.
webSocketsRoomChannel?.onSubscribed = {
print("Yay! You're subscribed!")
}
// A channel was unsubscribed, either manually or from a client disconnect.
webSocketsRoomChannel?.onUnsubscribed = {
print("Unsubscribed")
}
// The attempt at subscribing to a channel was rejected by the server.
webSocketsRoomChannel?.onRejected = {
print("Rejected")
}
}
func removeWebSocketsNotifications() {
// Remove channel subscription
webSocketsRoomChannel?.unsubscribe()
// Stop listening notification
NotificationCenter.default.removeObserver(self)
}
func addWebSocketsNotifications() {
NotificationCenter.default.addObserver(self, selector: #selector(webSocketConnected(_:)), name: NSNotification.Name(rawValue: WebsocketDidConnectNotification), object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(webSocketDisconnected(_:)), name: NSNotification.Name(rawValue: WebsocketDidDisconnectNotification), object: nil)
}
func webSocketConnected(_ notification: NSNotification) {
print("\(self.className): web socket connected")
if let roomChannel = webSocketsRoomChannel, roomChannel.isSubscribed == false {
subscribeToWebsocketChannel()
}
}
func webSocketDisconnected(_ notification: NSNotification) {
print("\(self.className): web socket disconnected")
}
}
So from the looks of Instruments above, it seems like either you were running the application for a very long time or you might be calling subscribeToWebsocketChannel
very very often. The code you highlighted is simply doing a Dictionary lookup, which isn't blocking, and is O(1), so I'm not sure how that would be causing a problem.
I did, however, find that there was some JSON encoding being done from the calling thread (which I assume is main in this case), and so for whatever reason, this might be the source of the time being taken above (sometimes Instruments only makes an educated guess about where in the code things happen).
Try out the json_encoding_on_calling_thread_fix
branch and tell me how that worked for you.
https://github.com/danielrhodes/Swift-ActionCableClient/tree/json_encoding_on_calling_thread_fix
In your Podfile
, you can do the following:
pod 'ActionCableClient', :git => 'https://github.com/danielrhodes/Swift-ActionCableClient.git', :branch => 'json_encoding_on_calling_thread_fix'
Note that I had to change the API slightly, but now there is a callback on the channel.action(:)
method telling you if the action was successfully sent or an error occurred. There is an example on the main README and in the header docs.
@danielrhodes the new method of sending an action doesn't call the callback when an error occurs
The old way of doing it was working fine (throwing an error)
Do you mean on the server side or client side? If the error is on the server side, there is no way for the client to know an error occurred.
on client side. all the problems I have are client-side.
What was the specific error you were looking for?
creating a channel blocks the main thread
How many times are you calling this over the course of runtime? And how many total channels do you have? I ran the example app and put client.create
in a 5000 iteration loop (on the main thread) and profiled it to see if anything weird came up.
Nothing there indicates anything amiss given how many times it was called. In fact, most of that would only occur once because it checks to see if there's already a channel. So that reduces the performance bottleneck to doing a lookup in a dictionary. This seems consistent with your Instruments trace, but I can't see how that would come up if something strange weren't also occurring.
Perhaps a Dictionary
lookup could cause a performance problem if there were an unusually large number of objects or were called in a very tight loop. Swift does not necessarily have the most optimized version of this, but it's certainly not a blocking operation.
I'm only doing it once
So you only go into that ViewController once the entire time the app is running? And it completely blocks the main thread?
From looking at your code above, can you try calling subscribeToWebsocketChannel()
in viewDidLoad
and removing those NSNotification
observers in addWebSocketsNotifications
. You can subscribe to a channel before the client connects and it will automatically subscribe once a connection occurs, so most that logic is unnecessary.
I will definitely try that.
I have a list of channels, which I can select. And sometimes, very randomly - it completely freezes like that.
Thank you for your support. I will try and let you know how it goes
@danielrhodes there's a retain cycle in the create function: