unworking version

This commit is contained in:
InsanusMokrassar 2020-12-22 22:45:36 +06:00
parent 632d2545d4
commit 9739bd871e
2 changed files with 70 additions and 17 deletions

View File

@ -1,9 +1,9 @@
package dev.inmo.micro_utils.coroutines
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.supervisorScope
import kotlinx.coroutines.*
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.coroutineContext
import kotlin.reflect.KClass
typealias ExceptionHandler<T> = suspend (Throwable) -> T
@ -16,13 +16,24 @@ var defaultSafelyExceptionHandler: ExceptionHandler<Nothing> = { throw it }
* Key for [SafelyExceptionHandler] which can be used in [CoroutineContext.get] to get current default
* [SafelyExceptionHandler]
*/
class SafelyExceptionHandlerKey<T> : CoroutineContext.Key<SafelyExceptionHandler<T>>
class SafelyExceptionHandlerKey<T>() : CoroutineContext.Key<SafelyExceptionHandler<T>>
private val nothingSafelyEceptionHandlerKey = SafelyExceptionHandlerKey<Nothing>()
private val unitSafelyEceptionHandlerKey = SafelyExceptionHandlerKey<Unit>()
private val exceptionHandlersKeysCache = mutableMapOf<>()
/**
* Shortcut for creating instance of [SafelyExceptionHandlerKey]
*/
@Suppress("NOTHING_TO_INLINE")
inline fun <T> safelyExceptionHandlerKey() = SafelyExceptionHandlerKey<T>()
inline fun <T> safelyExceptionHandlerKey() = SafelyExceptionHandlerKey(T::class)
/**
* Shortcut for getting instance of [SafelyExceptionHandler] from current [coroutineContext]
*/
@Suppress("NOTHING_TO_INLINE")
suspend inline fun <reified T : Any> safelyExceptionHandler() = coroutineContext[safelyExceptionHandlerKey<T>()]
@Suppress("NOTHING_TO_INLINE")
inline fun <reified T : Any> ExceptionHandler<T>.safelyExceptionHandler() = SafelyExceptionHandler(this, T::class)
/**
* Wrapper for [ExceptionHandler] which can be used in [CoroutineContext] to set local (for [CoroutineContext]) default
@ -31,11 +42,11 @@ inline fun <T> safelyExceptionHandlerKey() = SafelyExceptionHandlerKey<T>()
* @see SafelyExceptionHandlerKey
* @see ExceptionHandler
*/
class SafelyExceptionHandler<T>(
val handler: ExceptionHandler<T>
class SafelyExceptionHandler<T : Any>(
val handler: ExceptionHandler<T>,
returnKClass: KClass<T>
) : CoroutineContext.Element {
override val key: CoroutineContext.Key<*> = safelyExceptionHandlerKey<T>()
override val key: CoroutineContext.Key<*> = SafelyExceptionHandlerKey(returnKClass)
}
/**
@ -56,18 +67,28 @@ class SafelyExceptionHandler<T>(
* @see SafelyExceptionHandler
*/
suspend inline fun <T> safely(
noinline onException: ExceptionHandler<T> = defaultSafelyExceptionHandler,
noinline block: suspend CoroutineScope.() -> T
onException: ExceptionHandler<T> = defaultSafelyExceptionHandler,
block: suspend CoroutineScope.() -> T
): T {
val contextHandler = if (onException === defaultSafelyExceptionHandler) {
coroutineContext[nothingSafelyEceptionHandlerKey] ?:
safelyExceptionHandler<Unit>() ?.let { unitHandler ->
val handler = unitHandler.handler
SafelyExceptionHandler<T> {
handler(it)
onException(it)
}
} ?:
onException.safelyExceptionHandler()
} else {
onException.safelyExceptionHandler()
}
return try {
supervisorScope(block)
} catch (e: Throwable) {
val handler = if (onException == defaultSafelyExceptionHandler) {
coroutineContext[safelyExceptionHandlerKey<T>()] ?.handler ?: onException
} else {
onException
withContext(contextHandler) {
supervisorScope(block)
}
handler(e)
} catch (e: Throwable) {
contextHandler.handler(e)
}
}

View File

@ -0,0 +1,32 @@
package dev.inmo.micro_utils.coroutines
import kotlinx.coroutines.*
import kotlin.test.Test
class HandleSafelyCoroutineContextTest {
@Test
fun testHandleSafelyCoroutineContext() {
val scope = CoroutineScope(Dispatchers.Default)
var contextHandlerHappen = false
var localHandlerHappen = false
val contextHandler: ExceptionHandler<Unit> = {
contextHandlerHappen = true
}
val checkJob = scope.launch(Dispatchers.Default + contextHandler.safelyExceptionHandler()) {
safely {
safely(
{
localHandlerHappen = true
}
) {
error("That must happen :)")
}
println(coroutineContext)
error("That must happen too:)")
}
}
launchSynchronously { checkJob.join() }
assert(contextHandlerHappen)
assert(localHandlerHappen)
}
}