From 539b5fbcd8411507b43d031e608a27d4e44656f8 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Sat, 25 Sep 2021 18:51:51 +0600 Subject: [PATCH 1/5] 0.6.5 --- CHANGELOG.md | 2 ++ gradle.properties | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d54460c..925afe3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ # Changelog +## 0.6.5 + ## 0.6.4 * Versions diff --git a/gradle.properties b/gradle.properties index 4637242..9f6a6be 100644 --- a/gradle.properties +++ b/gradle.properties @@ -33,5 +33,5 @@ androidx_work_version=2.6.0 ## Common -version=0.6.4 -android_code_version=8 +version=0.6.5 +android_code_version=9 From 4dc65bf09a46d53867cb3c22c47611b3e2301ad1 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Sat, 25 Sep 2021 19:28:49 +0600 Subject: [PATCH 2/5] delay in doWhile and flows with doWhile instead their own logic --- CHANGELOG.md | 3 + .../kotlin/dev/inmo/krontab/Executes.kt | 59 +++++++++++++++++-- .../dev/inmo/krontab/utils/SchedulerFlow.kt | 46 +++++++++------ 3 files changed, 87 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 925afe3..4ff747d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ ## 0.6.5 +* Flows now use `doWhile` functions +* `doWhile` now use additional delay (for 1 ms) for cases when `block` executing too fast + ## 0.6.4 * Versions diff --git a/src/commonMain/kotlin/dev/inmo/krontab/Executes.kt b/src/commonMain/kotlin/dev/inmo/krontab/Executes.kt index f3077a9..e15eebf 100644 --- a/src/commonMain/kotlin/dev/inmo/krontab/Executes.kt +++ b/src/commonMain/kotlin/dev/inmo/krontab/Executes.kt @@ -1,8 +1,23 @@ package dev.inmo.krontab import com.soywiz.klock.DateTime +import com.soywiz.klock.DateTimeTz import kotlinx.coroutines.delay +/** + * Execute [block] once at the [KronScheduler.next] time and return result of [block] calculation. + * + * WARNING!!! If you want to launch it in parallel, you must do this explicitly. + * + * WARNING!!! In case if [KronScheduler.next] of [this] instance will return null, [block] will be called immediately + */ +suspend inline fun KronSchedulerTz.doOnce(noinline block: suspend () -> T): T { + nextTimeZoned() ?.let { + delay((it - DateTimeTz.nowLocal()).millisecondsLong) + } + return block() +} + /** * Execute [block] once at the [KronScheduler.next] time and return result of [block] calculation. * @@ -24,13 +39,31 @@ suspend inline fun KronScheduler.doOnce(noinline block: suspend () -> T): T suspend inline fun doOnce( scheduleConfig: String, noinline block: suspend () -> T -) = buildSchedule(scheduleConfig).doOnce(block) +) = with(buildSchedule(scheduleConfig)) { + when (this) { + is KronSchedulerTz -> doOnce(block) + else -> doOnce(block) + } +} /** * Will execute [block] while it will return true as a result of its calculation */ suspend inline fun KronScheduler.doWhile(noinline block: suspend () -> Boolean) { - do { val doNext = doOnce(block) } while (doNext) + do { + delay(1L) + val doNext = doOnce(block) + } while (doNext) +} + +/** + * Will execute [block] while it will return true as a result of its calculation + */ +suspend inline fun KronSchedulerTz.doWhile(noinline block: suspend () -> Boolean) { + do { + delay(1L) + val doNext = doOnce(block) + } while (doNext) } /** @@ -41,7 +74,12 @@ suspend inline fun KronScheduler.doWhile(noinline block: suspend () -> Boolean) suspend inline fun doWhile( scheduleConfig: String, noinline block: suspend () -> Boolean -) = buildSchedule(scheduleConfig).doWhile(block) +) = with(buildSchedule(scheduleConfig)) { + when (this) { + is KronSchedulerTz -> doWhile(block) + else -> doWhile(block) + } +} /** * Will execute [block] without any checking of result @@ -50,6 +88,14 @@ suspend inline fun KronScheduler.doInfinity(noinline block: suspend () -> Unit) block() true } + +/** + * Will execute [block] without any checking of result + */ +suspend inline fun KronSchedulerTz.doInfinity(noinline block: suspend () -> Unit) = doWhile { + block() + true +} /** * Will [buildSchedule] using [scheduleConfig] and call [doInfinity] with [block] * @@ -58,4 +104,9 @@ suspend inline fun KronScheduler.doInfinity(noinline block: suspend () -> Unit) suspend inline fun doInfinity( scheduleConfig: String, noinline block: suspend () -> Unit -) = buildSchedule(scheduleConfig).doInfinity(block) +) = with(buildSchedule(scheduleConfig)) { + when (this) { + is KronSchedulerTz -> doInfinity(block) + else -> doInfinity(block) + } +} diff --git a/src/commonMain/kotlin/dev/inmo/krontab/utils/SchedulerFlow.kt b/src/commonMain/kotlin/dev/inmo/krontab/utils/SchedulerFlow.kt index 1382d86..b54e308 100644 --- a/src/commonMain/kotlin/dev/inmo/krontab/utils/SchedulerFlow.kt +++ b/src/commonMain/kotlin/dev/inmo/krontab/utils/SchedulerFlow.kt @@ -2,8 +2,7 @@ package dev.inmo.krontab.utils import com.soywiz.klock.DateTime import com.soywiz.klock.DateTimeTz -import dev.inmo.krontab.KronScheduler -import dev.inmo.krontab.next +import dev.inmo.krontab.* import kotlinx.coroutines.* import kotlinx.coroutines.flow.* @@ -12,30 +11,43 @@ import kotlinx.coroutines.flow.* * time zones * * @see channelFlow + * @see KronSchedulerTz.doWhile */ @FlowPreview -fun KronScheduler.asTzFlow(): Flow = channelFlow { - var previousTime = DateTime.nowLocal() - while (isActive) { - val now = DateTime.nowLocal() - val nextTime = next(now) ?: break - if (previousTime == nextTime) { - delay(1L) // skip 1ms - continue - } else { - previousTime = nextTime - } - val sleepDelay = (nextTime - DateTime.now().local).millisecondsLong - delay(sleepDelay) - send(nextTime) +fun KronSchedulerTz.asTzFlow(): Flow = channelFlow { + doWhile { + send(DateTime.nowLocal()) + isActive } } /** * This method is a map for [asTzFlow] and will works the same but return flow with [DateTime]s + * + * @see channelFlow + * @see KronScheduler.doWhile */ @FlowPreview -fun KronScheduler.asFlow(): Flow = asTzFlow().map { it.local } +fun KronScheduler.asFlow(): Flow = channelFlow { + doWhile { + send(DateTime.now()) + isActive + } +} + +/** + * This [Flow] will trigger emitting each near time which will be returned from [this] [KronScheduler] with attention to + * time zones + * + * @see channelFlow + * @see KronScheduler.asFlow + * @see KronSchedulerTz.asTzFlow + */ +@FlowPreview +fun KronScheduler.asTzFlow(): Flow = when (this) { + is KronSchedulerTz -> asTzFlow() + else -> asFlow().map { it.local } +} @Deprecated( "It is not recommended to use this class in future. This functionality will be removed soon", From d36e406b5995e50c49c2b7c5430e01fc9387f239 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Sun, 26 Sep 2021 11:49:01 +0600 Subject: [PATCH 3/5] now flows are using doInfinity --- src/commonMain/kotlin/dev/inmo/krontab/Executes.kt | 6 ++++-- .../kotlin/dev/inmo/krontab/utils/SchedulerFlow.kt | 6 ++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/commonMain/kotlin/dev/inmo/krontab/Executes.kt b/src/commonMain/kotlin/dev/inmo/krontab/Executes.kt index e15eebf..b35c7a2 100644 --- a/src/commonMain/kotlin/dev/inmo/krontab/Executes.kt +++ b/src/commonMain/kotlin/dev/inmo/krontab/Executes.kt @@ -3,6 +3,8 @@ package dev.inmo.krontab import com.soywiz.klock.DateTime import com.soywiz.klock.DateTimeTz import kotlinx.coroutines.delay +import kotlinx.coroutines.isActive +import kotlin.coroutines.coroutineContext /** * Execute [block] once at the [KronScheduler.next] time and return result of [block] calculation. @@ -86,7 +88,7 @@ suspend inline fun doWhile( */ suspend inline fun KronScheduler.doInfinity(noinline block: suspend () -> Unit) = doWhile { block() - true + coroutineContext.isActive } /** @@ -94,7 +96,7 @@ suspend inline fun KronScheduler.doInfinity(noinline block: suspend () -> Unit) */ suspend inline fun KronSchedulerTz.doInfinity(noinline block: suspend () -> Unit) = doWhile { block() - true + coroutineContext.isActive } /** * Will [buildSchedule] using [scheduleConfig] and call [doInfinity] with [block] diff --git a/src/commonMain/kotlin/dev/inmo/krontab/utils/SchedulerFlow.kt b/src/commonMain/kotlin/dev/inmo/krontab/utils/SchedulerFlow.kt index b54e308..d83fbcd 100644 --- a/src/commonMain/kotlin/dev/inmo/krontab/utils/SchedulerFlow.kt +++ b/src/commonMain/kotlin/dev/inmo/krontab/utils/SchedulerFlow.kt @@ -15,9 +15,8 @@ import kotlinx.coroutines.flow.* */ @FlowPreview fun KronSchedulerTz.asTzFlow(): Flow = channelFlow { - doWhile { + doInfinity { send(DateTime.nowLocal()) - isActive } } @@ -29,9 +28,8 @@ fun KronSchedulerTz.asTzFlow(): Flow = channelFlow { */ @FlowPreview fun KronScheduler.asFlow(): Flow = channelFlow { - doWhile { + doInfinity { send(DateTime.now()) - isActive } } From 276ecaac27074ba11ef93ef9c76c6de9728fce8a Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Sun, 26 Sep 2021 13:05:59 +0600 Subject: [PATCH 4/5] overwrite do* extensions --- CHANGELOG.md | 1 + README.md | 44 +++-- .../kotlin/dev/inmo/krontab/Executes.kt | 150 +++++++++++++----- 3 files changed, 143 insertions(+), 52 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4ff747d..8c8d7b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ * Flows now use `doWhile` functions * `doWhile` now use additional delay (for 1 ms) for cases when `block` executing too fast +* New extensions for `KronScheduler`: `do(Once/While/Infinity)Local`/`do(Once/While/Infinity)Tz` ## 0.6.4 diff --git a/README.md b/README.md index c859367..9d51a63 100644 --- a/README.md +++ b/README.md @@ -7,16 +7,6 @@ Library was created to give oppotunity to launch some things from time to time according to some schedule in runtime of applications. -| Table of content | -|---| -| [ How to use ](#how-to-use) | -| [ How to use: Including in project ](#including-in-project) | -| [ How to use: Config from string ](#config-from-string) | -| [ How to use: Config via builder (DSL preview) ](#config-via-builder) | -| [ How to use: KronScheduler as a Flow ](#KronScheduler-as-a-Flow) | -| [ How to use: Offsets ](#Offsets) | -| [ How to use: Note about week days ](#Note-about-week-days) | - ## How to use There are several ways to configure and use this library: @@ -133,6 +123,38 @@ kronScheduler.doInfinity { All of these examples will do the same things: print `Called` message every five seconds. +### do\* functions + +With regular `doOnce`/`doWhile`/`doInfinity` there are two types of their variations: **local** and **timezoned**. Local +variations (`doOnceLocal`/`doWhileLocal`/`doInfinityLocal`) will pass `DateTime` as an argument into the block: + +```kotlin +doInfinityLocal("/5 * * * *") { + println(it) // will print current date time +} +``` + +Timezoned variations (`doOnceTz`/`doWhileTz`/`doInfinityTz`) will do the same thing but pass as an argument `DateTimeTz`: + +```kotlin +doInfinityTz("/5 * * * * 0o") { + println(it) // will print current date time in UTC +} +``` + +It is useful in cases when you need to get the time of calling and avoid extra calls to system time. + +#### Helpful table for + +| | No args | Local `DateTime` | Local `DateTimeTz` with offset of `KronScheduler` | +|---| ------- | ---------------- | ------------------------------------------------- | +| **Call only near time** | doOnce | doOnceLocal | doOnceTz | +| **Call while condition is true** | doWhile | doWhileLocal | doWhileTz | +| **Work infinity*** | doInfinity | doInfinityLocal | doInfinityTz | + +*Here there is an important notice, that `Work infinity` is not exactly `infinity`. Actually, that means that `do while +coroutine is alive` and in fact executing will be stopped when coroutine became cancelled. + ### KronScheduler as a Flow Any `KronScheduler`can e converted to a `Flow KronSchedulerTz.doOnce(noinline block: suspend () -> T): T { - nextTimeZoned() ?.let { - delay((it - DateTimeTz.nowLocal()).millisecondsLong) +suspend inline fun KronScheduler.doOnceLocal(noinline block: suspend (DateTime) -> T): T { + val time = nextOrNow().also { + delay((it - DateTime.now()).millisecondsLong) } - return block() + return block(time) +} + +/** + * Execute [block] once at the [KronScheduler.next] time and return result of [block] calculation. + * + * WARNING!!! If you want to launch it in parallel, you must do this explicitly. + * + * WARNING!!! In case if [KronScheduler.next] of [this] instance will return null, [block] will be called immediatelly + */ +suspend inline fun KronScheduler.doOnceTz(noinline block: suspend (DateTimeTz) -> T): T { + val time = when (this) { + is KronSchedulerTz -> nextOrNowWithOffset() + else -> nextOrNow().local + } + delay((time - DateTimeTz.nowLocal()).millisecondsLong) + return block(time) } /** @@ -27,46 +43,78 @@ suspend inline fun KronSchedulerTz.doOnce(noinline block: suspend () -> T): * * WARNING!!! In case if [KronScheduler.next] of [this] instance will return null, [block] will be called immediately */ -suspend inline fun KronScheduler.doOnce(noinline block: suspend () -> T): T { - next() ?.let { - delay((it - DateTime.now()).millisecondsLong) - } - return block() -} +suspend inline fun KronScheduler.doOnce(noinline block: suspend () -> T): T = doOnceLocal { _ -> block() } /** - * Will [buildSchedule] using [scheduleConfig] and call [doOnce] on it + * Will [buildSchedule] using [scheduleConfig] and call [doOnceLocal] on it + * @see buildSchedule + */ +suspend inline fun doOnce( + scheduleConfig: String, + noinline block: suspend (DateTime) -> T +) = buildSchedule(scheduleConfig).doOnceLocal(block) + +/** + * Will [buildSchedule] using [scheduleConfig] and call [doOnceLocal] on it + * @see buildSchedule + */ +suspend inline fun doOnceTz( + scheduleConfig: String, + noinline block: suspend (DateTimeTz) -> T +) = buildSchedule(scheduleConfig).doOnceTz(block) + +/** + * Will [buildSchedule] using [scheduleConfig] and call [doOnceLocal] on it * @see buildSchedule */ suspend inline fun doOnce( scheduleConfig: String, noinline block: suspend () -> T -) = with(buildSchedule(scheduleConfig)) { - when (this) { - is KronSchedulerTz -> doOnce(block) - else -> doOnce(block) - } +) = doOnce(scheduleConfig) { _ -> block() } + + +/** + * Will execute [block] while it will return true as a result of its calculation + */ +suspend inline fun KronScheduler.doWhileLocal(noinline block: suspend (DateTime) -> Boolean) { + do { + delay(1L) + } while (doOnceLocal(block)) } /** * Will execute [block] while it will return true as a result of its calculation */ -suspend inline fun KronScheduler.doWhile(noinline block: suspend () -> Boolean) { +suspend inline fun KronScheduler.doWhileTz(noinline block: suspend (DateTimeTz) -> Boolean) { do { delay(1L) - val doNext = doOnce(block) - } while (doNext) + } while (doOnceTz(block)) } /** * Will execute [block] while it will return true as a result of its calculation */ -suspend inline fun KronSchedulerTz.doWhile(noinline block: suspend () -> Boolean) { - do { - delay(1L) - val doNext = doOnce(block) - } while (doNext) -} +suspend inline fun KronScheduler.doWhile(noinline block: suspend () -> Boolean) = doWhileLocal { block() } + +/** + * Will [buildSchedule] using [scheduleConfig] and call [doWhile] with [block] + * + * @see buildSchedule + */ +suspend inline fun doWhileLocal( + scheduleConfig: String, + noinline block: suspend (DateTime) -> Boolean +) = buildSchedule(scheduleConfig).doWhileLocal(block) + +/** + * Will [buildSchedule] using [scheduleConfig] and call [doWhile] with [block] + * + * @see buildSchedule + */ +suspend inline fun doWhileTz( + scheduleConfig: String, + noinline block: suspend (DateTimeTz) -> Boolean +) = buildSchedule(scheduleConfig).doWhileTz(block) /** * Will [buildSchedule] using [scheduleConfig] and call [doWhile] with [block] @@ -76,11 +124,23 @@ suspend inline fun KronSchedulerTz.doWhile(noinline block: suspend () -> Boolean suspend inline fun doWhile( scheduleConfig: String, noinline block: suspend () -> Boolean -) = with(buildSchedule(scheduleConfig)) { - when (this) { - is KronSchedulerTz -> doWhile(block) - else -> doWhile(block) - } +) = doWhileLocal(scheduleConfig) { block() } + + +/** + * Will execute [block] without any checking of result + */ +suspend inline fun KronScheduler.doInfinityLocal(noinline block: suspend (DateTime) -> Unit) = doWhileLocal { + block(it) + coroutineContext.isActive +} + +/** + * Will execute [block] without any checking of result + */ +suspend inline fun KronScheduler.doInfinityTz(noinline block: suspend (DateTimeTz) -> Unit) = doWhileTz { + block(it) + coroutineContext.isActive } /** @@ -92,12 +152,25 @@ suspend inline fun KronScheduler.doInfinity(noinline block: suspend () -> Unit) } /** - * Will execute [block] without any checking of result + * Will [buildSchedule] using [scheduleConfig] and call [doInfinity] with [block] + * + * @see buildSchedule */ -suspend inline fun KronSchedulerTz.doInfinity(noinline block: suspend () -> Unit) = doWhile { - block() - coroutineContext.isActive -} +suspend inline fun doInfinityLocal( + scheduleConfig: String, + noinline block: suspend (DateTime) -> Unit +) = buildSchedule(scheduleConfig).doInfinityLocal(block) + +/** + * Will [buildSchedule] using [scheduleConfig] and call [doInfinity] with [block] + * + * @see buildSchedule + */ +suspend inline fun doInfinityTz( + scheduleConfig: String, + noinline block: suspend (DateTimeTz) -> Unit +) = buildSchedule(scheduleConfig).doInfinityTz(block) + /** * Will [buildSchedule] using [scheduleConfig] and call [doInfinity] with [block] * @@ -106,9 +179,4 @@ suspend inline fun KronSchedulerTz.doInfinity(noinline block: suspend () -> Unit suspend inline fun doInfinity( scheduleConfig: String, noinline block: suspend () -> Unit -) = with(buildSchedule(scheduleConfig)) { - when (this) { - is KronSchedulerTz -> doInfinity(block) - else -> doInfinity(block) - } -} +) = buildSchedule(scheduleConfig).doInfinity(block) From 97752e1ac00795d1cac9ec8edc51894af62ea306 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Sun, 26 Sep 2021 13:06:22 +0600 Subject: [PATCH 5/5] optimize imports --- .../kotlin/dev/inmo/krontab/internal/CronDateTime.kt | 4 ++-- src/commonMain/kotlin/dev/inmo/krontab/utils/DateTimeCopy.kt | 3 ++- src/commonMain/kotlin/dev/inmo/krontab/utils/SchedulerFlow.kt | 3 ++- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/commonMain/kotlin/dev/inmo/krontab/internal/CronDateTime.kt b/src/commonMain/kotlin/dev/inmo/krontab/internal/CronDateTime.kt index 350a066..bfc979e 100644 --- a/src/commonMain/kotlin/dev/inmo/krontab/internal/CronDateTime.kt +++ b/src/commonMain/kotlin/dev/inmo/krontab/internal/CronDateTime.kt @@ -1,8 +1,8 @@ package dev.inmo.krontab.internal -import com.soywiz.klock.* +import com.soywiz.klock.DateTime +import com.soywiz.klock.TimezoneOffset import dev.inmo.krontab.KronScheduler -import dev.inmo.krontab.utils.copy /** * @param daysOfWeek 0-6 diff --git a/src/commonMain/kotlin/dev/inmo/krontab/utils/DateTimeCopy.kt b/src/commonMain/kotlin/dev/inmo/krontab/utils/DateTimeCopy.kt index c3666fb..4fdef64 100644 --- a/src/commonMain/kotlin/dev/inmo/krontab/utils/DateTimeCopy.kt +++ b/src/commonMain/kotlin/dev/inmo/krontab/utils/DateTimeCopy.kt @@ -1,6 +1,7 @@ package dev.inmo.krontab.utils -import com.soywiz.klock.* +import com.soywiz.klock.DateTime +import com.soywiz.klock.Month import kotlin.math.min fun DateTime.copy( diff --git a/src/commonMain/kotlin/dev/inmo/krontab/utils/SchedulerFlow.kt b/src/commonMain/kotlin/dev/inmo/krontab/utils/SchedulerFlow.kt index d83fbcd..a14807c 100644 --- a/src/commonMain/kotlin/dev/inmo/krontab/utils/SchedulerFlow.kt +++ b/src/commonMain/kotlin/dev/inmo/krontab/utils/SchedulerFlow.kt @@ -3,7 +3,8 @@ package dev.inmo.krontab.utils import com.soywiz.klock.DateTime import com.soywiz.klock.DateTimeTz import dev.inmo.krontab.* -import kotlinx.coroutines.* +import kotlinx.coroutines.FlowPreview +import kotlinx.coroutines.delay import kotlinx.coroutines.flow.* /**