kable icon indicating copy to clipboard operation
kable copied to clipboard

`connect()` freezes when invoked multiple times concurrently

Open ln-12 opened this issue 2 years ago • 3 comments

I am using your library for quite some time now and never had this issue on Android. However, on iOS I rarely get the following error when the connection to my other device fails:

[DEBUG] ===== CONNECT =====
2022-06-08 16:47:23.481038+0200 Debug[1821:62982] I/Kable: 5DAB21AF-411C-3802-5F8A-ABDA17408F8A Connecting

2022-06-08 16:47:23.702496+0200 Debug[1821:62982] Sentry - debug:: Add breadcrumb: <SentryBreadcrumb: 0x281878c80>
2022-06-08 16:47:23.823228+0200 Debug[1821:62982] D/Kable: 5DAB21AF-411C-3802-5F8A-ABDA17408F8A CentralManagerDelegate state change
  state: DidConnect(identifier=5DAB21AF-411C-3802-5F8A-ABDA17408F8A)
2022-06-08 16:47:23.823619+0200 Debug[1821:62982] V/Kable: 5DAB21AF-411C-3802-5F8A-ABDA17408F8A discoverServices

2022-06-08 16:47:23.834300+0200 Debug[1821:62982] Sentry - debug:: Add breadcrumb: <SentryBreadcrumb: 0x281883380>
2022-06-08 16:47:24.586824+0200 Debug[1821:62982] Sentry - debug:: currentOrientation is unknown.
2022-06-08 16:47:25.159094+0200 Debug[1821:62982] I/Kable: 5DAB21AF-411C-3802-5F8A-ABDA17408F8A Disconnected

2022-06-08 16:47:25.166130+0200 Debug[1821:62982] E/Kable: 5DAB21AF-411C-3802-5F8A-ABDA17408F8A Failed to connect

kotlinx.coroutines.JobCancellationException: LazyDeferredCoroutine was cancelled; job=LazyDeferredCoroutine{Cancelling}@ab111d0
    at 0   shared                              0x10719d4cb        kfun:kotlinx.coroutines.JobCancellationException#<init>(kotlin.String;kotlin.Throwable?;kotlinx.coroutines.Job){} + 179 
    at 1   shared                              0x10714c107        kfun:kotlinx.coroutines.JobSupport#cancel(kotlin.coroutines.cancellation.CancellationException?){} + 203 
    at 2   shared                              0x1071e09a3        kfun:com.juul.kable.ApplePeripheral.onDisconnected#internal + 647 
    at 3   shared                              0x1071ed057        kfun:com.juul.kable.ApplePeripheral.$connectAsync$lambda-6$lambda-3COROUTINE$475.invokeSuspend#internal + 447 
    at 4   shared                              0x1071ed2d3        kfun:com.juul.kable.ApplePeripheral.$connectAsync$lambda-6$lambda-3COROUTINE$475.invoke#interna

The call for peripheral.connect() looks like this:

try {
    Log.debug { "===== CONNECT =====" }
    newPeripheral.connect()
    Log.debug { "===== DONE =====" }
} catch(ex: Exception) {
    Log.debug { "===== ERROR =====" }
    Log.debug { "Connecting failed!\n" + ex.stackTraceToString() }
    return@coroutineScope true
} finally {
    Log.debug { "===== FINALLY =====" }
}

As you can see from the error message above, ===== CONNECT ===== is printed, by neither ===== ERROR ===== nor ===== FINALLY ===== appears, so I am assuming that connect() never returns.

On iOS, I call this code from the UI thread. Do you have any idea why this might happen? When the connection can be established, everything works fine afterwards.

ln-12 avatar Jun 08 '22 14:06 ln-12

I think that I found the cause of the problem. The connect() method was called two times in parallel. Depending of the execution speed and order, the call sometimes hung up and sometimes went through.

ln-12 avatar Jun 08 '22 15:06 ln-12

I think that I found the cause of the problem. The connect() method was called two times in parallel. Depending of the execution speed and order, the call sometimes hung up and sometimes went through.

Interesting. Re-opening, as connect should support concurrent calls (i.e. it is a bug if it is stalling in that situation).

It may be a while before we can find time to prioritize fixing this. Thanks for the thorough issue report, it will really help when we do eventually get around to fixing this!

Although it is good to hear that you found a way to prevent the stalling.

twyatt avatar Jun 08 '22 18:06 twyatt

Alright, thank you.

For comparison, here the connection also fails but the connect() call does not block forever as above with the exact same code:

[DEBUG] ===== CONNECT =====
2022-06-08 17:09:30.175347+0200  Debug[1976:69737] I/Kable: 5DAB21AF-411C-3802-5F8A-ABDA17408F8A Connecting

2022-06-08 17:09:30.532635+0200  Debug[1976:69737] D/Kable: 5DAB21AF-411C-3802-5F8A-ABDA17408F8A CentralManagerDelegate state change
  state: DidConnect(identifier=5DAB21AF-411C-3802-5F8A-ABDA17408F8A)
2022-06-08 17:09:30.533500+0200  Debug[1976:69737] V/Kable: 5DAB21AF-411C-3802-5F8A-ABDA17408F8A discoverServices

2022-06-08 17:09:31.130085+0200  Debug[1976:70004] D/Kable/Delegate: 5DAB21AF-411C-3802-5F8A-ABDA17408F8A 5DAB21AF-411C-3802-5F8A-ABDA17408F8A didDiscoverServices

2022-06-08 17:09:32.023419+0200  Debug[1976:69737] I/Kable: 5DAB21AF-411C-3802-5F8A-ABDA17408F8A Disconnected

2022-06-08 17:09:32.023617+0200  Debug[1976:69737] D/Kable: 5DAB21AF-411C-3802-5F8A-ABDA17408F8A CentralManagerDelegate state change
  state: DidDisconnect(identifier=5DAB21AF-411C-3802-5F8A-ABDA17408F8A, error=Error Domain=CBErrorDomain Code=6 "The connection has timed out unexpectedly." UserInfo={NSLocalizedDescription=The connection has timed out unexpectedly.})
2022-06-08 17:09:32.038659+0200  Debug[1976:69737] E/Kable: 5DAB21AF-411C-3802-5F8A-ABDA17408F8A Failed to connect

kotlinx.coroutines.JobCancellationException: LazyDeferredCoroutine was cancelled; job=LazyDeferredCoroutine{Cancelling}@a081090
    at 0   shared                              0x1054ac56b        kfun:kotlinx.coroutines.JobCancellationException#<init>(kotlin.String;kotlin.Throwable?;kotlinx.coroutines.Job){} + 179 
    at 1   shared                              0x10545b1a7        kfun:kotlinx.coroutines.JobSupport#cancel(kotlin.coroutines.cancellation.CancellationException?){} + 203 
    at 2   shared                              0x1054efa43        kfun:com.juul.kable.ApplePeripheral.onDisconnected#internal + 647 
    at 3   shared                              0x1054fc0f7        kfun:com.juul.kable.ApplePeripheral.$connectAsync$lambda-6$lambda-3COROUTINE$477.invokeSuspend#internal + 447 
    at 4   shared                              0x1054fc373        kfun:com.juul.kable.ApplePeripheral.$connectAsync$lambda-6$lambda-3COROUTINE$477.invoke#interna
[DEBUG] ===== ERROR =====
[DEBUG] Connecting failed!
kotlinx.coroutines.JobCancellationException: LazyDeferredCoroutine was cancelled; job=LazyDeferredCoroutine{Cancelled}@a081090
    at 0   shared                              0x1054ac56b        kfun:kotlinx.coroutines.JobCancellationException#<init>(kotlin.String;kotlin.Throwable?;kotlinx.coroutines.Job){} + 179 
    at 1   shared                              0x10545b1a7        kfun:kotlinx.coroutines.JobSupport#cancel(kotlin.coroutines.cancellation.CancellationException?){} + 203 
    at 2   shared                              0x1054efa43        kfun:com.juul.kable.ApplePeripheral.onDisconnected#internal + 647 
    at 3   shared                              0x1054fc0f7        kfun:com.juul.kable.ApplePeripheral.$connectAsync$lambda-6$lambda-3COROUTINE$477.invokeSuspend#internal + 447 
    at 4   shared                              0x1054fc373        kfun:com.juul.kable.ApplePeripheral.$connectAsync$lambda-6$lambda-3COROUTINE$477.invoke#internal + 199 
    at 5   shared                              0x105490f23        kfun:kotlinx.coroutines.flow.<no name provided>_1_1.$collect_2_2$<anonymous>_7_3COROUTINE$550.invokeSuspend#internal + 507 
    at 6   shared                              0x1054911e3        kfun:kotlinx.coroutines.flow.<no name provided>_1_1.$collect_2_2$<anonymous>_7_3$FUNCTION_REFERENCE$469.emit#internal + 347 
    at 7   shared                              0x10547f0a7        kfun:kotlinx.coroutines.flow.SharedFlowImpl.$collectCOROUTINE$524#invokeSuspend(kotlin.Result<kotlin.Any?>){}kotlin.Any? + 2535 
    at 8   shared                              0x105383feb        kfun:kotlin.coroutines.native.internal.BaseContinuationImpl#resumeWith(kotlin.Result<kotlin.Any?>){} + 295 
    at 9   shared                              0x1054963fb        kfun:kotlinx.coroutines.DispatchedTask#run(){} + 1527 
    at 10  shared                              0x1054ae947        kfun:kotlinx.coroutines.DarwinMainDispatcher.$dispatch$<anonymous>_3$FUNCTION_REFERENCE$487.$<bridge-UNN>invoke(){}#internal + 307 
    at 11  shared                              0x105acbd3f        ___6f72672e6a6574627261696e732e6b6f746c696e783a6b6f746c696e782d636f726f7574696e65732d636f7265_knbridge835_block_invoke + 451 
    at 12  libdispatch.dylib                   0x104600c6f        _dispatch_call_block_and_release + 31 
    at 13  libdispatch.dylib                   0x1046027bf        _dispatch_client_callout + 19 
    at 14  libdispatch.dylib                   0x104612c67        _dispatch_main_queue_drain + 1203 
    at 15  libdispatch.dylib                   0x1046127a3        _dispatch_main_queue_callback_4CF + 43 
    at 16  CoreFoundation                      0x1899f57ff        <redacted> + 15 
    at 17  CoreFoundation                      0x1899af703        <redacted> + 2531 
    at 18  CoreFoundation                      0x1899c2bc7        CFRunLoopRunSpecific + 599 
    at 19  GraphicsServices                    0x1a5af6373        GSEventRunModal + 163 
    at 20  UIKitCore                           0x18c332647        <redacted> + 1099 
    at 21  UIKitCore                           0x18c0b3d8f        UIApplicationMain + 363 
    at 22  SwiftUI                             0x191814f23        <redacted> + 163 
    at 23  SwiftUI                             0x191742e07        <redacted> + 251 
    at 24  SwiftUI                             0x1917240f3        $s7SwiftUI3AppPAAE4mainyyFZ + 127 
    at 25   Debug                     0x102bb918b        $s15S__Debug0A5UmAppV5$mainyyFZ + 39 
    at 26   Debug                     0x102bb926b        main + 11 
    at 27  dyld                                0x104151ce3        0x0 + 4363459811 

[DEBUG] ===== FINALLY =====

ln-12 avatar Jun 08 '22 18:06 ln-12

@ln-12 if you have some time, can you test if the following SNAPSHOT fixes this issue for you?

repositories {
    maven("https://oss.sonatype.org/content/repositories/snapshots")
}

dependencies {
    implementation("com.juul.kable:core:0.23.0-issue-339-1-SNAPSHOT")
}

Thank you!

twyatt avatar Jun 01 '23 08:06 twyatt

I had the same issue and that SNAPSHOT fixes it !

rafaelfrancisco-dev avatar Jun 01 '23 10:06 rafaelfrancisco-dev

I had the same issue and that SNAPSHOT fixes it !

@rafaelfrancisco-dev thanks for testing/validating the fix!

twyatt avatar Jun 01 '23 17:06 twyatt

Unfortunately, I am no longer working on the project which used this library, so I can not test it additionally :/

ln-12 avatar Jun 01 '23 17:06 ln-12