davidstump / SwiftPhoenixClient

Connect your Phoenix and iOS applications through WebSockets!
MIT License
506 stars 146 forks source link

`Channel` `bindingsDel` array is not thread safe and appears to rarely cause a crash #223

Closed jatindervrify closed 1 year ago

jatindervrify commented 2 years ago

Hi there,

We're encountering a rare crash where we see Swift/ContiguousArrayBuffer.swift:580: Fatal error: Index out of range when SwiftPhoenixClient attempts to filter bindingsDel in the Channel object while being manipulated from another thread. This is extremely hard to reproduce but we're seeing the crash in our Sentry logs. The call stack is below and we've isolated it to the code:

Code snippet:

    self.bindingsDel
      .filter( { return $0.event == message.event } )
      .forEach( { $0.callback.call(handledMessage) } )

Callstack:

Thread 28 Crashed:
0   libswiftCore.dylib              0x30a7a523c         _assertionFailure
1   libswiftCore.dylib              0x30a7a5238         _assertionFailure
2   libswiftCore.dylib              0x30a7a4fd8         _assertionFailure
3   libswiftCore.dylib              0x30a7a48cc         _assertionFailure
4   libswiftCore.dylib              0x30a781670         _ArrayBuffer._checkInoutAndNativeTypeCheckedBounds
5   libswiftCore.dylib              0x30a787a60         Array.subscript.read
6   libswiftCore.dylib              0x30a787978         [A]
7   libswiftCore.dylib              0x30a7e3404         IndexingIterator<T>
8   libswiftCore.dylib              0x30a7a1508         _ArrayProtocol.filter
9   -----                           0x2062563e0         Channel.trigger
10  -----                           0x20627a9fc         Socket.onConnectionMessage
11  -----                           0x20627aa28         thunk for closure
12  -----                           0x20627cc44         thunk for closure
13  libswiftCore.dylib              0x30a841334         Sequence.forEach
14  -----                           0x20627a5ac         Socket.onConnectionMessage
15  -----                           0x20627de10         Socket.onMessage
16  -----                           0x20627dffc         Socket
17  -----                           0x2062619f8         URLSessionTransport.receive
18  libswiftFoundation.dylib        0x30a112170         NSURLSessionWebSocketTask.receive
19  libswiftFoundation.dylib        0x30a112300         thunk for closure
20  Foundation                      0x30438eacc         __NSBLOCKOPERATION_IS_CALLING_OUT_TO_A_BLOCK__
21  Foundation                      0x3043a0c54         -[NSBlockOperation main]
22  Foundation                      0x3043796f0         __NSOPERATION_IS_INVOKING_MAIN__
23  Foundation                      0x30438a56c         -[NSOperation start]
24  Foundation                      0x30438db14         __NSOPERATIONQUEUE_IS_STARTING_AN_OPERATION__
25  Foundation                      0x30439bbac         __NSOQSchedule_f
26  libdispatch.dylib               0x300d1b440         _dispatch_block_async_invoke2
27  libdispatch.dylib               0x300d0c65c         _dispatch_client_callout
28  libdispatch.dylib               0x300d0fb30         _dispatch_continuation_pop
29  libdispatch.dylib               0x300d0f18c         _dispatch_async_redirect_invoke
30  libdispatch.dylib               0x300d1ddcc         _dispatch_root_queue_drain
31  libdispatch.dylib               0x300d1e5f4         _dispatch_worker_thread2
32  libsystem_pthread.dylib         0x3e085b0b4         _pthread_wqthread

I wish there was more details I could provide. It may be happening for our clients when the SDK attempts to reconnect after a connection interruption but I can't concretely say that. I'd suggest using Swift Concurrency and placing the class in an Actor to avoid these types of conditions but that's a lot of work and I'm not sure if the class is in a position to adopt that framework. Let me know if we can help at all.

Cheers

dsrees commented 1 year ago

See #235