@@ -5,6 +5,7 @@ import kotlinx.coroutines.await
55import kotlinx.coroutines.flow.MutableSharedFlow
66import kotlinx.coroutines.flow.collect
77import kotlinx.coroutines.flow.flow
8+ import kotlinx.coroutines.sync.withLock
89import org.khronos.webgl.DataView
910import org.w3c.dom.events.Event
1011
@@ -41,7 +42,9 @@ internal class Observers(
4142 if (++ observation.count == 1 ) {
4243 bluetoothRemoteGATTCharacteristic.apply {
4344 addEventListener(CHARACTERISTIC_VALUE_CHANGED , observation.listener)
44- startNotifications().await()
45+ peripheral.ioLock.withLock {
46+ startNotifications().await()
47+ }
4548 }
4649 }
4750
@@ -51,24 +54,42 @@ internal class Observers(
5154 emit(it.data)
5255 }
5356 }
54- } finally {
55- if (-- observation.count < 1 ) {
56- bluetoothRemoteGATTCharacteristic.apply {
57- /* Throws `DOMException` if connection is closed:
58- *
59- * DOMException: Failed to execute 'stopNotifications' on 'BluetoothRemoteGATTCharacteristic':
60- * Characteristic with UUID [...] is no longer valid. Remember to retrieve the characteristic
61- * again after reconnecting.
62- *
63- * Wrapped in `runCatching` to silently ignore failure, as notification will already be
64- * invalidated due to the connection being closed.
65- */
66- runCatching { stopNotifications().await() }
67-
68- removeEventListener(CHARACTERISTIC_VALUE_CHANGED , observation.listener)
57+ } catch (t: Throwable ) {
58+ // Unnecessary `catch` block as workaround for KT-37279 (needed until we switch to IR compiler).
59+ // https://youtrack.jetbrains.com/issue/KT-37279
60+ // Once KT-37279 is fixed, this `catch` should be replaced with `finally`.
61+ // See previous logic (before workaround) in:
62+ // https://github.com/JuulLabs/kable/blob/151e54d255bf5595c67023927084d083e6180706/core/src/jsMain/kotlin/Observers.kt#L48-L72
63+ observation.teardown(bluetoothRemoteGATTCharacteristic, characteristic)
64+ return @flow
65+ }
66+ observation.teardown(bluetoothRemoteGATTCharacteristic, characteristic)
67+ }
68+
69+ private suspend fun Observation.teardown (
70+ bluetoothRemoteGATTCharacteristic : BluetoothRemoteGATTCharacteristic ,
71+ characteristic : Characteristic
72+ ) {
73+ if (-- count < 1 ) {
74+ bluetoothRemoteGATTCharacteristic.apply {
75+ /* Throws `DOMException` if connection is closed:
76+ *
77+ * DOMException: Failed to execute 'stopNotifications' on 'BluetoothRemoteGATTCharacteristic':
78+ * Characteristic with UUID [...] is no longer valid. Remember to retrieve the characteristic
79+ * again after reconnecting.
80+ *
81+ * Wrapped in `runCatching` to silently ignore failure, as notification will already be
82+ * invalidated due to the connection being closed.
83+ */
84+ runCatching {
85+ peripheral.ioLock.withLock {
86+ stopNotifications().await()
87+ }
6988 }
70- observers.remove(characteristic)
89+
90+ removeEventListener(CHARACTERISTIC_VALUE_CHANGED , listener)
7191 }
92+ observers.remove(characteristic)
7293 }
7394 }
7495
@@ -89,7 +110,9 @@ internal class Observers(
89110 platformCharacteristic
90111 .bluetoothRemoteGATTCharacteristic
91112 .apply {
92- startNotifications().await()
113+ peripheral.ioLock.withLock {
114+ startNotifications().await()
115+ }
93116 addEventListener(CHARACTERISTIC_VALUE_CHANGED , platformCharacteristic.createListener())
94117 }
95118 }
0 commit comments