@@ -48,7 +48,6 @@ import kotlinx.coroutines.flow.MutableStateFlow
4848import kotlinx.coroutines.flow.StateFlow
4949import kotlinx.coroutines.flow.asStateFlow
5050import kotlinx.coroutines.launch
51- import kotlinx.coroutines.yield
5251
5352/* * Base class that shares logic for managing the Auth token and AppCheck token. */
5453internal sealed class DataConnectCredentialsTokenManager <T : Any >(
@@ -86,49 +85,39 @@ internal sealed class DataConnectCredentialsTokenManager<T : Any>(
8685 }
8786 }
8887
89- private interface ProviderProvider <T > {
90- val provider: T ?
91- }
92-
9388 private sealed interface State <out T > {
9489
9590 /* * State indicating that [close] has been invoked. */
9691 object Closed : State<Nothing>
9792
98- /* * State indicating that there is no outstanding "get token" request. */
99- class Idle <T >(
100-
101- /* *
102- * The [InternalAuthProvider] or [InteropAppCheckTokenProvider]; may be null if the deferred
103- * has not yet given us a provider.
104- */
105- override val provider : T ? ,
106-
93+ sealed interface StateWithForceTokenRefresh <out T > : State <T > {
10794 /* * The value to specify for `forceRefresh` on the next invocation of [getToken]. */
10895 val forceTokenRefresh: Boolean
109- ) : State<T>, ProviderProvider<T>
96+ }
11097
111- /* * State indicating that there _is_ an outstanding "get token" request. */
112- class Active <T >(
98+ /* * State indicating that the token provider is not (yet?) available. */
99+ data class New (override val forceTokenRefresh : Boolean ) : StateWithForceTokenRefresh<Nothing>
100+
101+ sealed interface StateWithProvider <out T > : State <T > {
102+ /* * The token provider, [InternalAuthProvider] or [InteropAppCheckTokenProvider] */
103+ val provider: T
104+ }
105+
106+ /* * State indicating that there is no outstanding "get token" request. */
107+ data class Idle <T >(override val provider : T , override val forceTokenRefresh : Boolean ) :
108+ StateWithProvider <T >, StateWithForceTokenRefresh <T >
113109
114- /* *
115- * The [InternalAuthProvider] or [InteropAppCheckTokenProvider] that is performing the "get
116- * token" request.
117- */
110+ /* * State indicating that there _is_ an outstanding "get token" request. */
111+ data class Active <out T >(
118112 override val provider : T ,
119113
120114 /* * The job that is performing the "get token" request. */
121115 val job : Deferred <SequencedReference <Result <GetTokenResult >>>
122- ) : State<T>, ProviderProvider <T>
116+ ) : StateWithProvider <T>
123117 }
124118
125- /* *
126- * The current state of this object. The value should only be changed in a compare-and-swap loop
127- * in order to be thread-safe. Such a loop should call `yield()` on each iteration to allow other
128- * coroutines to run on the thread.
129- */
130- private val state =
131- MutableStateFlow <State <T >>(State .Idle (provider = null , forceTokenRefresh = false ))
119+ /* * The current state of this object. */
120+ private val state = MutableStateFlow <State <T >>(State .New (forceTokenRefresh = false ))
132121
133122 /* *
134123 * Adds the token listener to the given provider.
@@ -171,15 +160,16 @@ internal sealed class DataConnectCredentialsTokenManager<T : Any>(
171160 private fun setClosedState () {
172161 while (true ) {
173162 val oldState = state.value
174- val providerProvider : ProviderProvider < T > =
163+ val provider : T ? =
175164 when (oldState) {
176165 is State .Closed -> return
177- is State .Idle -> oldState
178- is State .Active -> oldState
166+ is State .New -> null
167+ is State .Idle -> oldState.provider
168+ is State .Active -> oldState.provider
179169 }
180170
181171 if (state.compareAndSet(oldState, State .Closed )) {
182- providerProvider. provider?.let { removeTokenListener(it) }
172+ provider?.let { removeTokenListener(it) }
183173 break
184174 }
185175 }
@@ -190,27 +180,28 @@ internal sealed class DataConnectCredentialsTokenManager<T : Any>(
190180 *
191181 * If [close] has been called, this method does nothing.
192182 */
193- suspend fun forceRefresh () {
183+ fun forceRefresh () {
194184 logger.debug { " forceRefresh()" }
195185 while (true ) {
196186 val oldState = state.value
197- val oldStateProviderProvider =
187+ val newState : State . StateWithForceTokenRefresh < T > =
198188 when (oldState) {
199189 is State .Closed -> return
200- is State .Idle -> oldState
190+ is State .New -> oldState.copy(forceTokenRefresh = true )
191+ is State .Idle -> oldState.copy(forceTokenRefresh = true )
201192 is State .Active -> {
202193 val message = " needs token refresh (wgrwbrvjxt)"
203194 oldState.job.cancel(message, ForceRefresh (message))
204- oldState
195+ State . Idle ( oldState.provider, forceTokenRefresh = true )
205196 }
206197 }
207198
208- val newState = State .Idle (oldStateProviderProvider.provider, forceTokenRefresh = true )
199+ check(newState.forceTokenRefresh) {
200+ " newState.forceTokenRefresh should be true (error code gnvr2wx7nz)"
201+ }
209202 if (state.compareAndSet(oldState, newState)) {
210203 break
211204 }
212-
213- yield ()
214205 }
215206 }
216207
@@ -256,13 +247,13 @@ internal sealed class DataConnectCredentialsTokenManager<T : Any>(
256247 }
257248 throw CredentialsTokenManagerClosedException (this )
258249 }
259- is State .Idle -> {
260- if (oldState.provider == = null ) {
261- logger.debug {
262- " $invocationId getToken() returns null (token provider is not (yet?) available)"
263- }
264- return null
250+ is State .New -> {
251+ logger.debug {
252+ " $invocationId getToken() returns null (token provider is not (yet?) available)"
265253 }
254+ return null
255+ }
256+ is State .Idle -> {
266257 newActiveState(invocationId, oldState.provider, oldState.forceTokenRefresh)
267258 }
268259 is State .Active -> {
@@ -352,6 +343,7 @@ internal sealed class DataConnectCredentialsTokenManager<T : Any>(
352343 removeTokenListener(newProvider)
353344 break
354345 }
346+ is State .New -> State .Idle (newProvider, oldState.forceTokenRefresh)
355347 is State .Idle -> State .Idle (newProvider, oldState.forceTokenRefresh)
356348 is State .Active -> {
357349 val newProviderClassName = newProvider::class .qualifiedName
0 commit comments