withTimeout not working when calling channel.receive

I’m building an app using Kotlin JS, and the way the Firebase SDK handles remembering someone’s login state is kind of weird. You have to setup an onAuthStateChanged callback and handle the persistent user info from there. I set that user info to be written to a channel. Client code will then call getBackendAPIAccessToken. If the user is logged in eventually that function will return the API token. If not it will timeout while waiting for the User info in the channel. My problem is that the timeout in the getUser function is not working. It just hangs indefinitely. Any idea why that might be happening? Here’s a minimum reproducible example.

package app.frontend.common.clients import arrow.core.Either import arrow.core.raise.either import dev.gitlive.firebase.Firebase import dev.gitlive.firebase.FirebaseOptions import dev.gitlive.firebase.auth.auth import dev.gitlive.firebase.firebase import dev.gitlive.firebase.initialize import app.frontend.common.configs.FirebaseConfigs import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.await import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.launch import kotlinx.coroutines.withTimeout class AppClientError : Throwable() typealias AppEither<A> = Either<AppClientError, A> value class BackendAPIAccessToken(val value: String) class AuthenticationClient { private val waitForTokenTimeoutInMillis: Long = 100L private val userChannel: Channel<firebase.user.User> = Channel(capacity = 1) private val firebaseOptions: FirebaseOptions = FirebaseOptions(...) init { Firebase.initialize(options = firebaseOptions) Firebase.auth.js.onAuthStateChanged { user -> if (user != null) { GlobalScope.launch { userChannel.send(user) } } } } class UnauthenticatedException : AppClientError() private suspend fun <A> runBlockWithTimeoutAndCatch(block: suspend () -> A): AppEither<A> = Either.catch { withTimeout(waitForTokenTimeoutInMillis) { block() } } .mapLeft { UnauthenticatedException() } private suspend fun getUser(): AppEither<firebase.user.User> { return runBlockWithTimeoutAndCatch { userChannel.receive() } } suspend fun getBackendAPIAccessToken(): AppEither<BackendAPIAccessToken> { return either { getUser() .map { user -> val idToken = runBlockWithTimeoutAndCatch { user .getIdToken(false) .await() }.bind() BackendAPIAccessToken(idToken) } .bind() } } } fun main() { GlobalScope.launch { AuthenticationClient() .getBackendAPIAccessToken() .onLeft { println("Not logged in") } .onRight { println("Logged in") } } } 

submitted by /u/spline_reticulator
[link] [comments]