Can I call Deferred<>.await() many times? - kotlin-coroutines

I am newbie about coroutines and I have a simple question.
Can I call await() many times to get result value?
E.g.
class Test: CoroutineScope {
val my_value = async { "Hello World!" }
suspend fun f1() {
println(my_value.await())
}
suspend fun f2() {
println(my_value.await())
}
I suppose second call "await()" will immediately return computed value. It's right?
Tnx

You can call Deferred.await() as many times as you want/need. The result is simply returned or the exception is thrown again.

Related

what reason show message that "Possibly blocking call in non-blocking context could lead to thread starvation"?

i using coroutines with spring.
message that "Possibly blocking call in non-blocking context could lead to thread starvation" is show when this code using.
really happen blocking call ?
If happen could you tell me reason ?
Thank you.
suspend fun demo() = withContext(Dispatchers.IO) {
val deferred1 = async {
aRepository.findById("id") // here
}
val deferred2 = async {
bRepository.findById("id") // here
}
deferred1.await()
deferred2.await()
}
I'm assuming that this concerns JPA repositories, and they perform work synchronous, and were build for the servlet architecture, which uses blocking code.
Using them with (asynchronous) coroutines triggers this warning. However, I'm not entirely sure if this really is an issue per se.
A solution to suppress this warning is by wrapping the blocking code with withContext (also the provided fix-it in my intellij), which suspends the coroutine while the code within the block is executed;
suspend fun demo() = withContext(Dispatchers.IO) {
val deferred1 = async {
withContext(Dispatchers.IO) {
aRepository.findById("id") // here
}
}
val deferred2 = async {
withContext(Dispatchers.IO) {
bRepository.findById("id") // here
}
}
deferred1.await()
deferred2.await()
}

How this piece of code can be improved and rewritten to kotlin coroutines

I'm trying to achieve functionality: I have a rest endpoint that calls code that execution can take a lot of time. My idea to improve experience for now is to wrap that piece of code as a new thread, wait for completion or for some max time to elapse and return an appropriate message. Wrapped code should be completed even through endpoint already send message back. Current implementation looks like this:
private const val N = 1000
private const val MAX_WAIT_TIME = 5000
#RestController
#RequestMapping("/long")
class SomeController(
val service: SomeService,
) {
private val executor = Executors.newFixedThreadPool(N)
#PostMapping
fun longEndpoint(#RequestParam("someParam") someParam: Long): ResponseEntity<String> {
val submit = executor.submit {
service.longExecution(someParam)
}
val start = System.currentTimeMillis()
while (System.currentTimeMillis() - start < MAX_WAIT_TIME) {
if (submit.isDone)
return ResponseEntity.ok("Done")
}
return ResponseEntity.ok("Check later")
}
}
First question is - waiting on while for time seems wrong, we don't release thread, can it be improved?
More important question - how to rewrite it to Kotlin coroutines?
My attempt, simple without returning as soon as task is done, looked like this:
#PostMapping
fun longEndpoint(#RequestParam("someParam") someParam: Long): ResponseEntity<String> = runBlocking {
val result = async {
withContext(Dispatchers.Default) {
service.longExecution(someParam)
}
}
delay(MAX_WAIT_TIME)
return#runBlocking ResponseEntity.ok(if(result.isCompleted) "Done" else "Check later")
}
But even through correct string is returned, answer is not send until longExecution is done. How to fix that, what am I missing? Maybe coroutines are bad application here?
There are several problems with your current coroutines attempt:
you are launching your async computation within runBlocking's scope, so the overall endpoint method will wait for child coroutines to finish, despite your attempt at return-ing before that.
delay() will always wait for MAX_WAIT_TIME even if the task is done quicker than that
(optional) you don't have to use runBlocking at all if your framework supports async controller methods (Spring WebFlux does support suspend functions in controllers)
For the first problem, remember that every time you launch a coroutine that should outlive your function, you have to use an external scope. coroutineScope or runBlocking are not appropriate in these cases because they will wait for your child coroutines to finish.
You can use the CoroutineScope() factory function to create a scope, but you need to think about the lifetime of your coroutine and when you want it cancelled. If the longExecution function has a bug and hangs forever, you don't want to leak the coroutines that call it and blow up your memory, so you should cancel those coroutines somehow. That's why you should store the scope as a variable in your class and cancel it when appropriate (when you want to give up on those operations).
For the second problem, using withTimeout is very common, but it doesn't fit your use case because you want the task to keep going even after you timeout waiting for it. One possible solution would be using select clauses to either wait until the job is done, or wait for some specified maximum time:
// TODO call scope.cancel() somewhere appropriate (when this component is not needed anymore)
val scope = CoroutineScope(Job())
#PostMapping
fun longEndpoint(#RequestParam("someParam") someParam: Long): ResponseEntity<String> {
val job = scope.launch {
longExecution()
}
val resultText = runBlocking {
select {
job.onJoin() { "Done" }
onTimeout(MAX_WAIT_TIME) { "Check later" }
}
}
return ResponseEntity.ok(resultText)
}
Note: I'm using launch instead of async because you don't seem to need the return value of longExecution here.
If you want to solve the problem #3 too, you can simply declare your handler suspend and remove runBlocking around the select:
// TODO call scope.cancel() somewhere appropriate (when this component is not needed anymore)
val scope = CoroutineScope(Job())
#PostMapping
suspend fun longEndpoint(#RequestParam("someParam") someParam: Long): ResponseEntity<String> {
val job = scope.launch {
longExecution()
}
val resultText = select {
job.onJoin() { "Done" }
onTimeout(MAX_WAIT_TIME) { "Check later" }
}
return ResponseEntity.ok(resultText)
}
Note that this requires spring-boot-starter-webflux instead of spring-boot-starter-web.
Your implementation always waits for MAX_WAIT_TIME. This might work:
#PostMapping
fun longEndpoint(#RequestParam("someParam") someParam: Long): ResponseEntity<String> = runBlocking {
try {
withTimeout(MAX_WAIT_TIME) {
async {
withContext(Dispatchers.Default) {
service.longExecution(someParam)
}
}
}
} catch (ex: CancellationException) {
return#runBlocking ResponseEntity.ok("Check later")
}
return#runBlocking ResponseEntity.ok("Done")
}
Although I'm not sure if there will be any unwanted side effects because it seems that this will cancel the coroutine when it reaches MAX_WAIT_TIME. Read more about it here:
Cancellation and timeouts

Returning values into GlobalScope launch using Spring

I have an endpoint exposed, that is launching a coroutine:
val apiCall = ApiCall()
#GetMapping("/example")
fun example(#RequestParam paramExample:String):Int{
GlobalScope.launch{
return apiCall.callApi(paramExample)
}
}
This function is calling another external API, using Retrofit:
suspend fun callApi(param:String):Int{
var tot_records =0
val retrofit: Retrofit = Retrofit.Builder()
.baseUrl(appProperties.sampleUrl)
.addConverterFactory(GsonConverterFactory.create())
.build()
val service = retrofit.create<ResponseService>(ResponseService::class.java)
service.getResponse().enqueue(object : Callback<Response> {
override fun onFailure(call: Call<Response>, throwable: Throwable) {
println("Error")
println(throwable.stackTrace)
}
override fun onResponse(call: Call<Response>, response: Response<Response>) {
println("OK")
println(response.body())
println("Tot records")
tot_records = response.body()?.tot_records!!
}
})
return tot_records
}
The problem is that I can't launch this, the error is: 'return' is not allowed here
Any idea how to fix it and whats is happening?
Thanks for your help
It seems like you can't decide if you want your code to be synchronous (so code waits for its subtasks to finish before continuing) or asynchronous (it launches operations in the background). You intend to return a result from example(), so you need it to be synchronous, but you immediately use launch() to invoke callApi() asynchronously. The same in callApi() - you intend to return from it (so synchronous), but you invoke Retrofit using callbacks (so asynchronous). Note that callApi() has exactly the same problem as example(). Even if it compiles, it still does not really work properly. It always returns 0, because tot_records is returned before being set.
You have to decide between asynchronous and synchronous and stick to it. If you want to go fully asynchronous, then you need to redesign both callApi() and example() to return their results either with callbacks or futures.
However, I suggest going fully synchronous, utilizing Kotlin suspend functions. Make all functions suspend: example(), callApi() (it is already) and ResponseService.getResponse(). The last one will look something like:
suspend fun getResponse(): Response
Then remove GlobalScope.launch(), and almost everything inside enqueue(). Instead, service.getResponse() will return Response object directly, so you can just return its tot_records property.
Also note that in your original code you ignored failures. After above change service.getResponse() will throw exceptions on failures, so you have to handle them.
This solution seems that works:
This is the endpoint declaration:
#GetMapping("/example")
suspend fun example(#RequestParam param:String):CustomResponse{
return coroutineScope {
val job = async{apiCall.callApi(param)}
job.await()
}
}
And this is my function that is calling an external API:
suspend fun callApi(param:String):CustomResponse{
var responseCustom = CustomResponse()
val retrofit: Retrofit = Retrofit.Builder()
.baseUrl(appProperties.reservationUrl)
.addConverterFactory(GsonConverterFactory.create())
.build()
val service = retrofit.create<CustomResponseService>(CustomResponseService::class.java)
responseCustom = service.getResponse(appProperties.token, param).execute().body()!!
return responseCustom
}

How to understand order of calling coroutines with scope?

I saw the coroutines example here.
To understand the example I made three different examples with each scope.
The printed number is the following #N.
It's just my opinion, so if there is a wrong thing, let me know.
#1: 3->1->2->4
runBlocking {
launch {
println("1-Task from runBlocking")
}
coroutineScope { // Creates a coroutine scope
launch {
println("2-Task from nested launch")
}
println("3-Task from coroutine scope")
}
println("4-Coroutine scope is over")
}
I understood the coroutineScope waits until the children will be completed. The outer runBlocking has two coroutines.
The first is launch of 1 and the second is in coroutineScope.
The coroutineScope will be handled first based on the call stack.
Thus the first printed number is 3 because it's within regular function.
And then the 1 can be printed before 2.
The 4 will be printed after coroutineScope block is finished.
#2: 1->3->2->4
runBlocking {
launch {
println("1-Task from runBlocking")
}
runBlocking { // Creates a coroutine scope
launch {
println("2-Task from nested launch")
}
println("3-Task from coroutine scope")
}
println("4-Coroutine scope is over")
}
runBlocking is not suspend function that's why the outer runBlocking doesn't have a coroutine. Thus the 1 will be printed first. After that, the inner runBlocking has one coroutine so wait until this block will be finished. Thus 3 and 2 will be printed. Last, 4 printed after inner runBlocking.
#3: 2->4->3->1, 4->3->2->1, 4->2->3->1
runBlocking {
launch {
println("1-Task from runBlocking")
}
GlobalScope.launch { // Creates a coroutine scope
launch {
println("2-Task from nested launch")
}
println("3-Task from coroutine scope")
}
println("4-Coroutine scope is over")
}
There are several different results so I guessed it depends on environments on runtime.
But I wonder if it's impossible to print 1 first or not.
Second, how to understand scope in other suspend or scope.

How to let withTimeoutOrNull return null but finish code in the block

I need my code to run a block and return value after 1 second in case timeout but let it finish the job.
I managed to implement something that works but IDE suggest replacing async with withContext(DefaultDispatcher) but it's not working the same.
So my question is how to make it work without IDE warnings. I am new to Kotlin Coroutines so I might be missing something here.
#Test
fun how_timeout_with_null_works() = runBlocking<Unit> {
val time = measureTimeMillis {
println("Start test")
val result = withTimeoutOrNull(1, TimeUnit.SECONDS) {
async { doSomeHardWork() }.await()
}
println("End test $result")
}
println("Time $time")
delay(3000)
}
private suspend fun doSomeHardWork(): String {
println("start hard work")
Thread.sleep(2000)
print("end hard work")
return "[hard work done]"
}
IDE gives a warning in this case because async(ctx) { ... }.await() is usually a mistake and withContext(ctx) { ... } usually better reflects the original intent of the author of the code.
In case of your code, your intent is different. Your intent is to await for 1 second, without constraining your doSomeHardWork code. However, the structure of your code does not reflect your intent. You've wrapped the whole block into withTimeout and put doSomeHardWork inside of it, while your intent was only to do a time-limited wait for it. So, if you rewrite your code in the way where the structure of your code matches your intent, it will work without any warnings:
val work = async { doSomeHardWork() } // start work
val result = withTimeoutOrNull(1, TimeUnit.SECONDS) { work.await() } // wait with timeout
If you happen to need this pattern of code more than once, then you can define yourself a handy extension:
suspend fun <T> Deferred<T>.awaitWithTimeout(time: Long, unit: TimeUnit): T? =
withTimeoutOrNull(time, unit) { await() }
And then write even nicer code that reflects your intent:
val result = async { doSomeHardWork() }.awaitWithTimeout(1, TimeUnit.SECONDS)
Be careful, though. These coroutines that you start with async will continue running after the wait had timed out. This can easily lead to resource leaks, unless you take steps to limit the number of those concurrently running background jobs.

Resources