From 632d2545d461cfeb9ae94d9a564964d77c26fc68 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Tue, 22 Dec 2020 21:39:43 +0600 Subject: [PATCH 1/4] start 0.4.15 --- CHANGELOG.md | 2 ++ gradle.properties | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b185780e18f..7d5148f046c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ # Changelog +## 0.4.15 + ## 0.4.14 * `Versions`: diff --git a/gradle.properties b/gradle.properties index 1f09ca9d7e0..59f4dad4376 100644 --- a/gradle.properties +++ b/gradle.properties @@ -40,5 +40,5 @@ dokka_version=1.4.20 # Project data group=dev.inmo -version=0.4.14 -android_code_version=18 +version=0.4.15 +android_code_version=19 From 9739bd871e4489bec8c9e346d9d9966ccb250e08 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Tue, 22 Dec 2020 22:45:36 +0600 Subject: [PATCH 2/4] unworking version --- .../micro_utils/coroutines/HandleSafely.kt | 55 +++++++++++++------ .../HandleSafelyCoroutineContextTest.kt | 32 +++++++++++ 2 files changed, 70 insertions(+), 17 deletions(-) create mode 100644 coroutines/src/jvmTest/kotlin/dev/inmo/micro_utils/coroutines/HandleSafelyCoroutineContextTest.kt diff --git a/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/HandleSafely.kt b/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/HandleSafely.kt index 327a902cdcf..506932d5e8f 100644 --- a/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/HandleSafely.kt +++ b/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/HandleSafely.kt @@ -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 = suspend (Throwable) -> T @@ -16,13 +16,24 @@ var defaultSafelyExceptionHandler: ExceptionHandler = { throw it } * Key for [SafelyExceptionHandler] which can be used in [CoroutineContext.get] to get current default * [SafelyExceptionHandler] */ -class SafelyExceptionHandlerKey : CoroutineContext.Key> +class SafelyExceptionHandlerKey() : CoroutineContext.Key> +private val nothingSafelyEceptionHandlerKey = SafelyExceptionHandlerKey() +private val unitSafelyEceptionHandlerKey = SafelyExceptionHandlerKey() +private val exceptionHandlersKeysCache = mutableMapOf<>() /** * Shortcut for creating instance of [SafelyExceptionHandlerKey] */ @Suppress("NOTHING_TO_INLINE") -inline fun safelyExceptionHandlerKey() = SafelyExceptionHandlerKey() +inline fun safelyExceptionHandlerKey() = SafelyExceptionHandlerKey(T::class) + +/** + * Shortcut for getting instance of [SafelyExceptionHandler] from current [coroutineContext] + */ +@Suppress("NOTHING_TO_INLINE") +suspend inline fun safelyExceptionHandler() = coroutineContext[safelyExceptionHandlerKey()] +@Suppress("NOTHING_TO_INLINE") +inline fun ExceptionHandler.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 safelyExceptionHandlerKey() = SafelyExceptionHandlerKey() * @see SafelyExceptionHandlerKey * @see ExceptionHandler */ -class SafelyExceptionHandler( - val handler: ExceptionHandler +class SafelyExceptionHandler( + val handler: ExceptionHandler, + returnKClass: KClass ) : CoroutineContext.Element { - - override val key: CoroutineContext.Key<*> = safelyExceptionHandlerKey() + override val key: CoroutineContext.Key<*> = SafelyExceptionHandlerKey(returnKClass) } /** @@ -56,18 +67,28 @@ class SafelyExceptionHandler( * @see SafelyExceptionHandler */ suspend inline fun safely( - noinline onException: ExceptionHandler = defaultSafelyExceptionHandler, - noinline block: suspend CoroutineScope.() -> T + onException: ExceptionHandler = defaultSafelyExceptionHandler, + block: suspend CoroutineScope.() -> T ): T { + val contextHandler = if (onException === defaultSafelyExceptionHandler) { + coroutineContext[nothingSafelyEceptionHandlerKey] ?: + safelyExceptionHandler() ?.let { unitHandler -> + val handler = unitHandler.handler + SafelyExceptionHandler { + handler(it) + onException(it) + } + } ?: + onException.safelyExceptionHandler() + } else { + onException.safelyExceptionHandler() + } return try { - supervisorScope(block) - } catch (e: Throwable) { - val handler = if (onException == defaultSafelyExceptionHandler) { - coroutineContext[safelyExceptionHandlerKey()] ?.handler ?: onException - } else { - onException + withContext(contextHandler) { + supervisorScope(block) } - handler(e) + } catch (e: Throwable) { + contextHandler.handler(e) } } diff --git a/coroutines/src/jvmTest/kotlin/dev/inmo/micro_utils/coroutines/HandleSafelyCoroutineContextTest.kt b/coroutines/src/jvmTest/kotlin/dev/inmo/micro_utils/coroutines/HandleSafelyCoroutineContextTest.kt new file mode 100644 index 00000000000..445ba383006 --- /dev/null +++ b/coroutines/src/jvmTest/kotlin/dev/inmo/micro_utils/coroutines/HandleSafelyCoroutineContextTest.kt @@ -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 = { + 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) + } +} \ No newline at end of file From d5a8d0f4d4897c31cef4357b34f7aca4b20b8226 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Tue, 22 Dec 2020 22:45:43 +0600 Subject: [PATCH 3/4] Revert "unworking version" This reverts commit 9739bd871e4489bec8c9e346d9d9966ccb250e08. --- .../micro_utils/coroutines/HandleSafely.kt | 55 ++++++------------- .../HandleSafelyCoroutineContextTest.kt | 32 ----------- 2 files changed, 17 insertions(+), 70 deletions(-) delete mode 100644 coroutines/src/jvmTest/kotlin/dev/inmo/micro_utils/coroutines/HandleSafelyCoroutineContextTest.kt diff --git a/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/HandleSafely.kt b/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/HandleSafely.kt index 506932d5e8f..327a902cdcf 100644 --- a/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/HandleSafely.kt +++ b/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/HandleSafely.kt @@ -1,9 +1,9 @@ package dev.inmo.micro_utils.coroutines -import kotlinx.coroutines.* +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.supervisorScope import kotlin.coroutines.CoroutineContext import kotlin.coroutines.coroutineContext -import kotlin.reflect.KClass typealias ExceptionHandler = suspend (Throwable) -> T @@ -16,24 +16,13 @@ var defaultSafelyExceptionHandler: ExceptionHandler = { throw it } * Key for [SafelyExceptionHandler] which can be used in [CoroutineContext.get] to get current default * [SafelyExceptionHandler] */ -class SafelyExceptionHandlerKey() : CoroutineContext.Key> -private val nothingSafelyEceptionHandlerKey = SafelyExceptionHandlerKey() -private val unitSafelyEceptionHandlerKey = SafelyExceptionHandlerKey() +class SafelyExceptionHandlerKey : CoroutineContext.Key> -private val exceptionHandlersKeysCache = mutableMapOf<>() /** * Shortcut for creating instance of [SafelyExceptionHandlerKey] */ @Suppress("NOTHING_TO_INLINE") -inline fun safelyExceptionHandlerKey() = SafelyExceptionHandlerKey(T::class) - -/** - * Shortcut for getting instance of [SafelyExceptionHandler] from current [coroutineContext] - */ -@Suppress("NOTHING_TO_INLINE") -suspend inline fun safelyExceptionHandler() = coroutineContext[safelyExceptionHandlerKey()] -@Suppress("NOTHING_TO_INLINE") -inline fun ExceptionHandler.safelyExceptionHandler() = SafelyExceptionHandler(this, T::class) +inline fun safelyExceptionHandlerKey() = SafelyExceptionHandlerKey() /** * Wrapper for [ExceptionHandler] which can be used in [CoroutineContext] to set local (for [CoroutineContext]) default @@ -42,11 +31,11 @@ inline fun ExceptionHandler.safelyExceptionHandler() = Safe * @see SafelyExceptionHandlerKey * @see ExceptionHandler */ -class SafelyExceptionHandler( - val handler: ExceptionHandler, - returnKClass: KClass +class SafelyExceptionHandler( + val handler: ExceptionHandler ) : CoroutineContext.Element { - override val key: CoroutineContext.Key<*> = SafelyExceptionHandlerKey(returnKClass) + + override val key: CoroutineContext.Key<*> = safelyExceptionHandlerKey() } /** @@ -67,28 +56,18 @@ class SafelyExceptionHandler( * @see SafelyExceptionHandler */ suspend inline fun safely( - onException: ExceptionHandler = defaultSafelyExceptionHandler, - block: suspend CoroutineScope.() -> T + noinline onException: ExceptionHandler = defaultSafelyExceptionHandler, + noinline block: suspend CoroutineScope.() -> T ): T { - val contextHandler = if (onException === defaultSafelyExceptionHandler) { - coroutineContext[nothingSafelyEceptionHandlerKey] ?: - safelyExceptionHandler() ?.let { unitHandler -> - val handler = unitHandler.handler - SafelyExceptionHandler { - handler(it) - onException(it) - } - } ?: - onException.safelyExceptionHandler() - } else { - onException.safelyExceptionHandler() - } return try { - withContext(contextHandler) { - supervisorScope(block) - } + supervisorScope(block) } catch (e: Throwable) { - contextHandler.handler(e) + val handler = if (onException == defaultSafelyExceptionHandler) { + coroutineContext[safelyExceptionHandlerKey()] ?.handler ?: onException + } else { + onException + } + handler(e) } } diff --git a/coroutines/src/jvmTest/kotlin/dev/inmo/micro_utils/coroutines/HandleSafelyCoroutineContextTest.kt b/coroutines/src/jvmTest/kotlin/dev/inmo/micro_utils/coroutines/HandleSafelyCoroutineContextTest.kt deleted file mode 100644 index 445ba383006..00000000000 --- a/coroutines/src/jvmTest/kotlin/dev/inmo/micro_utils/coroutines/HandleSafelyCoroutineContextTest.kt +++ /dev/null @@ -1,32 +0,0 @@ -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 = { - 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) - } -} \ No newline at end of file From 6a89ffaa8a3a563557f70fc9f04d0772d9d44233 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Tue, 22 Dec 2020 23:17:28 +0600 Subject: [PATCH 4/4] rework of context exceptions --- CHANGELOG.md | 8 ++ .../micro_utils/coroutines/HandleSafely.kt | 73 ++++++++++++++++--- .../HandleSafelyCoroutineContextTest.kt | 38 ++++++++++ 3 files changed, 108 insertions(+), 11 deletions(-) create mode 100644 coroutines/src/jvmTest/kotlin/dev/inmo/micro_utils/coroutines/HandleSafelyCoroutineContextTest.kt diff --git a/CHANGELOG.md b/CHANGELOG.md index 7d5148f046c..dda2413ccba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,14 @@ ## 0.4.15 +* `Coroutines`: + * `safely`: + * `SafelyExceptionHandlerKey` has been deprecated + * `SafelyExceptionHandler` has been deprecated + * `ContextSafelyExceptionHandlerKey` has been added + * `ContextSafelyExceptionHandler` has been added + * `safelyWithContextExceptionHandler` has been added + ## 0.4.14 * `Versions`: diff --git a/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/HandleSafely.kt b/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/HandleSafely.kt index 327a902cdcf..423d4ad76e5 100644 --- a/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/HandleSafely.kt +++ b/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/HandleSafely.kt @@ -1,7 +1,6 @@ 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 @@ -16,12 +15,14 @@ var defaultSafelyExceptionHandler: ExceptionHandler = { throw it } * Key for [SafelyExceptionHandler] which can be used in [CoroutineContext.get] to get current default * [SafelyExceptionHandler] */ +@Deprecated("This method will be useless in future major update", ReplaceWith("ContextSafelyExceptionHandlerKey", "dev.inmo.micro_utils.coroutines.ContextSafelyExceptionHandler")) class SafelyExceptionHandlerKey : CoroutineContext.Key> /** * Shortcut for creating instance of [SafelyExceptionHandlerKey] */ @Suppress("NOTHING_TO_INLINE") +@Deprecated("This method will be useless in future major update", ReplaceWith("ContextSafelyExceptionHandlerKey", "dev.inmo.micro_utils.coroutines.ContextSafelyExceptionHandler")) inline fun safelyExceptionHandlerKey() = SafelyExceptionHandlerKey() /** @@ -31,13 +32,68 @@ inline fun safelyExceptionHandlerKey() = SafelyExceptionHandlerKey() * @see SafelyExceptionHandlerKey * @see ExceptionHandler */ +@Deprecated("This method will be useless in future major update", ReplaceWith("ContextSafelyExceptionHandler", "dev.inmo.micro_utils.coroutines.ContextSafelyExceptionHandler")) class SafelyExceptionHandler( val handler: ExceptionHandler ) : CoroutineContext.Element { - override val key: CoroutineContext.Key<*> = safelyExceptionHandlerKey() } +/** + * This key can (and will) be used to get [ContextSafelyExceptionHandler] from [coroutineContext] of suspend functions + * and in [ContextSafelyExceptionHandler] for defining of its [CoroutineContext.Element.key] + * + * @see safelyWithContextExceptionHandler + * @see ContextSafelyExceptionHandler + */ +object ContextSafelyExceptionHandlerKey : CoroutineContext.Key + +/** + * [ExceptionHandler] wrapper which was created to make possible to use [handler] across all coroutines calls + * + * @see safelyWithContextExceptionHandler + * @see ContextSafelyExceptionHandlerKey + */ +class ContextSafelyExceptionHandler( + val handler: ExceptionHandler +) : CoroutineContext.Element { + override val key: CoroutineContext.Key<*> + get() = ContextSafelyExceptionHandlerKey +} + +/** + * @return [ContextSafelyExceptionHandler] from [coroutineContext] by key [ContextSafelyExceptionHandlerKey] if + * exists + * + * @see ContextSafelyExceptionHandler + * @see ContextSafelyExceptionHandlerKey + */ +suspend inline fun contextSafelyExceptionHandler() = coroutineContext[ContextSafelyExceptionHandlerKey] + +/** + * This method will set new [coroutineContext] with [ContextSafelyExceptionHandler]. In case if [coroutineContext] + * already contains [ContextSafelyExceptionHandler], [ContextSafelyExceptionHandler.handler] will be used BEFORE + * [contextExceptionHandler] in case of exception. + * + * After all, will be called [withContext] method with created [ContextSafelyExceptionHandler] and block which will call + * [safely] method with [safelyExceptionHandler] as onException parameter and [block] as execution block + */ +suspend fun safelyWithContextExceptionHandler( + contextExceptionHandler: ExceptionHandler, + safelyExceptionHandler: ExceptionHandler = defaultSafelyExceptionHandler, + block: suspend CoroutineScope.() -> T +): T { + val contextSafelyExceptionHandler = contextSafelyExceptionHandler() ?.handler ?.let { oldHandler -> + ContextSafelyExceptionHandler { + oldHandler(it) + contextExceptionHandler(it) + } + } ?: ContextSafelyExceptionHandler(contextExceptionHandler) + return withContext(contextSafelyExceptionHandler) { + safely(safelyExceptionHandler, block) + } +} + /** * It will run [block] inside of [supervisorScope] to avoid problems with catching of exceptions * @@ -52,8 +108,7 @@ class SafelyExceptionHandler( * * @see defaultSafelyExceptionHandler * @see safelyWithoutExceptions - * @see SafelyExceptionHandlerKey - * @see SafelyExceptionHandler + * @see safelyWithContextExceptionHandler */ suspend inline fun safely( noinline onException: ExceptionHandler = defaultSafelyExceptionHandler, @@ -62,12 +117,8 @@ suspend inline fun safely( return try { supervisorScope(block) } catch (e: Throwable) { - val handler = if (onException == defaultSafelyExceptionHandler) { - coroutineContext[safelyExceptionHandlerKey()] ?.handler ?: onException - } else { - onException - } - handler(e) + coroutineContext[ContextSafelyExceptionHandlerKey] ?.handler ?.invoke(e) + onException(e) } } diff --git a/coroutines/src/jvmTest/kotlin/dev/inmo/micro_utils/coroutines/HandleSafelyCoroutineContextTest.kt b/coroutines/src/jvmTest/kotlin/dev/inmo/micro_utils/coroutines/HandleSafelyCoroutineContextTest.kt new file mode 100644 index 00000000000..3c209b67357 --- /dev/null +++ b/coroutines/src/jvmTest/kotlin/dev/inmo/micro_utils/coroutines/HandleSafelyCoroutineContextTest.kt @@ -0,0 +1,38 @@ +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 + var defaultHandlerHappen = false + defaultSafelyExceptionHandler = { + defaultHandlerHappen = true + throw it + } + val contextHandler: ExceptionHandler = { + contextHandlerHappen = true + } + val checkJob = scope.launch { + safelyWithContextExceptionHandler(contextHandler) { + safely( + { + localHandlerHappen = true + } + ) { + error("That must happen :)") + } + println(coroutineContext) + error("That must happen too:)") + } + } + launchSynchronously { checkJob.join() } + assert(contextHandlerHappen) + assert(localHandlerHappen) + assert(defaultHandlerHappen) + } +} \ No newline at end of file