People like to think of a coroutine as an independ...
# coroutines
m
People like to think of a coroutine as an independent process. That seems clear that
async
and 
launch
 start a coroutine.
runBlocking
is less clear, but I would say, that even though what is start is not concurrent, it starts a coroutine, it is just bound to the thread. But how about
coroutineScope
? I think that they do not really start any new coroutine, just like suspending functions do not start a coroutine, but instead they can suspend the coroutine on which they are started. I just wanted to confirm if there are no disagreements here.
👍 1
j
I agree with most of these.
runBlocking
does start a coroutine - you will be able to run suspend functions inside it, and you were not able to outside of it. It just blocks the current thread while that coroutine executes. However, I wouldn't say the started coroutine is "bound to the current thread". You could do
runBlocking(Dispatchers.Default)
and run your coroutine on a different thread. This could actually be a major difference with
coroutineScope
, which technically has to stay in the same context (and thus dispatcher).
e
Instead of saying that the
runBlocking
coroutine is bound to the current thread, you could say that the current thread is bound to the coroutine. This is simply because the function doesn't return until the coroutine completes.
👍 2
m
@Joffrey That is a really good argument. When we run
runBlocking(Dispatchers.Default)
, the body will run on the main thread. But the coroutine will still block the thread on which it is started until this body is finished. This is probably something that should be explained in the book 🤔
Copy code
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.runBlocking

fun main() {
    runBlocking(Dispatchers.Default) {
        delay(1000)
        println(Thread.currentThread().name)
    }
    runBlocking(Dispatchers.Default) {
        delay(1000)
        println(Thread.currentThread().name)
    }
}
// (1 sec)
// DefaultDispatcher-worker-1
// (1 sec)
// DefaultDispatcher-worker-1
d
Names. I was confused with many of the kotlin terminology until I realized that the authors are predominantly from eastern europe, where there is differences in sentence structure. I dont know about the actual author's native language, but I do know , for example, in Dutch the noun and verb take different 'places' in a sentence then in English. When I read 'runBlocking'; I read "Run this 'Blocking Code' " But the real meaning is 'Run THIS code WHILE blocking the caller'
💯 2
🤯 2
How this relates to spoken/natural languge -- roughly : If you take the english sentance "Throw the hay over the fence to the horse" And translate it to and from some eastern european languages you may end up with something like "Throw the Horse over the fence, some hay"
Translating back to kotlin coroutines, my first (incorrect) interpretation of runBlocking was that it was to be used for running "blocking code' within a coroutine safely. E.g. a wrapper around blocking code. It is nearly exactly the reverse
e
Interesting. I can see how your initial understanding didn't match the behaviour of
runBlocking
. What would have been a more logical name for you, indicating that the current thread will be blocked until the coroutine completes?
runWhileBlocking
? That has some connotations with
while
which is completely different...
Or maybe a counterpart to `async {}`:
sync {}
. It clearly communicates that something it synchronous, i.e. the block of coroutine code.
OTOH, note that the signature of
runBlocking
can only mean that it blocks until it returns a value. Admittedly, a function's name should be enough to understand what it does without looking at the signature. And it's less obvious if the return type is
Unit
.
j
blockAndRun
?
j
blockAndRun
could sound like block THEN run. Maybe
blockToRun
?
j
Well, blocking occurs before running, so what's wrong with "block THEN run"?
But I like
blockToRun
too...
j
I see your point, although the way I see it we block during the run, so we may start blocking before starting to run the lambda, but we stop blocking afterwards. I was thinking of this in terms of "block in order to run"
But honestly I'm fine with
runBlocking
too 😂 I don't believe this will ever change anyway
👍 1
j
🤣 yeah, this is all pie-in-the-sky talk.
^^ Yeah, that makes sense.
d
I cannot think of any good name. Before any name would make sense one must deeply understand the distinction between suspend and blocking that is not easy (at least was not for me and anyone I have talked to -- who come from knowing threads but not coroutines - the concept of suspend is totally foreign) This is not helped by most of the coroutine docs mixing the 2 concepts in sample code Such as comments like "Simulate a blocking operation" or "Simulate a long running operation" delay(10000) -->>> This does no such thing .. but if Thread.sleep(10000) were used the code would not work IMHO there is a fundimeneatl , and difficult to solve problem of documenting coroutines for people not already deeply familiar. Especially when the 'environment' they are used in today is almost certainly a program not already fully coroutine friendly The docs on most of the coroutine tutorials and APIs only makes sense in the case where the program is Already fully coroutine friendly where the appropriate scope is in context, where the caller and callee code is already integrated to coroutines, where 3rd party libraries are all suspending not blocking IO etc -- So the docs (appear to) start out with the premise that they are people new to coroutines how to Use coroutines in an already coroutine friendly application, when it is most likely not the case, rather its most likely that new users are trying coroutines for the first time in an application previously written in a non-coroutine way with non-coroutine friendly libraries. The end result is that most of the sample code simply does not work outside its playground, and that that its not at all obvious why. Attempting to make coroutines simpler by describing them 'look like other familiar concepts' as the docs do in many places, may be more harmful then useful. I am probably not a 'typical user' -- my opinion is that the single most important concept of coroutines is the deep understanding of the difference between blocking and suspending. Without that, even the most basic use will at best mysteriously misbehave, and at worse be a sleeping time bomb. (appear to work as expected but bizarrely misbehave at the most opportune time for disaster) I say this from my own personal experience as well as working in teams where the team members had studied kotlin coroutines, used them somewhat (for > 1 year) yet had completely wrong understandings of how they behaved in the most basic ways. Concepts like "a suspend function guarantees the function is executed in another thread", or "wrapping a 3rd part HTTP library with a async() call makes it a suspending function", or "async guaranteed that the function will not block the current thread" or "launch guarantees that the function runs on the UI thread" And all would answer differently when asked the difference between 'blocking' and 'suspending' -- creatively brilliantly different often, and all different from the meaning in kotlin coroutines. 'concurrent' is another word that generally means something totally different to different people -- passionately different -- I have yet to find a single person (from experienced programmers > 20 yrs ) whos definition of 'concurrent' was even close to the meaning used in kotlin coroutine docs. Every person I have talked to passionately believes that "It is obvious" and "There is no other meaning" of concurrent then a synonym for parallel. To my thinking -- the names of functions are irrelvent until we can share the same meaning of core terminology -- doesnt matter at all if we agree runBlocking means "run While Blockign" if we dont all share the same meaning of "Blocking" .
j
@DALDEI This is quite interesting. I remember being confused about suspending vs blocking at first as well. It wasn't clear for me what it meant to "suspend" (as opposed to block) if the code after the call was still waiting anyway. That being said, the docs were not the same I believe at that time. I would be interested in having actual links to the places of the docs you're referring to. If it's simulating blocking calls with
delay()
it's most likely a mistake, and needs to be fixed. We could contribute to the docs in this respect. I don't quite share the opinion that the docs assume a coroutine-friendly environment, but I would need to re-read them to be sure. Any specific pointers about pieces of the docs you find confusing would be very helpful here.
d
Here is one example, the first paragraph of : https://kotlinlang.org/docs/composing-suspending-functions.html#sequential-by-default I am not sayiung the docs are incorrect, rather they can be misleading. "Assume that we have two suspending functions defined elsewhere that do something useful like some kind of remote service call or computation. We just pretend they are useful, but actually each one just delays for a second for the purpose of this example:" Reading that, I, as a (in the past) coroutine-ignorant, threading experienced programmer interpret the above as describing either a computationally expensive or a blocking IO function. "something useful" , "remote service call or computation" -- to simulate that I'd either have a spin-loop or a blocking IO or Sleep call. "just delays for a second for the purpose:" -- I would interpret 'delay' in the conventional sense -- a function that takes time to process and return. The 'stub' for that would be Thread.sleep() or a computational loop. Something that *definitely blocks the calling thread. If one is new to coroutines -- *all such functions would block the calling thread. But the example cleverly uses:
suspend
*fun* doSomethingUsefulOne(): Int {
delay(1000L) // pretend we are doing something useful here
*return* 13
}
It is this exact example that a coworker (reasonably, IMHO) interpreted the above as equivilent to: suspend fun doSomethingUsefulOne(): Int { do-blocking-HTTP-call() // pretend we are doing something useful here return 13 } This 'works' well enough that you have to do more then a basic unit test to discover that it does not prevent the calling thread from blocking I will speculate that one can classify ones experience in kotlin coroutines by the answer to the question: is 'doSomethingUsefulOne' blocking or suspending ? Then further refined by 'Exactly what did you mean by that'
🙏 1
Reading the above I would (and did) ascribe the meaning to be that if one adds the 'suspend' modifier to a function it will convert it into a 'suspending function' (it does) -- and therefore can be called 'concurrently' (in some meanings yes it can) -- and that it woujld not 'stop the calling coroutine' (hard to say -- depends on what one means by 'stop') -- and that it does 'not block the calling thread' -- and that one would be able to use that function in a async way, to do mutiple HTTP calls without 'using threads. If you run the sample code -- thats precisely what it does (!yea!) but if you then extrapolate to 'something useful' -- there is effectively no useful code that would behave like the sample would have one believe. In that if I, and the libraries and programs I am using , are not deeply integrated to kotlin coroutines (and there are very few today that are) -- nothing -- nothing -- that I would need to do in my app would behave in the way described. Even in the kotlin std lib there are extremely few functions that behave like 'delay' does. all the standard IO classes are traditional thread-blocking classes, the networking classes, the computational libraries - all traditional single threaded blocking functionality. It is very difficult (at first) to find any 'something useful' library that behaves in any way like this sample. This is repeated many many times. I understand why - it is precisely because there is nearly no other existing functions like delay to use for an example of suspension -- And that is the problem today. Maybe someday we may have a fully coroutine integrated stdlib such that it behaves like GO IO does, deeply integrated into goroutines so that the user does not need to understand how it all works -- where the difference between 'blocking' and 'suspending' is academic. but not today. Today -- in fact the primarily reason I find Kotlin to be the most valuable language available presently, is precisely because it is designed for integration with other languages -- it is not 'academic' in the sense that Scala is nor in isolation like Go -- it is designed to co-mingle with non-kotlin programs instead of 'all or nothing' -- that is one of the most compelling features -- yet the coroutine documentation is almost entirely written like the above reference, only really meaningful and useful as written 'in the future' when 'everything is implemented with deep integration to coroutines'