@@ -9,6 +9,7 @@ import io.ably.lib.util.Log
99import kotlinx.coroutines.*
1010import kotlinx.coroutines.channels.Channel.Factory.UNLIMITED
1111import kotlinx.coroutines.flow.MutableSharedFlow
12+ import java.util.concurrent.CancellationException
1213
1314/* *
1415 * Default implementation of LiveObjects interface.
@@ -34,12 +35,6 @@ internal class DefaultLiveObjects(private val channelName: String, internal val
3435 private val sequentialScope =
3536 CoroutineScope (Dispatchers .Default .limitedParallelism(1 ) + CoroutineName (channelName) + SupervisorJob ())
3637
37- /* *
38- * Coroutine scope for handling callbacks asynchronously.
39- */
40- private val callbackScope =
41- CoroutineScope (Dispatchers .Default + CoroutineName (" LiveObjectsCallback-$channelName " ) + SupervisorJob ())
42-
4338 /* *
4439 * Event bus for handling incoming object messages sequentially.
4540 */
@@ -55,7 +50,7 @@ internal class DefaultLiveObjects(private val channelName: String, internal val
5550 }
5651
5752 override fun getRootAsync (callback : Callback <LiveMap >) {
58- callbackScope .launchWithCallback(callback) { getRootAsync() }
53+ GlobalCallbackScope .launchWithCallback(callback) { getRootAsync() }
5954 }
6055
6156 override fun createMap (liveMap : LiveMap ): LiveMap {
@@ -97,12 +92,10 @@ internal class DefaultLiveObjects(private val channelName: String, internal val
9792
9893 override fun offAll () = objectsManager.offAll()
9994
100- private suspend fun getRootAsync (): LiveMap {
101- return sequentialScope.async {
102- adapter.throwIfInvalidAccessApiConfiguration(channelName)
103- objectsManager.ensureSynced(state)
104- objectsPool.get(ROOT_OBJECT_ID ) as LiveMap
105- }.await()
95+ private suspend fun getRootAsync (): LiveMap = withContext(sequentialScope.coroutineContext) {
96+ adapter.throwIfInvalidAccessApiConfiguration(channelName)
97+ objectsManager.ensureSynced(state)
98+ objectsPool.get(ROOT_OBJECT_ID ) as LiveMap
10699 }
107100
108101 /* *
@@ -188,9 +181,13 @@ internal class DefaultLiveObjects(private val channelName: String, internal val
188181 }
189182
190183 // Dispose of any resources associated with this LiveObjects instance
191- fun dispose () {
192- incomingObjectsHandler.cancel() // objectsEventBus automatically garbage collected when collector is cancelled
184+ // Called when either connection is closed, channel is released, so channel goes into detached state.
185+ fun dispose (reason : String ) {
186+ val cancellationError = CancellationException (" Objects disposed for channel $channelName , reason: $reason " )
187+ incomingObjectsHandler.cancel(cancellationError) // objectsEventBus automatically garbage collected when collector is cancelled
193188 objectsPool.dispose()
194189 objectsManager.dispose()
190+ // Don't cancel sequentialScope (needed in public methods), just cancel ongoing coroutines
191+ sequentialScope.coroutineContext.cancelChildren(cancellationError)
195192 }
196193}
0 commit comments