Skip to content

Commit ef12004

Browse files
authored
dataconnect(chore): factor out repeated logic for creating CoroutineScope objects (#8151)
1 parent c97fcf5 commit ef12004

5 files changed

Lines changed: 73 additions & 26 deletions

File tree

firebase-dataconnect/src/main/kotlin/com/google/firebase/dataconnect/core/DataConnectBidiConnectStream.kt

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ package com.google.firebase.dataconnect.core
1818

1919
import com.google.firebase.dataconnect.ExperimentalRealtimeQueries
2020
import com.google.firebase.dataconnect.core.LoggerGlobals.debug
21-
import com.google.firebase.dataconnect.util.CoroutineUtils
21+
import com.google.firebase.dataconnect.util.CoroutineUtils.createChildSupervisorScope
2222
import com.google.firebase.dataconnect.util.NullableReference
2323
import com.google.firebase.dataconnect.util.ProtoUtil.toCompactString
2424
import com.google.protobuf.Struct
@@ -75,12 +75,7 @@ internal class DataConnectBidiConnectStream(
7575
private val state =
7676
MutableStateFlow<State>(
7777
run {
78-
val collectCoroutineScope =
79-
CoroutineUtils.createSupervisorCoroutineScope(
80-
coroutineScope.coroutineContext,
81-
logger,
82-
parent = coroutineScope.coroutineContext.job,
83-
)
78+
val collectCoroutineScope = coroutineScope.createChildSupervisorScope(logger)
8479

8580
val completedResponse =
8681
MutableStateFlow(NullableReference<IncomingResponse.Completed>(null))

firebase-dataconnect/src/main/kotlin/com/google/firebase/dataconnect/core/DataConnectCredentialsTokenManager.kt

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import com.google.firebase.dataconnect.core.DataConnectCredentialsTokenManager.G
2424
import com.google.firebase.dataconnect.core.Globals.toScrubbedAccessToken
2525
import com.google.firebase.dataconnect.core.LoggerGlobals.debug
2626
import com.google.firebase.dataconnect.core.LoggerGlobals.warn
27-
import com.google.firebase.dataconnect.util.CoroutineUtils.createSupervisorCoroutineScope
27+
import com.google.firebase.dataconnect.util.CoroutineUtils.createChildSupervisorScope
2828
import com.google.firebase.dataconnect.util.SequencedReference
2929
import com.google.firebase.dataconnect.util.SequencedReference.Companion.nextSequenceNumber
3030
import com.google.firebase.inject.Deferred.DeferredHandler
@@ -40,7 +40,6 @@ import kotlinx.coroutines.CoroutineName
4040
import kotlinx.coroutines.CoroutineScope
4141
import kotlinx.coroutines.CoroutineStart
4242
import kotlinx.coroutines.Deferred
43-
import kotlinx.coroutines.Job
4443
import kotlinx.coroutines.async
4544
import kotlinx.coroutines.cancel
4645
import kotlinx.coroutines.ensureActive
@@ -63,12 +62,7 @@ internal sealed class DataConnectCredentialsTokenManager<T : Any, R : GetTokenRe
6362

6463
@Suppress("LeakingThis") private val weakThis = WeakReference(this)
6564

66-
private val coroutineScope =
67-
createSupervisorCoroutineScope(
68-
context = parentCoroutineScope.coroutineContext,
69-
logger = logger,
70-
parent = parentCoroutineScope.coroutineContext[Job]
71-
)
65+
private val coroutineScope = parentCoroutineScope.createChildSupervisorScope(logger)
7266

7367
private sealed interface State<out T, out R : GetTokenResult> {
7468

firebase-dataconnect/src/main/kotlin/com/google/firebase/dataconnect/core/FirebaseDataConnectImpl.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -335,7 +335,6 @@ internal class FirebaseDataConnectImpl(
335335
operationName = operationName,
336336
variables = variables,
337337
parentCoroutineScope = coroutineScope,
338-
nonBlockingCoroutineDispatcher = nonBlockingDispatcher,
339338
grpcClient = grpcClient,
340339
registeredDataDeserializerFactory = registeredDataDeserializerFactory,
341340
secureRandom = secureRandom,

firebase-dataconnect/src/main/kotlin/com/google/firebase/dataconnect/querymgr/LiveQuery.kt

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import com.google.firebase.dataconnect.core.DataConnectGrpcClient.OperationResul
2323
import com.google.firebase.dataconnect.core.Logger
2424
import com.google.firebase.dataconnect.core.LoggerGlobals.Logger
2525
import com.google.firebase.dataconnect.core.LoggerGlobals.debug
26-
import com.google.firebase.dataconnect.util.CoroutineUtils.createSupervisorCoroutineScope
26+
import com.google.firebase.dataconnect.util.CoroutineUtils.createChildSupervisorScope
2727
import com.google.firebase.dataconnect.util.ImmutableByteArray
2828
import com.google.firebase.dataconnect.util.NullableReference
2929
import com.google.firebase.dataconnect.util.SequencedReference
@@ -33,7 +33,6 @@ import com.google.firebase.util.nextAlphanumericString
3333
import com.google.protobuf.Struct
3434
import java.util.concurrent.CopyOnWriteArrayList
3535
import kotlin.random.Random
36-
import kotlinx.coroutines.CoroutineDispatcher
3736
import kotlinx.coroutines.CoroutineScope
3837
import kotlinx.coroutines.Job
3938
import kotlinx.coroutines.async
@@ -50,7 +49,6 @@ internal class LiveQuery(
5049
private val operationName: String,
5150
private val variables: Struct,
5251
parentCoroutineScope: CoroutineScope,
53-
nonBlockingCoroutineDispatcher: CoroutineDispatcher,
5452
private val grpcClient: DataConnectGrpcClient,
5553
private val registeredDataDeserializerFactory: RegisteredDataDeserializerFactory,
5654
private val secureRandom: Random,
@@ -67,10 +65,8 @@ internal class LiveQuery(
6765
}
6866

6967
private val coroutineScope =
70-
createSupervisorCoroutineScope(
71-
nonBlockingCoroutineDispatcher,
68+
parentCoroutineScope.createChildSupervisorScope(
7269
logger,
73-
parent = parentCoroutineScope.coroutineContext[Job],
7470
coroutineName = "LiveQuery[${logger.nameWithId}]",
7571
)
7672

firebase-dataconnect/src/main/kotlin/com/google/firebase/dataconnect/util/CoroutineUtils.kt

Lines changed: 67 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,11 @@ import kotlinx.coroutines.SupervisorJob
2929
internal object CoroutineUtils {
3030

3131
/**
32-
* Creates and returns a new [CoroutineScope] with a [SupervisorJob], [CoroutineName], and
33-
* [CoroutineExceptionHandler].
32+
* Creates and returns a new [CoroutineScope] with newly-created [SupervisorJob], [CoroutineName],
33+
* and [CoroutineExceptionHandler] elements.
3434
*
35-
* The [CoroutineContext] of the returned [CoroutineScope] will be the given [context], with some
36-
* elements unconditionally replaced:
35+
* The [CoroutineContext] of the returned [CoroutineScope] will be the given [context], with the
36+
* following elements unconditionally replaced:
3737
*
3838
* * The [Job] element will be a newly-created [SupervisorJob] with the given [parent]; notably,
3939
* if the given [parent] is null then the parent of the [SupervisorJob] will _also_ be null.
@@ -43,6 +43,18 @@ internal object CoroutineUtils {
4343
* * The [CoroutineExceptionHandler] will be a newly-created instance that simply logs a warning
4444
* message to the given [Logger] and then drops the exception. This prevents any crashing
4545
* coroutines in the returned scope from propagating outside the scope.
46+
*
47+
* @param context The context elements for the returned [CoroutineScope]; note that the [Job],
48+
* [CoroutineName], and [CoroutineExceptionHandler] will be discarded and replaced, as documented
49+
* above.
50+
* @param logger The logger to use to log uncaught exceptions in the [CoroutineExceptionHandler]
51+
* element of the [CoroutineContext] of the returned [CoroutineScope]; also used to calculate the
52+
* [coroutineName] if the given value is null.
53+
* @param parent The parent for the [SupervisorJob] of the [CoroutineContext] of the returned
54+
* [CoroutineScope].
55+
* @param coroutineName The name to specify in the [CoroutineName] element of the
56+
* [CoroutineContext] of the returned [CoroutineScope]; if null, use the [Logger.nameWithId] value
57+
* of the given [logger].
4658
*/
4759
fun createSupervisorCoroutineScope(
4860
context: CoroutineContext = EmptyCoroutineContext,
@@ -60,4 +72,55 @@ internal object CoroutineUtils {
6072
}
6173
}
6274
)
75+
76+
/**
77+
* Creates and returns a new [CoroutineScope] with newly-created [SupervisorJob], [CoroutineName],
78+
* and [CoroutineExceptionHandler] elements, that is a "child" of the given [parentScope], and
79+
* uses the [CoroutineContext] of the given [parentScope] as its base context.
80+
*
81+
* The [SupervisorJob] of the returned [CoroutineScope] will have its parent set to the [Job] of
82+
* the [CoroutineContext] of the given [parentScope]. If the parent's [Job] is found to be null
83+
* then the parent of the [SupervisorJob] of the returned [CoroutineScope] will also be null.
84+
*
85+
* The [CoroutineContext] of the returned [CoroutineScope] will be that of the [parentScope] plus
86+
* the given [childContextOverrides], plus some elements unconditionally replaced as described in
87+
* [createSupervisorCoroutineScope].
88+
*
89+
* This is a convenience wrapper around [createSupervisorCoroutineScope] that automatically
90+
* extracts the job from the [parentScope] to establish structured concurrency.
91+
*
92+
* @param parentScope The parent scope from which to inherit the context and job.
93+
* @param logger Passed verbatim to [createSupervisorCoroutineScope].
94+
* @param childContextOverrides Additional context elements to add or override from the parent.
95+
* @param coroutineName Passed verbatim to [createSupervisorCoroutineScope].
96+
*/
97+
fun createChildSupervisorCoroutineScope(
98+
parentScope: CoroutineScope,
99+
logger: Logger,
100+
childContextOverrides: CoroutineContext = EmptyCoroutineContext,
101+
coroutineName: String? = null
102+
): CoroutineScope =
103+
createSupervisorCoroutineScope(
104+
context = parentScope.coroutineContext + childContextOverrides,
105+
logger = logger,
106+
parent = parentScope.coroutineContext[Job],
107+
coroutineName = coroutineName,
108+
)
109+
110+
/**
111+
* Convenience extension function that simply calls [createChildSupervisorCoroutineScope] with the
112+
* receiver [CoroutineScope] as the first argument.
113+
*
114+
* @receiver Passed to [createChildSupervisorCoroutineScope] as the `parentScope` parameter.
115+
* @param logger Passed to [createChildSupervisorCoroutineScope] as the `logger` parameter.
116+
* @param context Passed to [createChildSupervisorCoroutineScope] as the `childContextOverrides`
117+
* parameter.
118+
* @param coroutineName Passed to [createChildSupervisorCoroutineScope] as the `coroutineName`
119+
* parameter.
120+
*/
121+
fun CoroutineScope.createChildSupervisorScope(
122+
logger: Logger,
123+
context: CoroutineContext = EmptyCoroutineContext,
124+
coroutineName: String? = null
125+
): CoroutineScope = createChildSupervisorCoroutineScope(this, logger, context, coroutineName)
63126
}

0 commit comments

Comments
 (0)