Skip to content

Instantly share code, notes, and snippets.

View orcchg's full-sized avatar
🏠
Working from home

Maxim Alov orcchg

🏠
Working from home
View GitHub Profile
@orcchg
orcchg / suspend_next_coroutines.kt
Created October 21, 2023 18:22
Next coroutine suspends at the very beginning
suspend fun <T> synchronized(block: suspend () -> T): T {
val thisJobId = id.getAndIncrement()
while (isInsideCriticalSection) {
suspendCoroutine { cont -> // <-- suspending call
suspendedJobs[thisJobId] = cont
}
suspendedJobs.remove(thisJobId)
}
...
}
@orcchg
orcchg / obtain_and_refresh_token_in_a_single_thread.kt
Created October 21, 2023 18:04
Run obtain and refresh token login in a single thread
fun obtainAccessToken(): String {
val kCriticalSection = DI.get<KCriticalSection>(qualifier = "shared")
val singleThread = DI.get<CoroutineDispatcher>(qualifier = "shared")
return runBlocking(singleThread) {
kCriticalSection.synchronized {
val accessToken = readAccessTokenFromStorageOrMemory() // might be a suspending call
if (accessToken != null && System.currentTimeMillis() < accessToken.createdAt + accessToken.expiresIn) {
return@synchronized accessToken.toString() // access token is valid
@orcchg
orcchg / check_token_exist_or_expired.kt
Created October 21, 2023 17:51
Check for token existence and expiration
if (accessToken != null && System.currentTimeMillis() < accessToken.createdAt + accessToken.expiresIn) {
return@synchronized accessToken.toString() // access token is valid
}
@orcchg
orcchg / shared_critical_section_for_coroutines.kt
Created October 21, 2023 10:47
Shared critical section for true synchronization
suspend fun obtainAccessToken(): String {
val kCriticalSection = DI.get<KCriticalSection>(qualifier = "shared")
return kCriticalSection.synchronized {
val accessToken = readAccessTokenFromStorageOrMemory() // might be a suspending call
if (accessToken != null && System.currentTimeMillis() < accessToken.createdAt + accessToken.expiresIn) {
return@synchronized accessToken.toString() // access token is valid
}
@orcchg
orcchg / critical_section_for_coroutines.kt
Last active October 21, 2023 17:29
Critical section for coroutines
import java.util.concurrent.ConcurrentHashMap
import kotlin.coroutines.Continuation
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine
class KCriticalSection {
private val suspendedJobs = ConcurrentHashMap<Int, Continuation<Boolean>>()
private var id = AtomicInteger(1000)
@Volatile private var isInsideCriticalSection = false
@orcchg
orcchg / get_and_refresh_token_in_a_critical_section.kt
Created October 21, 2023 10:12
Wrap token obtain and refresh logic in a critical section
suspend fun obtainAccessToken(): String =
KCriticalSection().synchronized {
val accessToken = readAccessTokenFromStorageOrMemory() // might be a suspending call
if (accessToken != null && System.currentTimeMillis() < accessToken.createdAt + accessToken.expiresIn) {
return@synchronized accessToken.toString() // access token is valid
}
val refreshToken = readRefreshTokenFromSecureStorage() // normally a suspending call
@orcchg
orcchg / token_refresh_logic.kt
Last active October 21, 2023 10:05
Check existing access token for expiration and refresh if need
suspend fun obtainAccessToken(): String {
val accessToken = readAccessTokenFromStorageOrMemory() // might be a suspending call
if (accessToken != null && System.currentTimeMillis() < accessToken.createdAt + accessToken.expiresIn) {
return accessToken.toString() // access token is valid
}
val refreshToken = readRefreshTokenFromSecureStorage() // normally a suspending call
// normally suspending as it makes another API call
@orcchg
orcchg / obtain_access_token.kt
Created October 21, 2023 08:56
Obtaining access token is normally an asynchronous operation
suspend fun obtainAccessToken(): String {
// do the real job to obtain access token
return "access token"
}
@orcchg
orcchg / auth_interceptor_with_async_access_token.kt
Created October 21, 2023 08:51
Interceptor with Authorization header and asynchronously obtained access token
import kotlinx.coroutines.runBlocking
import okhttp3.Interceptor
import okhttp3.Response
class AuthInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val accessToken = runBlocking { obtainAccessToken() }
val request = chain.request()
val newRequest = request.newBuilder()
.addHeader("Authorization", accessToken)
@orcchg
orcchg / authorization_interceptor.kt
Created October 21, 2023 08:41
Interceptor with Authorization header
import okhttp3.Interceptor
import okhttp3.Response
class AuthInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val accessToken = obtainAccessToken() // <-- how to get token? This might be a suspending call
val request = chain.request()
val newRequest = request.newBuilder()
.addHeader("Authorization", accessToken)