11package com.juul.kable
22
3- import co.touchlab.stately.collections.IsoMutableList
4- import co.touchlab.stately.isolate.IsolateState
53import com.juul.kable.ObservationEvent.CharacteristicChange
64import com.juul.kable.ObservationEvent.Error
75import com.juul.kable.logs.Logging
6+ import kotlinx.atomicfu.locks.SynchronizedObject
7+ import kotlinx.atomicfu.locks.synchronized
88import kotlinx.coroutines.flow.Flow
99import kotlinx.coroutines.flow.MutableSharedFlow
1010import kotlinx.coroutines.flow.filter
@@ -44,7 +44,9 @@ internal class Observers<T>(
4444) {
4545
4646 val characteristicChanges = MutableSharedFlow <ObservationEvent <T >>(extraBufferCapacity = Int .MAX_VALUE )
47- private val observations = Observations ()
47+
48+ private val observations = mutableMapOf<Characteristic , Observation >()
49+ private val lock = SynchronizedObject ()
4850
4951 fun acquire (
5052 characteristic : Characteristic ,
@@ -54,12 +56,10 @@ internal class Observers<T>(
5456 val handler = peripheral.observationHandler()
5557 val identifier = peripheral.identifier
5658
57- // `IsoMutableList` created outside of `getOrPut`, because it would deadlock on Native if created in
58- // `Observation` constructor.
59- val subscribers = IsoMutableList <OnSubscriptionAction >()
60-
61- val observation = observations.getOrPut(characteristic) {
62- Observation (state, handler, characteristic, logging, identifier.toString(), subscribers)
59+ val observation = synchronized(lock) {
60+ observations.getOrPut(characteristic) {
61+ Observation (state, handler, characteristic, logging, identifier.toString())
62+ }
6363 }
6464
6565 return characteristicChanges
@@ -91,7 +91,9 @@ internal class Observers<T>(
9191 }
9292
9393 suspend fun onConnected () {
94- observations.entries.forEach { (_, observation) ->
94+ synchronized(lock) {
95+ observations.entries
96+ }.forEach { (_, observation) ->
9597 // Pipe failures to `characteristicChanges` while honoring in-flight connection cancellations.
9698 try {
9799 observation.onConnected()
@@ -103,23 +105,3 @@ internal class Observers<T>(
103105 }
104106 }
105107}
106-
107- private class Observations : IsolateState <MutableMap <Characteristic , Observation >>(
108- producer = { mutableMapOf() },
109- ) {
110-
111- val entries: List <Pair <Characteristic , Observation >>
112- get() = access { observations ->
113- // `map` used as a means to copy entries, to prevent freeze exceptions on Native.
114- observations.entries.map { (characteristic, observation) ->
115- characteristic to observation
116- }
117- }
118-
119- fun getOrPut (
120- characteristic : Characteristic ,
121- defaultValue : () -> Observation ,
122- ): Observation = access { observations ->
123- observations.getOrPut(characteristic, defaultValue)
124- }
125- }
0 commit comments