when I wrap an object that holds resources and req...
# coroutines
l
when I wrap an object that holds resources and requires a callback through a
suspendCancellableCoroutine
, do I need to cleanup the resources inside the error callback?
Copy code
suspend fun isSessionValid(): Boolean = suspendCancellableCoroutine { continuation ->
    lateinit var auth: Auth

    val callback = object : AuthHandler {
        override fun onSuccess(session: AuthUserSession) {
            continuation.resume(session.isValid)
            auth.release()
        }

        override fun onFailure(e: Exception) {
            continuation.resumeWithException(e)
        }
    }

    auth = builder.setAuthHandler(callback).build()
    auth.getSession()
    continuation.invokeOnCancellation { auth.release() }
}
d
Short answer is yes. Long answer is I need to see more code.
👍 1
l
I can share more, would the whole function be enough? not sure what else could be valuable
d
The whole function I'd say.
l
Cool! I edited the initial post
d
Wow, what an awkward api. Is this like Google Play services thing or...? (I'm looking for docs to confirm something)
l
Amazon cognito 🙈
d
Copy code
suspend fun isSessionValid(): Boolean {
    val latch = CompletableDeferred<AuthUserSession>()
    val callback = object : AuthHandler {
        override fun onSuccess(session: AuthUserSession) {
            latch.complete(session)
        }

        override fun onFailure(e: Exception) {
            latch.completeExceptionally(e)
        }
    }

    val auth = builder.setAuthHandler(callback).build()
    try {
        auth.getSession()
        val session = latch.await()
        return session.isValid
    } finally {
        auth.release()
    }
}
l
interesting, TIL about
CompletableDeferred
! It does seem to do the job, like before, but in a better ergonomics. Is there something that I need to be aware of?
d
Not that I can think of.
👍 1
l
Thank you, that’s super helpful 🙏
m
@Dominaezzz is your implementation better? why prefer it over
suspendCancellableCoroutine
?
d
Mostly to avoid calling auth.release() three times and the lateinit. I also believe the contents of suspend*Coroutine should be brief/simple, this didn't seem to fit that.
m
Copy code
suspend fun isSessionValid(): Boolean = suspendCancellableCoroutine { continuation ->
    val callback = object : AuthHandler {
        override fun onSuccess(session: AuthUserSession) {
            continuation.resume(session.isValid)
        }
        override fun onFailure(e: Exception) {
            continuation.resumeWithException(e)
        }
    }
    val auth = builder.setAuthHandler(callback).build()
    auth.getSession()
    continuation.invokeOnCompletion { auth.release() }
}
Wouldn't that work?
m
Maybe because it's experimental. Either way you can retrieve the
Job
instance and effectively achieve the same thing AFAIK.
Copy code
coroutineContext.getValue(Job).invokeOnCompletion { auth.release() }