9

This might be a very basic question in terms of Asynchronous Programming, but I tried reading about it and couldn't find any resource for the same.

Assumption: A general idea I have about asynchronous programming:

When we start a blocking operation(networks call, reading from DB/file), we can delegate it to Kernel thread(s) which will keep our application thread(s) free for other jobs. The kernel thread waits for the job to be done and gives a callback to the application thread whenever the job is done.

Coroutines: I have been reading about Kotlin Coroutines for last few days. I think the concept wise coroutines are language agnostic though. The question I have been getting is:

How the suspension and continuation takes place for a co-routine. Coroutines are not threads(which are given a slice of the processor by OS), they are tasks which will be scheduled on a thread to be executed.

Who keeps looking for the program in execution and say, this coroutine has hit a suspension point and it should be taken out of thread. Another coroutine which needs to be resumed from continuation should be scheduled on the thread. As for as I have read about Java Fibers it will be done by a Fiber Scheduler, is it similar in Kotlin?

Thank for the help in advance.

Trevor Reid
  • 3,310
  • 4
  • 27
  • 46
Mritunjay
  • 25,338
  • 7
  • 55
  • 68
  • 1
    Java doesn't have coroutines, so I have removed the java tag. – Stephen C May 24 '19 at 09:14
  • @StephenC, They are planning to introduce a similar concept using java fibers. I was searching for a tag with that name, I couldn't find. Thanks though, it's not too relevant. – Mritunjay May 24 '19 at 13:03

1 Answers1

6

Coroutine suspension is entirely explicit and first-class. It happens when you call suspendCoroutine() or suspendCancellableCoroutine(), passing in a block that receives the continuation as the parameter.

The block can do whatever it wants with the continuation object, and when someone, somewhere, calls continuation.resume(resultValue(), it will resume. The thread it resumes on is initially the thread that calls resume(), but the logic inside resume immediately delegates to the Dispatcher in charge, which then typically submits the resumption to another thread or thread pool.

The Dispatcher logic is again first-class and you can write your own dispatcher. However, this is almost never needed because there are only a handful of meaningful ways to do it, and Kotlin already supports them.

You can also review a concrete example in code that demonstrates the naked usage of suspendCoroutine and coroutine.resume(), without the layer the Dispatcher adds to it.


BTW you never delegate blocking operations to "kernel threads" to make them non-blocking. Async operations don't block any thread. On the low level there's for example the mechanism of a selector that receives events from IO operations when they complete. It works much like the event loop in a GUI thread.

Marko Topolnik
  • 195,646
  • 29
  • 319
  • 436
  • Hi, Thanks for your answer. I will do some more research about the coroutines. If you have any good references, please add them to the answer. – Mritunjay May 26 '19 at 16:49
  • When you say `even-loop` do you mean that all the callbacks get put in a `queue/stack` and the application keeps coming checking for is the data resolved for a particular callback so it can be invoked. As for as I know that's how `event-loop ` works. – Mritunjay May 26 '19 at 16:53
  • The event loop's thread just picks up events from the queue and executes their handlers. It doesn't have to check anything because the fact that an event is on the queue already means that the data for its callback is there. The event loop is an essential part of green threads / cooperative multithreading. – Marko Topolnik May 27 '19 at 06:13
  • Sorry I missed your comment. I am not sure if I was able to my confusion in the comment. I meant a scenario where, where I am saying `readLines('a.txt', processLines)` where `processLines` is a callback fn. Now I assume as soon as this line is executed the `processLines` is pushed on a callback queue to be picked up later, once the data is available. Now how does `event-loop` picks this `callback` again, I assume it keeps going through the callback-queue and executes all the callbacks for which data is available. – Mritunjay Jun 06 '19 at 04:34
  • It's not immediately pushed to the callback queue, but only after the lines have been read. It's actually a two-step process: you pass a library-specific callback to `readLines` (for example, a HTTP response callback) that the library at hand calls when it has retrieved the data. The callback calls `continuation.resume(response)`, which implicitly triggers the Kotlin dispatcher, which will automatically create another callback to put on the GUI event queue. This will then resume your suspendable function. – Marko Topolnik Jun 06 '19 at 18:27
  • ohhh, thanks for the detailed response. It will be really great if you can point a few references for me to go through. – Mritunjay Jun 07 '19 at 02:36