Ignoring offers to coroutine channels after closing - kotlin

Is there a good way to have channels ignore offers once closed without throwing an exception?
Currently, it seems like only try catch would work, as isClosedForSend isn't atomic.
Alternatively, is there a problem if I just never close a channel at all?
For my specific use case, I'm using channels as an alternative to Android livedata (as I don't need any of the benefits beyond sending values from any thread and listening from the main thread). In that case, I could listen to the channel through a producer that only sends values when I want to, and simply ignore all other inputs.
Ideally, I'd have a solution where the ReceiveChannel can still finish listening, but where SendChannel will never crash when offered a new value.

Channels throw this exception by design, as means of correct communication.
If you absolutely must have something like this, you can use an extension function of this sort:
private suspend fun <E> Channel<E>.sendOrNothing(e: E) {
try {
this.send(e)
}
catch (closedException: ClosedSendChannelException) {
println("It's fine")
}
}
You can test it with the following piece of code:
val channel = Channel<Int>(capacity = 3)
launch {
try {
for (i in 1..10) {
channel.sendOrNothing(i)
delay(50)
if (i == 5) {
channel.close()
}
}
println("Done")
}
catch (e: Exception) {
e.printStackTrace()
}
finally {
println("Finally")
}
}
launch {
for (c in channel) {
println(c)
delay(300)
}
}
As you'll notice, producer will start printing "It's fine" since the channel is closed, but consumer will still be able to read first 5 values.
Regarding your second question: it depends.
Channels don't have such a big overhead, and neither do suspended coroutines. But a leak is a leak, you know.

I ended up posting an issue to the repo, and the solution was to use BroadcastChannel. You can create a new ReceiveChannel through openSubscription, where closing it will not close the SendChannel.
This more accurately reflects RxJava's PublishSubject

Related

Kotlin Coroutines - Asynchronously consume a sequence

I'm looking for a way to keep a Kotlin sequence that can produces values very quickly, from outpacing slower async consumers of its values. In the following code, if the async handleValue(it) cannot keep up with the rate that the sequence is producing values, the rate imbalance leads to buffering of produced values, and eventual out-of-memory errors.
getSequence().map { async {
handleValue(it)
}}
I believe this is a classic producer/consumer "back-pressure" situation, and I'm trying to understand how to use Kotlin coroutines to deal with it.
Thanks for any suggestions :)
Kotlin channels and flows offer buffering producer dispatched data until the consumer/collector is ready to consume it.
But Channels have some concerns that have been manipulated in Flows; for instance, they are considered hot streams:
The producer starts for dispatching data whether or not there is an attached consumer; and this introduces resource leaks.
As long as no consumer attached to the producer, the producer will stuck in suspending state
However Flows are cold streams; nothing will be produced until there is something to consume.
To handle your query with Flows:
GlobalScope.launch {
flow {
// Producer
for (item in getSequence()) emit(item)
}.map { handleValue(it) }
.buffer(10) // Optionally specify the buffer size
.collect { // Collector
}
}
For my own reference, and to anyone else this may help, here's how I eventually solved this using Channels - https://kotlinlang.org/docs/channels.html#channel-basics
A producer coroutine:
fun itemChannel() : ReceiveChannel<MyItem> {
return produce {
while (moreItems()) {
send(nextItem()) // <-- suspend until next 'receive()'
}
}
}
And a function to run multiple consumer coroutines, each reading off that channel:
fun itemConsumers() {
runBlocking {
val channel = itemChannel()
repeat(numberOfConsumers) {
launch {
var more = true
while (more) {
try {
val item = channel.receive()
// do stuff with item here...
} catch (ex: ClosedReceiveChannelException) {
more = false
}
}
}
}
}
}
The idea here is that the consumer receives off the channel within the coroutine, so the next receive() is not called until a consumer coroutine finishes handling the last item. This results in the desired back-pressure, as opposed to receiving from a sequence or flow in the main thread, and then passing the item into a coroutine to be consumed. In that scenario there is no back-pressure from the receiver, since the receive happens in a different coroutine than where the received item is consumed.

Is Kotlin's runCatching..also equivalent to try..finally?

I want to run cleanup code after a certain block of code completes, regardless of exceptions. This is not a closeable resource and I cannot use try-with-resources (or Kotlin's use).
In Java, I could do the following:
try {
// ... Run some code
} catch(Exception ex) {
// ... Handle exception
} finally {
// ... Cleanup code
}
Is the following Kotlin code equivalent?
runCatching {
// ... Run some code
}.also {
// ... Cleanup code
}.onFailure {
// ... Handle exception
}
Edit: added boilerplate exception handling - my concern is with ensuring the cleanup code runs, and maintainability.
There is one important difference, where the code inside runCatching contains an early return. A finally block will be executed even after a return, whereas also has no such magic.
This code, when run, will print nothing:
fun test1()
runCatching {
return
}.also {
println("test1")
}
}
This code, when run, will print "test2":
fun test2() {
try {
return
} finally {
println("test2")
}
}
There is one big difference between both code samples. try...finally propagates exceptions while runCatching().also() catches/consumes them. To make it similar you would have to throw the result at the end:
runCatching {
// ... Run some code
}.also {
// ... Cleanup code
}.getOrThrow()
But still, it is not really 1:1 equivalent. It catches all exceptions just to rethrow them. For this reason, it is probably less performant than simple try...finally.
Also, I think this is less clear for the reader. try...finally is a standard way of dealing with exceptions. By using runCatching() just to immediately rethrow, you actually confuse people reading this code later.
Your question sounded a little like you believed Kotlin does not have try...finally and you need to search for alternatives. If this is the case, then of course Kotlin has try...finally and I think you should use it instead of runCatching().
As per Kotlin's doc for runCatching:
Calls the specified function block and returns its encapsulated result if invocation was successful, catching any Throwable exception that was thrown from the block function execution and encapsulating it as a failure.
Even if finally always runs after a try block and also always runs after a runCatching, they do not serve the same purpose.
finally doesn't receive any argument and cannot operate on the values of the try block, while also receives the Result of the runCatching block.
TLDR; .runCatching{}.also{} is a more advanced try{}finally{}
There is also a difference in what is the result of evaluating the expression.
Consider the following code:
fun main() {
val foo = try {
throw Exception("try")
} catch(e: Exception) {
"catch"
} finally {
"finally"
}
val bar = runCatching{
throw Exception("runCatching")
}.also{
"also"
}.onFailure {
"onFailure"
}
println(foo)
println(bar)
}
The output will be:
catch
Failure(java.lang.Exception: runCatching)
https://pl.kotl.in/a0aByS5l1
EDIT:
An interesting article that points out some differences as well:
https://medium.com/#mattia23r/a-take-on-functional-error-handling-in-kotlin-515b67b4212b
Now let’s give a second look at the implementation of runCatching in the gist above. What does it do? It catches everything.
In this case, it goes even further: it catches all Throwables. For those not knowing, Throwable is everything that can go after a throw keyword; it has two descendants: Exceptions and Errors. We haven’t mentioned Errors so far; Errors usually represent something wrong that happened at a lower level than your business logic, something that can’t usually be recovered with a simple catch.

CoroutineScope cancel listener

I'm performing some work in a class that is using a Scope:
class MyClass(val scope: CoroutineScope) {
private val state: StateFlow<Int> = someFlow()
.shareIn(scope, started = SharingStared.Eagerly, initialValue = 0)
fun save() {
scope.launch {
save(state.value)
}
}
}
Now I want to clean up when the scope is cancelled. What is the best way to do this? I could come up with this, but that doesn't really sound stable.
init {
scope.launch {
try { delay(10000000000000) }
finally { withContext(Noncancellable) { save(state.value) } }
}
}
Edit: I've modified my snippet to more reflect what I'm doing. The state Flow updates several times per second, and when I invoke the save() method I want to save the state to disk (So I don't want to do this every time the state changes).
Next to that, I want to save the state when the scope is cancelled (i.e. at the very end). This is where I'm having trouble.
There is no such "onCancellation" mechanism on CoroutineScope to my knowledge.
In general, clean up can be "prepared" on the spot when executing the code that requires cleanup. For instance, using an input stream with use { ... } or closing resources with finally blocks.
This will be automatically honored on cancellation (or any other failures, btw), because cancellation of the scope simply generates CancellationExceptions inside running coroutines.
Now, sometimes (as in your case) you have more complex needs, and in that case I would say that the cancellation of the scope is just one thing that happens at the end of some kind of lifecycle, and you can do the cleanup you need at the same place where you cancel the scope.
If you really want to use a workaround like your current parallel coroutine, you can use awaitCancellation instead of a huge delay:
init {
scope.launch {
try { awaitCancellation() }
finally { withContext(Noncancellable) { save(state.value) } }
}
}
But I still don't find it very appealing tbh.
You can use a Exception handler
// Destroy service when completed or in case of an error.
val handler = CoroutineExceptionHandler { _, exception ->
Log.e("CoroutineExceptionHandler Error", exception.message!!)
stopSelf(startId)
}
Then you can use this Handler as
scope.launch(handler){
// do stuff
}
handler will be called only if an exception is thrown

How can I get a non-blocking infinite loop in a Kotlin Actor?

I would like to consume some stream-data using Kotlin actors
I was thinking to put my consumer inside an actor, while it polls in an infinite loop while(true). Then, when I decide, I send a message to stop the consumer.
Currently I have this:
while(true) {
for (message in channel){ <--- blocked in here, waiting
when(message) {
is MessageStop -> consumer.close()
else -> {}
}
}
consumer.poll()
}
The problem
The problem with this is that it only runs when I send a message to the actor, so my consumer is not polling the rest of the time because channel is blocking waiting to receive the next message
Is there any alternative?, someone with the same issue?, or something similar to actors but not blocked by channel in Kotlin?
Since the channel is just a Channel (https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-channel/index.html) you can first check if the channel is empty and if so start your polling. Otherwise handle the messages.
E.g.
while(true) {
while (channel.isNotEmpty()) {
val message = channel.receive()
when(message) {
is MessageStop -> consumer.close()
else -> {}
}
}
consumer.poll()
}
In the end I used AKKA with Kotlin, I'm finding much easier this way
You should use postDelayed(), for example:
final Runnable r = new Runnable() {
public void run() {
// your code here
handler.postDelayed(this, 1000)
}
}
You can change 1000 with the the millisecond delay you want. Also I highly recommend to put your code inside a thread (if you are not already have) to prevent ANR (App Not Responding)

webflux with reactor does not print exception in bodyToMono

I expect either map or doOnError prints the log, but the fact is neither of them does so, making it seem like both fail to run. If bodyToMono throws exception, how to print it?
public Mono<ServerResponse> tryWebflux(ServerRequest request) {
log.info("start controller");
Mono bodyMono = request.bodyToMono(HashMap.class)
.doOnError(e -> log.error("something wrong", e))
.map(body -> {
log.info("in map");
return body;
});
bodyMono.log().subscribe(System.out::println);
return ServerResponse.ok().build();
}
Result:
[reactor-http-nio-2] reactor.util.Loggers$Slf4JLogger: onSubscribe(FluxMap.MapSubscriber)
[reactor-http-nio-2] reactor.util.Loggers$Slf4JLogger: request(unbounded)
[reactor-http-nio-2] reactor.util.Loggers$Slf4JLogger: onComplete()
my guess it is because of this line here:
bodyMono.log().subscribe(System.out::println);
You see in most cases you never subscribe in your application. The one that subscribes is the one whom initiated the call, and in most cases this is the calling client (front end, caller to the api, etc. etc.) Your server is the publisher, and in this server you want to build an event chain that basically does things one after the other.
By subscribing in the middle you are breaking the event chain.
Try:
public Mono<ServerResponse> tryWebflux(ServerRequest request) {
return request.bodyToMono(HashMap.class)
.doOnError(e -> log.error("something wrong", e))
.map(body -> {
log.info("in map");
return body;
}).log() // We log, chaining on as you did before
.then(ServerResponse.ok().build());
// Here we use the then() keyword to still keep chaining on the mono
}
It is very important to never break the event chain. If you see yourself subscribing in the middle, you have probably done something wrong, unless your application is the initiator of something, or the "consumer".