Cancel all scope children coroutines based on any child result - kotlin-coroutines

Assume there are two suspend function which return a boolean. They need to be launched in parallel but cancel each other if false is returned.
suspend fun task1(): Boolean {
delay(10000)
return true
}
suspend fun task2(): Boolean {
return false
}
The tasks can be run in parallel by launching them with async and cancel each other by throwing CancellationException inside a coroutineScope as shown below.
coroutineScope {
setOf(
async { task1() },
async { task2() }
).forEach{ d ->
d.await().also {
if (it == false)
throw CancellationException()
}
}
}
The problem is that the results are processed in the order of await calls, i.e. task2 result is processed only after task1 result and hence task2 won't ever cancel task1.
It is possible to re-arrange this to let task2 cancel task1?

If you need to wait for more results at once in a parallel manner (instead of the sequential manner where your problem lies) you can use the select clause, like this:
coroutineScope {
val tasks = setOf(
async { task1() },
async { task2() }
)
select {
tasks.forEach {
it.onAwait { finishedSuccessfully ->
if (!finishedSuccessfully)
tasks.forEach { task -> task.cancel() }
}
}
}
}

You could use mutableStateFlow and listen if any data changes then collect it and cancel all tasks if it returns false. Like in LiveData.

Related

Why is the time taken different using async and await in a coroutine?

I have a question while learning coroutines.
fun main() {
runBlocking {
val time = measureTimeMillis {
val a = async {
delay(1000)
"1"
}
val b = async {
delay(1000)
"2"
}
val a1 = a.await()
val b1 = b.await()
}
println(time)
}
}
Running this takes 1000
fun main() {
runBlocking {
val time = measureTimeMillis {
val a = async {
delay(1000)
"1"
}.await()
val b = async {
delay(1000)
"2"
}.await()
}
println(time)
}
}
but Running this takes 2000
I'd like to know why this is happening
Does the behavior change depending on returning Deferred versus returning a value after execution?
From async docs
By default, the coroutine is immediately scheduled for execution.
In first case you simply launch two coroutines with async, which start executing immediately, by the time you call await on both of them they are up and running. so they both take only 1000 millis to complete.
but in second case you launch a and then wait for it to finish, this takes 1000 millis and then you start b and wait for it to finish, which also takes 1000 millis. so ultimatly this costs you 2000 millis.

What's the best pattern for exception handling when using coroutines in kotlinjs?

I have a kotlinjs app. I handle a particular event (dropping of data onto a component) like this:
onEvent {
drop = { event ->
GlobalScope.async {
//...
dropTask(y, data)
}
}
}
// ...
// this function has to be a suspend function because model's is
private suspend fun dropTask(y: Int, taskId: TaskId) {
// ... prepare data
model.insertBefore(taskId!!, insertBefore?.id)
}
// ... Model's function is defined like this:
suspend fun insertBefore(taskToInsert: TaskId, taskBefore: TaskId?) {
val (src, _) = memory.find(taskToInsert)
// ... and finally, the find function is:
fun find(taskId: TaskId): Pair<Task?, Int> {
// ...
return if (task != null) {
// ...
} else {
throw Exception("Couldn't find task with id $taskId!!")
}
}
The issue is that the Exception gets thrown, but isn't reported anywhere.
I have tried:
a) Installing a CoroutineExceptionHandler into the GlobalScope.async (i.e.:
val handler = CoroutineExceptionHandler { _, e ->
console.log("Caught exception: $e")
}
GlobalScope.async(handler) {
...but this never gets called. This would be relatively clean if I could make it work. It would be even nicer if this was default behavior for kotlinjs, so that exceptions weren't accidentally unreported.
b) Calling await:
drop = { event ->
GlobalScope.launch {
GlobalScope.async() {
// ...
dropTask(y, data)
}.await()
}
}
This does result in the exception being logged to the console, but it's so ugly. It's not possible to call .await() outside of a suspend function or coroutine, so for this particular event handler I have to wrap the async call in a launch. I must be doing something wrong. Anybody have a better pattern that I should be using?

How do I write an asynchronous function which polls a resource and returns when it's ready or otherwise retries in a few seconds?

I want to write an asynchronous function which repeatedly polls a resource from the web and returns when it's ready. I am implementing it using future::poll_fn:
#![feature(async_await)]
/*
[dependencies]
rand = "0.7.0"
futures-preview = "=0.3.0-alpha.18"
*/
use futures::future;
use rand;
use std::task::Poll;
enum ResourceStatus {
Ready,
NotReady,
}
use ResourceStatus::*;
// Mocking the function requesting a web resource
fn poll_web_resource() -> ResourceStatus {
if rand::random::<f32>() < 0.1 {
Ready
} else {
NotReady
}
}
async fn async_get_resource() {
// do other works
future::poll_fn(|ctx| match poll_web_resource() {
Ready => Poll::Ready(()),
NotReady => Poll::Pending,
})
.await
}
fn main() {
futures::executor::block_on(async_get_resource());
}
It doesn't work because the task gets parked forever when poll_web_resource() returns NotReady. One way to solve it is to wake the task every time it returns Pending:
future::poll_fn(|ctx| match poll_web_resource() {
Ready => Poll::Ready(()),
NotReady => {
ctx.waker().wake_by_ref();
Poll::Pending
}
})
.await
This creates loads of unnecessary requests. For my use case, the ideal situation would be request the resource every few seconds when it's not ready. Here's my current workaround:
future::poll_fn(|ctx| match poll_web_resource() {
Ready => Poll::Ready(()),
NotReady => {
let waker = ctx.waker().clone();
thread::spawn(move || {
thread::sleep(Duration.from_millis(5000));
waker.wake();
});
Poll::Pending
}
})
.await
This works, but it uses an extra thread just for tracking the timeout. I think there should be a better way to do it. How can I achieve the same goal more idiomatically?
Since you are using async / await keywords, write a loop that exits when the resource is available, or waits when it's not. Waiting can be accomplished with Tokio's Delay:
#![feature(async_await)]
use futures; // 0.3.0-alpha.17
use rand; // 0.7.0
use std::time::Duration;
use tokio::timer; // 0.2.0-alpha.1
enum ResourceStatus {
Ready,
NotReady,
}
use ResourceStatus::*;
async fn async_get_resource() {
const SLEEP_TIME: Duration = Duration::from_secs(1);
loop {
match poll_web_resource() {
Ready => return,
NotReady => {
// Don't actually use println in production async code.
println!("Waiting...");
timer::Delay::new(tokio::clock::now() + SLEEP_TIME).await;
}
}
}
}
fn poll_web_resource() -> ResourceStatus {
if rand::random::<f32>() < 0.1 {
Ready
} else {
NotReady
}
}
fn main() {
let runtime = tokio::runtime::Runtime::new().expect("Unable to create the runtime");
let _resource = runtime.block_on(async_get_resource());
}

is this right to use coroutines in a non coroutine context

Having a Processor class, trying to replace some of the code with coroutines. Since it is in a non coroutines context so val serviceScope = CoroutineScope(Dispatchers.IO + serviceJob) is added and used for start coroutines.
Added CoroutineScope, and using serviceScope.launch{} in the place which was using Thread{}.start().
Inside the function restart(), it replaced the using of CountDownLatch with
serviceScope.launch {
withContext(Dispatchers.IO) {
doReset()
}
}
Question: this launch/withContext actually does not stop the code execution of the next if (!conDoProcess) -- so it fails to do what the latch used to do.
what is the right way to stop the code execution until the doReset() . is done?
Another question, when dispose this Processor object it calls serviceScope.cancel(),
what is the difference if call with serviceJob.cancel()?
class Processor {
private val serviceJob = Job()
private val serviceScope = CoroutineScope(Dispatchers.IO + serviceJob)
.........
/* return false if the it does not start the processing */
fun restart(): Boolean {
synchronized(_lock) {
.........
// 1.old code using latch to wait
/******************
val latch = CountDownLatch(1)
streamThreadPoolExecutor.execute {
doReset() //
latch.countDown()
}
latch.await(3, TimeUnit.SECONDS) // wait at most for 3 seconds if no one calls countDown
*******************/
// 2. change to using coroutines to suspend
serviceScope.launch {
withContext(Dispatchers.IO) {
doReset()
}
}
// wait until reset is done
if (!conDoProcess) {// the doRest() should update conDoProcess
return false
}
for (i in providers.indices) {
val pr = provider[i]
serviceScope.launch {
pr.doProcess()
}
}
return true
}
}
fun dispose() {
synchronized(_lock) {
.........
serviceScope.cancel()
// or should it use
// serviceJob.cancel()
//==========>
}
}
}
I think it used the serviceScope.launch wrong, it should include the rest part after the blocking part withContext(Dispatchers.IO), but inside the serviceScope.launch.
// 2. change to using coroutines to suspend
serviceScope.launch {
withContext(Dispatchers.IO) {
doReset()
}
// wait until reset is done
if (!conDoProcess) {// the doRest() should update conDoProcess
return false
}
for (i in providers.indices) {
val pr = provider[i]
serviceScope.launch {
pr.doProcess()
}
}
}
return true

How to build a monitor with infinite loop?

I am building a monitor in Kotlin to schedule certain operations, what I want is a program that inserts or updates some database entries for a given time intervall. What I got so far is a program that runs for a given time span, but I have an infinite loop in my porgram that takes up to 30% of processor power when it is not time for an update. So my question is how to build a monitor without an infinite loop?
this my code so far:
while(!operations.done && appConfigurations.run_with_monitor) {
if (DataSourceMonitor.isReadyForUpdate(lastMonitorModel)) {
operations.update()
}
}
operations is an entire sequence of different actions to execute. Each operation implementing the IScheduler interface.
interface IScheduler {
var done: Boolean
fun update()
fun reset()
}
Example of implementation:
class Repeat(private val task: IScheduler) : IScheduler {
override var done = false
override fun update() {
if (this.task.done) {
this.reset()
}
this.task.update()
//logger.info { "Update repeat, done is always $done" }
}
override fun reset() {
this.task.reset()
this.done = false
}
}
class Sequence(private val task1: IScheduler, private val task2: IScheduler): IScheduler {
override var done = false
var current = task1
var next = task2
override fun update() {
if (!this.done) {
this.current.update()
if (this.current.done) {
this.current = this.next
}
if (this.next.done) {
this.done = true
}
}
}
class Print(private val msg: String): IScheduler {
override var done = false
override fun update() {
println(this.msg)
this.done = true
}
override fun reset() {
this.done = false
}
}
The value of operations can be as follows:
val operations = Repeat(Sequence(Print("First action"), Print("Another action")))
**So right now my monitor is working and completely functional, but how can I improve the performance of the infinite loop? **
Hope anyone has some ideas about this.
If your DataSourceMonitor has no way to block until isReadyForUpdate is going to return true, then the usual approach is to add a delay. eg:
while(!operations.done && appConfigurations.run_with_monitor) {
if (DataSourceMonitor.isReadyForUpdate(lastMonitorModel)) {
operations.update()
} else {
Thread.sleep(POLL_DELAY);
}
}
If it's always ready for update there won't be any delay, but if it ever isn't ready for update then it'll sleep. You'll need to tune the POLL_DELAY. Bigger values mean less CPU usage, but greater latency in detecting new events to process. Smaller values produce less latency, but use more CPU.
If you really want to get fancy you can have the poll delay start small and then increase up to some maximum, dropping back down once events are found. This is probably overkill, but look up "adaptive polling" if you're interested.
I have refactored my code and I can accomplish the same result with less code, by removing the IScheduler interface by the abstract class TimerTask. The job can be done with these lines of code:
val scheduler = Sequence(
Print("Executed task 1"),
Sequence(Print("Executed task 2"),
Sequence(Print("Executed task 3"), Print("Finished Scheduler")))
)
Timer().schedule(scheduler, DELAY, PERIOD)
All the interface implementations are changed to TimerTask implementations:
class Print(private val msg: String): TimerTask() {
override fun run() {
println(msg)
}
}
class Sequence(private val task1: Runnable, private val task2: Runnable): TimerTask() {
override fun run() {
task1.run()
task2.run()
}
}

Resources