Skip to content

Something like Deadlock #331

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
lymychm opened this issue Apr 14, 2018 · 9 comments
Closed

Something like Deadlock #331

lymychm opened this issue Apr 14, 2018 · 9 comments

Comments

@lymychm
Copy link

lymychm commented Apr 14, 2018

import kotlinx.coroutines.experimental.*

object KotlinCoroutineTest {

    @JvmStatic
    fun main(args: Array<String>) = runBlocking {
        println("start")
        val to = 6
        val jobs = arrayListOf<Job>()
        (0..to).forEach {
            println("start=$it")
            jobs += launch { result() }
        }
        jobs.forEach { it.join() }
        println("End")
    }

    private fun result() = runBlocking {
        val a = async {
            delay(500)
            5
        }
        val b = async {
            delay(500)
            4
        }
        a.await() * b.await()
    }

}

Hi, so when I'm set to >= 6 I'm getting something like deadlock, End line is never printed, when to <= 5 everything works fine

coroutines version = 0.22.5
kotlin.version=1.2.31
jdk1.8.0_162
my cpu has 8 cores

my real problem is that I having REST service(on spring boot) where i'm getting data from DB in 2 async lines and in the end I'm calling await()

val firstData = async { firstService.getDataFromDb(someId) }
val secondData = async { secondService.getDataFromDb(someId)}
Result(firstData.await(), secondData.await())

and after some period from 10-30 mins(spring-boot jar working), all services in my REST api freezes, and nothing in log file, same as in this small example(but in this example I'm getting freeze every time when to >=6)

I assuming that I'm using coroutines in wrong way(maybe).

I would be glad of any help

@fvasco
Copy link
Contributor

fvasco commented Apr 14, 2018

runBlocking is a blocking function and you must not use it on the DefaultDispatcher.

So you have to use regular multi-thread programming with blocking function or suspending function in non-blocking mode.
Obviously you can mix these only if you understand well the differences.

If you want to try the non-blocking mode take a look to Spring Reactor integration.

@elizarov
Copy link
Contributor

@lymychm Remove runBlocking from fun result() and add suspend modifier like this:

private suspend fun result()  {
    ...
    return a.await() * b.await()
}

@lymychm
Copy link
Author

lymychm commented Apr 15, 2018

Thank you for the answers.
As I understand, at some top level you eventually need to runBlocking some method,

So this simple example(using String REST Controllers):

@RestController
@RequestMapping("/coroutine")
class CoroutineController(private var coroutineTestService: CoroutineTestService) {

    @GetMapping(value = ["/test"], produces = [(MediaType.APPLICATION_JSON_UTF8_VALUE)])
    fun testCoroutine() = runBlocking<ResponseEntity<*>> {
        ResponseEntity.ok(coroutineTestService.test())
    }

}

interface CoroutineTestService {
    suspend fun test(): Result
}

@Service
@Transactional(readOnly = true)
open class CoroutineTestServiceImpl(
        private val firstService: FirstService,
        private val secondService: SecondService
) : CoroutineTestService {

    override suspend fun test(): Result {
        val firstData = async { firstService.getDataFromDb() }
        val secondData = async { secondService.getDataFromDb() }
        return Result(firstData.await(), secondData.await())
    }

}

my bottleneck will be CoroutineController.test() method? Because I'm running it blocking runBlocking and at some point of time, when I will get too many request to my Controller with runBlocking method, all new requests will wait until fun test() = runBlocking<ResponseEntity<*>> unblock this fun(step by step), and my controller may dead :) ?

same with this example https://habrahabr.ru/post/346004/, we should not use coroutines in that way?

@fvasco
Copy link
Contributor

fvasco commented Apr 15, 2018

Hi @lymychm ,
how you defined getDataFromDb?

@lymychm
Copy link
Author

lymychm commented Apr 15, 2018

Hi @fvasco ,
as regular function fun getDataFromDb(): List<SomeEntity>

@fvasco
Copy link
Contributor

fvasco commented Apr 15, 2018

You cannot invoke blocking code, consider

suspend fun getDataFromDb(): List<SomeEntity>

fun getDataFromDb(): Flux<SomeEntity>

and change the function

@lymychm
Copy link
Author

lymychm commented Apr 15, 2018

you mean that if I want to invoke some function/method inside async for instance: val firstData = async { firstService.getDataFromDb() } this function/method in this case(getDataFromDb) need to be suspend? my suspend fun test(): Result in CoroutineTestServiceImpl is already suspend

@fvasco
Copy link
Contributor

fvasco commented Apr 15, 2018

getDataFromDb have to be non-blocking or you MUST use a different dispatcher (#79)

@lymychm
Copy link
Author

lymychm commented Apr 15, 2018

I see, fun getDataFromDb is non-blocking

@lymychm lymychm closed this as completed Apr 16, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants