From 0398a7bebd36d1d7850f2882138ea4fc58ce6ce4 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Fri, 14 Jun 2024 16:36:35 +0600 Subject: [PATCH 1/4] start 0.20.52 --- CHANGELOG.md | 2 ++ gradle.properties | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bb99a5d0e5a..51ed2d053e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ # Changelog +## 0.20.52 + ## 0.20.51 * `Versions`: diff --git a/gradle.properties b/gradle.properties index 3ba75f5b0ec..c2138181643 100644 --- a/gradle.properties +++ b/gradle.properties @@ -15,5 +15,5 @@ crypto_js_version=4.1.1 # Project data group=dev.inmo -version=0.20.51 -android_code_version=257 +version=0.20.52 +android_code_version=258 From f373524f345e48dcf8eaba4103a6b5a81b701f56 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Fri, 14 Jun 2024 16:53:56 +0600 Subject: [PATCH 2/4] a little improve of weak extensions and add tests for weak scopes --- .../inmo/micro_utils/coroutines/WeakJob.kt | 24 ++++++++++--- .../src/commonTest/kotlin/WeakJobTests.kt | 36 +++++++++++++++++++ 2 files changed, 56 insertions(+), 4 deletions(-) create mode 100644 coroutines/src/commonTest/kotlin/WeakJobTests.kt diff --git a/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/WeakJob.kt b/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/WeakJob.kt index c020a359ecb..0d004efc65a 100644 --- a/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/WeakJob.kt +++ b/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/WeakJob.kt @@ -4,11 +4,14 @@ import kotlinx.coroutines.* import kotlin.coroutines.CoroutineContext import kotlin.coroutines.EmptyCoroutineContext -private fun CoroutineScope.createWeakSubScope() = CoroutineScope(coroutineContext.minusKey(Job)).also { newScope -> - coroutineContext.job.invokeOnCompletion { newScope.cancel() } +private fun CoroutineScope.createWeakSubScope() = CoroutineScope(coroutineContext.minusKey(Job) + Job()).also { newScope -> + newScope.launch { + this@createWeakSubScope.coroutineContext.job.join() + newScope.cancel() + } } -fun CoroutineScope.weakLaunch( +fun CoroutineScope.launchWeak( context: CoroutineContext = EmptyCoroutineContext, start: CoroutineStart = CoroutineStart.DEFAULT, block: suspend CoroutineScope.() -> Unit @@ -19,7 +22,7 @@ fun CoroutineScope.weakLaunch( return job } -fun CoroutineScope.weakAsync( +fun CoroutineScope.asyncWeak( context: CoroutineContext = EmptyCoroutineContext, start: CoroutineStart = CoroutineStart.DEFAULT, block: suspend CoroutineScope.() -> T @@ -29,3 +32,16 @@ fun CoroutineScope.weakAsync( deferred.invokeOnCompletion { scope.cancel() } return deferred } +@Deprecated("Renamed", ReplaceWith("launchWeak(context, start, block)", "dev.inmo.micro_utils.coroutines.launchWeak")) +fun CoroutineScope.weakLaunch( + context: CoroutineContext = EmptyCoroutineContext, + start: CoroutineStart = CoroutineStart.DEFAULT, + block: suspend CoroutineScope.() -> Unit +): Job = launchWeak(context, start, block) + +@Deprecated("Renamed", ReplaceWith("asyncWeak(context, start, block)", "dev.inmo.micro_utils.coroutines.asyncWeak")) +fun CoroutineScope.weakAsync( + context: CoroutineContext = EmptyCoroutineContext, + start: CoroutineStart = CoroutineStart.DEFAULT, + block: suspend CoroutineScope.() -> T +): Deferred = asyncWeak(context, start, block) diff --git a/coroutines/src/commonTest/kotlin/WeakJobTests.kt b/coroutines/src/commonTest/kotlin/WeakJobTests.kt new file mode 100644 index 00000000000..69a0ffb463e --- /dev/null +++ b/coroutines/src/commonTest/kotlin/WeakJobTests.kt @@ -0,0 +1,36 @@ +import dev.inmo.micro_utils.coroutines.asyncWeak +import dev.inmo.micro_utils.coroutines.launchWeak +import kotlinx.coroutines.CancellationException +import kotlinx.coroutines.coroutineScope +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch +import kotlinx.coroutines.test.runTest +import kotlin.test.Test +import kotlin.test.assertTrue + +class WeakJobTests { + @Test + fun testWeakJob() = runTest { + var commonJobDone = false + var weakJobStarted = false + try { + coroutineScope { + launch { + delay(1000) + commonJobDone = true + } + asyncWeak { + weakJobStarted = true + delay(100500L) + error("This must never happen") + } + }.await() + } catch (error: Throwable) { + assertTrue(error is CancellationException) + assertTrue(commonJobDone) + assertTrue(weakJobStarted) + return@runTest + } + error("Cancellation exception has not been thrown") + } +} \ No newline at end of file From 4d55ec6f36a262bda9370897a6224ee9404d3dfc Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Fri, 14 Jun 2024 16:58:40 +0600 Subject: [PATCH 3/4] add separated WeakScope --- .../dev/inmo/micro_utils/coroutines/WeakJob.kt | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/WeakJob.kt b/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/WeakJob.kt index 0d004efc65a..48a49f295db 100644 --- a/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/WeakJob.kt +++ b/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/WeakJob.kt @@ -4,19 +4,25 @@ import kotlinx.coroutines.* import kotlin.coroutines.CoroutineContext import kotlin.coroutines.EmptyCoroutineContext -private fun CoroutineScope.createWeakSubScope() = CoroutineScope(coroutineContext.minusKey(Job) + Job()).also { newScope -> +fun WeakScope( + context: CoroutineContext +) = CoroutineScope(context.minusKey(Job) + Job()).also { newScope -> newScope.launch { - this@createWeakSubScope.coroutineContext.job.join() + context.job.join() newScope.cancel() } } +fun WeakScope( + scope: CoroutineScope +) = WeakScope(scope.coroutineContext) + fun CoroutineScope.launchWeak( context: CoroutineContext = EmptyCoroutineContext, start: CoroutineStart = CoroutineStart.DEFAULT, block: suspend CoroutineScope.() -> Unit ): Job { - val scope = createWeakSubScope() + val scope = WeakScope(this) val job = scope.launch(context, start, block) job.invokeOnCompletion { scope.cancel() } return job @@ -27,7 +33,7 @@ fun CoroutineScope.asyncWeak( start: CoroutineStart = CoroutineStart.DEFAULT, block: suspend CoroutineScope.() -> T ): Deferred { - val scope = createWeakSubScope() + val scope = WeakScope(this) val deferred = scope.async(context, start, block) deferred.invokeOnCompletion { scope.cancel() } return deferred From 09bb90604df2c1f2709f576046e52280673dd30c Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Fri, 14 Jun 2024 17:09:04 +0600 Subject: [PATCH 4/4] weaks rework --- CHANGELOG.md | 3 ++ .../inmo/micro_utils/coroutines/WeakJob.kt | 21 ++++++++++ .../src/commonTest/kotlin/WeakJobTests.kt | 36 +++++++++++++++-- .../inmo/micro_utils/coroutines/WeakJob.kt | 40 ------------------- 4 files changed, 56 insertions(+), 44 deletions(-) delete mode 100644 coroutines/src/jvmTest/kotlin/dev/inmo/micro_utils/coroutines/WeakJob.kt diff --git a/CHANGELOG.md b/CHANGELOG.md index 51ed2d053e1..3db6aa60047 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ ## 0.20.52 +* `Coroutines`: + * Small rework of weak jobs: add `WeakScope` factory, rename old weal extensions and add kdocs + ## 0.20.51 * `Versions`: diff --git a/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/WeakJob.kt b/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/WeakJob.kt index 48a49f295db..a00840d8f37 100644 --- a/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/WeakJob.kt +++ b/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/WeakJob.kt @@ -4,6 +4,12 @@ import kotlinx.coroutines.* import kotlin.coroutines.CoroutineContext import kotlin.coroutines.EmptyCoroutineContext +/** + * Created [CoroutineScope] which will [launch] listening of [context] job completing and drop itself. Current weak + * scope **will not** be attached to [context] directly. So, this [CoroutineScope] will not prevent parent one from + * cancelling if it is launched with [supervisorScope] or [coroutineScope], but still will follow closing status + * of parent [Job] + */ fun WeakScope( context: CoroutineContext ) = CoroutineScope(context.minusKey(Job) + Job()).also { newScope -> @@ -13,10 +19,20 @@ fun WeakScope( } } +/** + * Created [CoroutineScope] which will [launch] listening of [scope] [CoroutineContext] job completing and drop itself. Current weak + * scope **will not** be attached to [scope] [CoroutineContext] directly. So, this [CoroutineScope] will not prevent parent one from + * cancelling if it is launched with [supervisorScope] or [coroutineScope], but still will follow closing status + * of parent [Job] + */ fun WeakScope( scope: CoroutineScope ) = WeakScope(scope.coroutineContext) +/** + * [this] [CoroutineScope] will be used as base for [WeakScope]. Other parameters ([context], [start], [block]) + * will be used to [launch] [Job] + */ fun CoroutineScope.launchWeak( context: CoroutineContext = EmptyCoroutineContext, start: CoroutineStart = CoroutineStart.DEFAULT, @@ -28,6 +44,10 @@ fun CoroutineScope.launchWeak( return job } +/** + * [this] [CoroutineScope] will be used as base for [WeakScope]. Other parameters ([context], [start], [block]) + * will be used to create [async] [Deferred] + */ fun CoroutineScope.asyncWeak( context: CoroutineContext = EmptyCoroutineContext, start: CoroutineStart = CoroutineStart.DEFAULT, @@ -38,6 +58,7 @@ fun CoroutineScope.asyncWeak( deferred.invokeOnCompletion { scope.cancel() } return deferred } + @Deprecated("Renamed", ReplaceWith("launchWeak(context, start, block)", "dev.inmo.micro_utils.coroutines.launchWeak")) fun CoroutineScope.weakLaunch( context: CoroutineContext = EmptyCoroutineContext, diff --git a/coroutines/src/commonTest/kotlin/WeakJobTests.kt b/coroutines/src/commonTest/kotlin/WeakJobTests.kt index 69a0ffb463e..0a973465896 100644 --- a/coroutines/src/commonTest/kotlin/WeakJobTests.kt +++ b/coroutines/src/commonTest/kotlin/WeakJobTests.kt @@ -1,9 +1,6 @@ import dev.inmo.micro_utils.coroutines.asyncWeak import dev.inmo.micro_utils.coroutines.launchWeak -import kotlinx.coroutines.CancellationException -import kotlinx.coroutines.coroutineScope -import kotlinx.coroutines.delay -import kotlinx.coroutines.launch +import kotlinx.coroutines.* import kotlinx.coroutines.test.runTest import kotlin.test.Test import kotlin.test.assertTrue @@ -33,4 +30,35 @@ class WeakJobTests { } error("Cancellation exception has not been thrown") } + @Test + fun testThatWeakJobsWorksCorrectly() = runTest { + val scope = CoroutineScope(Dispatchers.Default) + lateinit var weakLaunchJob: Job + lateinit var weakAsyncJob: Job + val completeDeferred = Job() + coroutineScope { + weakLaunchJob = launchWeak { + while (isActive) { + delay(100L) + } + } + weakAsyncJob = asyncWeak { + while (isActive) { + delay(100L) + } + } + + coroutineContext.job.invokeOnCompletion { + scope.launch { + delay(1000L) + completeDeferred.complete() + } + } + launch { delay(1000L); cancel() } + } + completeDeferred.join() + + assertTrue(!weakLaunchJob.isActive) + assertTrue(!weakAsyncJob.isActive) + } } \ No newline at end of file diff --git a/coroutines/src/jvmTest/kotlin/dev/inmo/micro_utils/coroutines/WeakJob.kt b/coroutines/src/jvmTest/kotlin/dev/inmo/micro_utils/coroutines/WeakJob.kt deleted file mode 100644 index 311b3b05b3b..00000000000 --- a/coroutines/src/jvmTest/kotlin/dev/inmo/micro_utils/coroutines/WeakJob.kt +++ /dev/null @@ -1,40 +0,0 @@ -package dev.inmo.micro_utils.coroutines - -import kotlinx.coroutines.* -import org.junit.Test - -class WeakJob { - @Test - fun `test that weak jobs works correctly`() { - val scope = CoroutineScope(Dispatchers.Default) - lateinit var weakLaunchJob: Job - lateinit var weakAsyncJob: Job - scope.launchSynchronously { - val completeDeferred = Job() - coroutineScope { - weakLaunchJob = weakLaunch { - while (isActive) { - delay(100L) - } - } - weakAsyncJob = weakAsync { - while (isActive) { - delay(100L) - } - } - - coroutineContext.job.invokeOnCompletion { - scope.launch { - delay(1000L) - completeDeferred.complete() - } - } - launch { delay(1000L); cancel() } - } - completeDeferred.join() - } - - assert(!weakLaunchJob.isActive) - assert(!weakAsyncJob.isActive) - } -}