i just stumbled across this sentence in the `corou...
# coroutines
t
i just stumbled across this sentence in the
coroutineContext
documentation "By convention, should contain an instance of a job to enforce structured concurrency.". What does it mean? It'spossible that a context does NOT contain a job? what are the consequences if a job instance is missing?
d
I haven't checked but maybe when you use
suspend fun main(args: Array<String>)
.
Since you don't need to use
kotlinx.coroutines
for that and that's where
Job
comes from.
If
Job
is missing, cancellation can't happen. (I'm not sure if I should say "easily" or "not at all" here).
m
Dominic is up 24/7 ready to help
d
😅
g
Just want to add that it's not related on
suspend main
, because it doesn't have scope so not related to structured concurrency and scopes, this convention only about coroutine scopes implementation, because if it doesn't have Job, it doesn't have child/parent relationship and cannot be cancelled
d
Yeah, suspend main runs a simple event loop, that's it
When kotlinx coroutines isn't present, there is no job to speak of unless someone made their own library
But CoroutineContext is part of stdlib, not kotlinx
t
Ahh, so the Job is the thing that makes structured concurrency really possible but is introduced by the extension only?
g
the extension?
t
sorry, i mean kontlinx.coroutine
g
Coroutines cancellation, Job, Structured Concurrence, CoroutineScope all those things are library features of kotlinx.coroutine, it’s not a part of the language
But suspend function and CoroutineContext is part of the language
t
so basically the "native" parts are much like java's
Thread
from a structuring point of view?
g
not sure how it’s comparable
t
A
Thread
is basically fire and forget
g
See, this is whole coroutines API that available in the language and stdlib https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.coroutines/index.html
No
because suspend function is not fire and forget, it always returns value or throws exception
it just don’t have cancellation, which has own semantics and just based on the primitives provided by the language
t
now i do interpret the essence of structured concurreny is being able to cancle a coroutine or a coroutine scope... but i feel this is wrong 🤔
'm really having problems wrapping my head arund coroutines and structured concurrency 😞
g
see, you need structured concurrency when you have some background or parallel work, like async/launch, right?
t
yes!
g
so, because there is no way to start background coroutine, async and launch do not exist in stdlib, than no probblem %)
t
and managing threads has aways been PITA
g
coroutines has nothing to do with managing threads by itself
t
ywah but that's what you have to do in java 🙂
g
this is just a feature of kotlinx.coroutines which provides dispatcher API
t
how would we use coroutines on a world without kotlinx.coroutines? what would be there use?
oh sequences are part of the stdlib
g
ywah but that’s what you have to do in java
not sure what you mean. See, coroutines by itself and their basic primitives in stdlib has nothing to do with threading, it’s language feature to simplify writing asynchronous code in a sequential style, that’s all, it provides you primitives to convert callback to suspend fucntion Threading, cancellation, scopes, async, launch etc are features of kotlinx.coroutines library which provides high level primitves based on Kotlin Coroutines
how would we use coroutines on a world without kotlinx.coroutines? what would be there use?
They are pretty low level, and this is whole point of them. For example #arrow (and some other libraries) use coroutines but do not use kotlinx.coroutines Again, it’s just primitives used by other libraries
t
you are right - i should really change my view on coroutines. i alway fall back to the concurrency-and-multithreading-viewl
g
how would we use coroutines on a world without kotlinx.coroutines? what would be there use?
Just a very simple example, which has nothing to do with threading or cancellation. Imagine you have this asynchronous API:
Copy code
fun <T> sendRequest(onSuccess: (T) -> Unit, onError: (Throwable) -> Unit) {
    TODO()
}
so you can see how to use it, do something in onSuccess or onError, but if you have nested calls or you have some more complicated behavior you have all usual async programming probblems
now you convert it to suspend function without dependency on kotlinx.coroutines:
Copy code
suspend fun <T> awaitRequest(): T {
    return suspendCoroutine { cont ->
        sendRequest<T>(
                onSuccess = { cont.resume(it) },
                onError = { cont.resumeWithException(it) }
        )
    }
}
and use it with suspend main:
Copy code
suspend fun main() {
    val result = awaitRequest<T>()
    println(result)
}
See, no callbacks, no, nesting, only stdlib APIs
also no cancellation, but it’s the same as original, callback based, API
t
i see, thank you very much 🙂
Since the initial rollout of Kotlin coroutines as an experimental feature in Kotlin 1.1 in the beginning of 2017 we’ve been working hard to explain the concept of coroutines to the programmers who used to think of concurrency in terms of threads, so our key analogy and motto was “coroutines are light-weight threads”.
i think that's why i always think about threads when i think about coroutines 😉
g
yes, and it’s covered by kotlinx.coroutines, which has own “light-weight thread” implementation based on coroutines
t
well, i think roman talks about coroutines in context of the stdlib here... it's hard to see a didfference here, when you are new to coroutines 😕
g
no need to see difference, just use kotlinx.coroutines %) This is the way to use them, especially if you just started
t
😄
g
stdlib coroutines just too low level for every day usage, and more like a primitive for library authors
The main thing that you should understand what is suspend function
t
but then again i come back to my kind of initial question: why are `Job`s the crucial point in applying structured concurrency?
https://medium.com/@elizarov/structured-concurrency-722d765aa952 e.g. doesn't even mention Jobs, yet on almost every api the has to do with
CoroutineScope
or
coroutineContext
it's written that any context should contain a job to enforce structured concurrency
g
Strucutred concurrency is just approach to management of asynchronous background task lifecycle. To manage lifecycle (aka cancel it when task is not needed anymore to free resources, avoid leaks etc) you need some abstraction for cancellation. And Job is this abstraction. You have (in simplest and most common case) 1 Job per Scope, this job become parent for all child coroutines launched from this scope, and when you cancel scope all child coroutines are also cancelled
Note that an implementation of CoroutineScope also defines an appropriate coroutine context for your UI updates. You can find a full-blown example of CoroutineScope implementation on its documentation page.
https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-scope/index.html
it’s just simple introduction, mostly focused on use case with parallel decomposition, where you don’t need to work with Job directly
it’s written that any context should contain a job to enforce structured concurrency
So, I don’t see contradiction here
t
ok.. i think i start to grasp it... from the beginning coroutines have had a more or less well defined scope they live in, but cancellation was the missing piece at least when it comes to longer running backround tasks that we might or might not want to cancel at some point in time?
g
StructuredConcurrency just designed in a way when you can run background task only in some scope (it can be global, but you have to be explicit about it and use
GlobalScope.launch
), and this scope can be cancelled
Imagine if you need scope for every started thread, like you cannot start a new thread without ThreadPool, it’s essenttially the same. Than to stop all threads you just cancel thread pool
t
but the scope can only be canceled if its context contains a
Job
, right?
g
That is the thing, you cannot cancel scope by itself
it’s just an interface with
coroutineContext
property
But there is extension function
CoroutineScope.cancel()
just check what it does
So scope cancellation is just a convention
t
error("Scope cannot be cancelled because it does not have a job: $this")
ha
so maybe finally my understanding allows me to make correct assumptions 😄
thank you very much, @gildor i have much to think about now 🙂
👍 1