https://kotlinlang.org logo
Channels
100daysofcode
100daysofkotlin
100daysofkotlin-2021
advent-of-code
aem
ai
alexa
algeria
algolialibraries
amsterdam
android
android-architecture
android-databinding
android-studio
androidgithubprojects
androidthings
androidx
androidx-xprocessing
anime
anko
announcements
apollo-kotlin
appintro
arabic
argentina
arkenv
arksemdevteam
armenia
arrow
arrow-contributors
arrow-meta
ass
atlanta
atm17
atrium
austin
australia
austria
awesome-kotlin
ballast
bangladesh
barcelona
bayarea
bazel
beepiz-libraries
belgium
benchmarks
berlin
big-data
books
boston
brazil
brikk
budapest
build
build-tools
bulgaria
bydgoszcz
cambodia
canada
carrat
carrat-dev
carrat-feed
chicago
chile
china
chucker
cincinnati-user-group
cli
clikt
cloudfoundry
cn
cobalt
code-coverage
codeforces
codemash-precompiler
codereview
codingame
codingconventions
coimbatore
collaborations
colombia
colorado
communities
competitive-programming
competitivecoding
compiler
compose
compose-android
compose-desktop
compose-hiring
compose-ios
compose-mp
compose-ui-showcase
compose-wear
compose-web
confetti
connect-audit-events
corda
cork
coroutines
couchbase
coursera
croatia
cryptography
cscenter-course-2016
cucumber-bdd
cyprus
czech
dagger
data2viz
databinding
datascience
dckotlin
debugging
decompose
decouple
denmark
deprecated
detekt
detekt-hint
dev-core
dfw
docs-revamped
dokka
domain-driven-design
doodle
dsl
dublin
dutch
eap
eclipse
ecuador
edinburgh
education
effective-kotlin
effectivekotlin
emacs
embedded-kotlin
estatik
event21-community-content
events
exposed
failgood
fb-internal-demo
feed
firebase
flow
fluid-libraries
forkhandles
forum
fosdem
fp-in-kotlin
framework-elide
freenode
french
fritz2
fuchsia
functional
funktionale
gamedev
ge-kotlin
general-advice
georgia
geospatial
german-lang
getting-started
github-workflows-kt
glance
godot-kotlin
google-io
gradle
graphic
graphkool
graphql
graphql-kotlin
graviton-browser
greece
grpc
gsoc
gui
hackathons
hacktoberfest
hamburg
hamkrest
helios
helsinki
hexagon
hibernate
hikari-cp
hire-me
hiring
hiring-french
hongkong
hoplite
http4k
hungary
hyderabad
image-processing
india
indonesia
inkremental
intellij
intellij-plugins
intellij-tricks
internships
introduce-yourself
io
ios
iran
israel
istanbulcoders
italian
jackson-kotlin
jadx
japanese
jasync-sql
java-to-kotlin-refactoring
javadevelopers
javafx
javalin
javascript
jdbi
jhipster-kotlin
jobsworldwide
jpa
jshdq
juul-libraries
jvm-ir-backend-feedback
jxadapter
k2-early-adopters
kaal
kafka
kakao
kalasim
kapt
karachi
karg
karlsruhe
kash_shell
kaskade
kbuild
kdbc
kgen-doc-tools
kgraphql
kinta
klaxon
klock
kloudformation
kmdc
kmm-español
kmongo
knbt
knote
koalaql
koans
kobalt
kobweb
kodein
kodex
kohesive
koin
koin-dev
komapper
kondor-json
kong
kontent
kontributors
korau
korean
korge
korim
korio
korlibs
korte
kotest
kotest-contributors
kotless
kotlick
kotlin-asia
kotlin-beam
kotlin-by-example
kotlin-csv
kotlin-data-storage
kotlin-foundation
kotlin-fuel
kotlin-in-action
kotlin-inject
kotlin-latam
kotlin-logging
kotlin-multiplatform-contest
kotlin-mumbai
kotlin-native
kotlin-pakistan
kotlin-plugin
kotlin-pune
kotlin-roadmap
kotlin-samples
kotlin-sap
kotlin-serbia
kotlin-spark
kotlin-szeged
kotlin-website
kotlinacademy
kotlinbot
kotlinconf
kotlindl
kotlinforbeginners
kotlingforbeginners
kotlinlondon
kotlinmad
kotlinprogrammers
kotlinsu
kotlintest
kotlintest-devs
kotlintlv
kotlinultimatechallenge
kotlinx-datetime
kotlinx-files
kotlinx-html
kotrix
kotson
kovenant
kprompt
kraph
krawler
kroto-plus
ksp
ktcc
ktfmt
ktlint
ktor
ktp
kubed
kug-leads
kug-torino
kvision
kweb
lambdaworld_cadiz
lanark
language-evolution
language-proposals
latvia
leakcanary
leedskotlinusergroup
lets-have-fun
libgdx
libkgd
library-development
lincheck
linkeddata
lithuania
london
losangeles
lottie
love
lychee
macedonia
machinelearningbawas
madrid
malaysia
mathematics
meetkotlin
memes
meta
metro-detroit
mexico
miami
micronaut
minnesota
minutest
mirror
mockk
moko
moldova
monsterpuzzle
montreal
moonbean
morocco
motionlayout
mpapt
mu
multiplatform
mumbai
munich
mvikotlin
mvrx
myndocs-oauth2-server
naming
navigation-architecture-component
nepal
new-mexico
new-zealand
newname
nigeria
nodejs
norway
npm-publish
nyc
oceania
ohio-kotlin-users
oldenburg
oolong
opensource
orbit-mvi
osgi
otpisani
package-search
pakistan
panamá
pattern-matching
pbandk
pdx
peru
philippines
phoenix
pinoy
pocketgitclient
polish
popkorn
portugal
practical-functional-programming
proguard
prozis-android-backup
pyhsikal
python
python-contributors
quasar
random
re
react
reaktive
realm
realworldkotlin
reductor
reduks
redux
redux-kotlin
refactoring-to-kotlin
reflect
refreshversions
reports
result
rethink
revolver
rhein-main
rocksdb
romania
room
rpi-pico
rsocket
russian
russian_feed
russian-kotlinasfirst
rx
rxjava
san-diego
science
scotland
scrcast
scrimage
script
scripting
seattle
serialization
server
sg-user-group
singapore
skia-wasm-interop-temp
skrape-it
slovak
snake
sofl-user-group
southafrica
spacemacs
spain
spanish
speaking
spek
spin
splitties
spotify-mobius
spring
spring-security
squarelibraries
stackoverflow
stacks
stayhungrystayfoolish
stdlib
stlouis
strife-discord-lib
strikt
students
stuttgart
sudan
swagger-gradle-codegen
swarm
sweden
swing
swiss-user-group
switzerland
talking-kotlin
tallinn
tampa
teamcity
tegal
tempe
tensorflow
terminal
test
testing
testtestest
texas
tgbotapi
thailand
tornadofx
touchlab-tools
training
tricity-kotlin-user-group
trójmiasto
truth
tunisia
turkey
turkiye
twitter-feed
uae
udacityindia
uk
ukrainian
uniflow
unkonf
uruguay
utah
uuid
vancouver
vankotlin
vertx
videos
vienna
vietnam
vim
vkug
vuejs
web-mpp
webassembly
webrtc
wimix_sentry
wwdc
zircon
Powered by
Title
a

Advitiay Anand

04/14/2022, 9:37 AM
Noob level question: What is the difference between suspend functions and normal functions when calling from a coroutine? I mean, even if you call normal functions, they still run asynchronously, right?
j

Joffrey

04/14/2022, 9:39 AM
I would say the opposite. When you're inside a coroutine, calling
suspend
functions and regular functions feels the same - they are run sequentially, in a manner that appears synchronous as far as the code is concerned (the next line of code will be executed after the function returns, and can access the return value of the called function). If you're calling a suspend function, the current thread may be freed to go execute some other work (we call this a suspension point because the coroutine may be suspended when making this call), so it's behaving asynchronously behind the scenes despite the synchronous aspect of the code. However, if you call a regular function, the thread is actually blocked until the function returns, so there is a difference.
m

mbonnin

04/14/2022, 9:41 AM
Calling a
suspend
function creates a suspension point. Calling a regular function will always execute in the same callstack
a

Advitiay Anand

04/14/2022, 9:45 AM
Yes, I should rephrase. Regardless of the
suspend
keyword, the code inside a coroutine is run sequentially. However, because we're inside a coroutine, the entire block of code can be run on a different thread (using Dispatchers) For common use cases, like Room, shouldn't this be enough? How does using
suspend
make a difference? An example would be very much preferable. Thanks
e

ephemient

04/14/2022, 10:19 AM
when calling a suspend function, your current function will be suspended. for example,
withContext(IO) { database() }
will free up the current thread to run other coroutines while IO is proceeding, while
database()
will block the current thread if it is not a suspend function, even if it's using another thread to perform its work
a

Advitiay Anand

04/14/2022, 11:30 AM
So if we are in the main thread and execute:
CoroutineScope(<http://Dispatchers.IO|Dispatchers.IO>).launch { database() }
where database is NOT a suspend function It will still block the main thread?
I'm confused with what a suspension point really means. I'm assuming that whenever a suspend function call is made, that counts as a suspension point. And the program would first deal with the non-suspending code in the given order before dealing with the suspended calls.
j

Joffrey

04/14/2022, 11:40 AM
Not sure what you mean by
CoroutineScope(<http://Dispatchers.IO|Dispatchers.IO>) { database() }
, as this doesn't compile. If you meant
CoroutineScope(<http://Dispatchers.IO|Dispatchers.IO>).launch { database() }
then you're creating another coroutine here, so things are much different.
launch
is not a suspend function itself, and it's actually how you make asynchronous calls, so the code after the call to
launch
will run concurrently with
database()
. The main thread would not be blocked, but the current coroutine wouldn't even be suspended anyway. If you meant
withContext(<http://Dispatchers.IO|Dispatchers.IO>) { database() }
, like @ephemient suggested above, it's a different story.
withContext
itself is suspending, so the current coroutine (which calls
withContext
) suspends when making the call (that's a suspension point), and the code after
withContext
will only run once the
database()
call is done. However, that doesn't mean the main thread will be blocked. Instead, the main thread just stops executing this specific coroutine, but it can run other coroutines while the current coroutine is waiting for the result of
database()
. Once
database()
completes (on whatever IO thread it was using), the main thread can resume executing the initial coroutine.
💡 1
Ok I think I'm starting to understand your initial question, now.
Assuming you have this code:
fun doStuff() {
    someScope.launch {
        someFunction()
    }
    doStuffConcurrently()
}
It doesn't matter (much) whether
someFunction
is suspend or not - I think that was your question. What will happen either way here is that
launch
is called, and
doStuffConcurrently()
will run concurrently with whatever is inside the
launch { ... }
block.
💡 1
a

Advitiay Anand

04/14/2022, 11:56 AM
Thank you for going in such detail. Then, in a nutshell, suspend is just sugar syntax for callbacks? And suspension points have nothing to do with multi-threaded programming. Rather they only change the order in which the code is executed, albeit on the same thread. More importantly, even if we call a suspend function on the main thread, it only blocks that specific coroutine but leaves the thread open to run other coroutines concurrently. Is that correct?
:yes: 1
j

Joffrey

04/14/2022, 12:01 PM
Expanding on the example above, things may behave slightly differently depending on the dispatchers used to run this code. Let's assume
doStuff()
is called on the main thread, and
someScope
has a dispatcher that runs stuff on the
Dispatchers.Main.immediate
(which is what happens if you use Android's built-in scopes like
lifecycleScope
or
viewModelScope
). If
someFunction
is not suspend, it has to block the thread running the
launch
block until it completes, which means blocking the main thread here. Because of
Dispatchers.Main.immediate
the launch body will not only run on the main thread but also run first (before
doStuffConcurrently
). So even though
doStuffConcurrently()
theoretically runs concurrently with the
launch
body, it will not be able to run until
someFunction
is done in this specific case due to thread starvation (because
someFunction
block the main thread). That's why if
someFunction()
does blocking I/O operations, it's better to override the dispatcher to make sure it runs on another thread or pool of threads. This is what
<http://DIspatchers.IO|DIspatchers.IO>
is for. Now, if
someFunction
is
suspend
, the call to it is a suspension point in itself, but also
someFunction
itself may contain suspension points inside (if it calls other suspend functions). Having multiple suspension points allows pieces of code from concurrent coroutines to interlace even when run on the same thread.
Then, in a nutshell, suspend is just sugar syntax for callbacks?
You can see it this way, yes. It's a way to write async code in a sequential way, when it would usually use callbacks. It's not implemented with actual callbacks, though, but with state machines instead.
And suspension points have nothing to do with multi-threaded programming. Rather they only change the order in which the code is executed, albeit on the same thread.
Suspension points decide when a coroutine can suspend. Suspending a coroutine means that the coroutine is set aside and frees the thread that was running it, so that thread can go execute other coroutines. You can see suspension points as a way to slice the coroutine's code into pieces (like sub-tasks), and those pieces are sort of the units that are executed by a thread. Those sub-tasks are guaranteed to run in the order they appeared in the coroutine, but they are not guaranteed to run on the same thread. If the dispatcher running the coroutine happens to be a multi-threaded dispatcher, each sub-task can theoretically be run by a different thread. Sorry if that sounds very theoretical, maybe an example would help better.
💡 1
More importantly, even if we call a suspend function on the main thread, it only blocks that specific coroutine but leaves the thread open to run other coroutines concurrently.
In general yes it's a good rule of thumb. Well-behaved suspend functions should be main-friendly. However, in theory it depends on the implementation of the suspend function. Adding the
suspend
keyword doesn't magically solve problems. For instance:
// don't do this!
suspend fun notReallySuspend() {
    Thread.sleep(2000)
}
This function is marked
suspend
but it actually still blocks the thread. The IDE would probably warn you here that
suspend
is redundant. Actual well-behaved suspend functions should instead either call other suspend functions (the most common), or explicitly suspend the coroutine and resume it (often when integrating with callback-based APIs).
💡 2
s

streetsofboston

04/14/2022, 12:17 PM
@Advitiay Anand Yup, suspend is like a callback that is called with a result (a value or Unit) or with an exception. That callback is a Continuation<T> The compiler allows the Continuation to appear on the left side of the expression (instead of on the right side as an input parameter), as a return value, allowing for a sequential/imperative programming style. suspend fun someFun(...): T is rewritten as fun someFun(..., Continuation<T>)
👀 1
j

Joffrey

04/14/2022, 12:18 PM
This is still the best source to understand this IMO if you're interested:

https://www.youtube.com/watch?v=YrrUCSi72E8

:thank-you: 1
s

streetsofboston

04/14/2022, 12:20 PM
That video is great, quite detailed! 😀
j

Joffrey

04/14/2022, 12:21 PM
☝️ yep that doc too, although it goes into even more details, so brace yourself!
e

ephemient

04/14/2022, 12:24 PM
there are callbacks but it's not the type of continuation-passing you would write by hand. each suspend function is turned into a state machine which will proceed to the next suspension point each time it is resumed, as well as being able to both call functions inline or dispatch them
s

streetsofboston

04/14/2022, 12:30 PM
Yup, seeing it as callbacks is a very simplified view. 😀 But that's the 10,000 feet view of 'suspend' for me. That a 'suspend' becomes Continuation is just one aspect. The guarantee that suspend funs can be called sequentially/imperatively in a safe way needs much more, like the state machine (with 'goto' statements) you describe.
e

ephemient

04/14/2022, 12:32 PM
yep, the "Continuation" aspect really shouldn't matter for how you use coroutines. suspension points as locations in the source code where execution can switch from one callstack to another, but otherwise everything that looks like sequential code will execute sequentially
💯 1