https://kotlinlang.org logo
Docs
Join the conversationJoin Slack
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
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
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
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
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 Linen
coroutines
  • a

    Andrea Giuliano

    10/02/2021, 12:32 PM
    Hi all, I was trying to find an easy implementation to launch multiple api calls in an async block but instead of waiting for all of them, just wait for the first n that return, cancelling the others. I know I can do something like
    val results = endpoints.map {
                async {
                    client.call(it)
                }
            }
    results.awaitAll()
    but of course that will wait for all the calls to come back, not just the first N. I know I could solve this problem by using channels and subscribe n topics waiting for them, but I was wondering if there is a counterpart with async/await as well? Also, as a side note, one thing I’ve noticed is that if the
    client
    is blocking (imagine a Thread.sleep) the async is called sequentially and I lose completely the parallelism..what am I doing wrong?
    e
    • 2
    • 30
  • u

    uli

    10/02/2021, 4:08 PM
    Try select
    a
    • 2
    • 2
  • u

    uli

    10/02/2021, 4:09 PM
    The blocking issue is probably caused by a single threaded dispatcher
    a
    • 2
    • 2
  • h

    Husam

    10/02/2021, 6:18 PM
    are there example impls of recommended patterns for achieving something similar to this intra-process EventBus but with coroutines (or project reactor)? #events https://guava.dev/releases/snapshot-jre/api/docs/com/google/common/eventbus/EventBus.html
    a
    • 2
    • 24
  • r

    Ravin Jain

    10/02/2021, 6:39 PM
    Is there a way for handling loss of items If a MutableStateFlow which is emitting from background thread faster than the subscriber can collect in main thread?. Using .value to set the value and copying the value from previous state value.
    l
    • 2
    • 6
  • s

    Stylianos Gakis

    10/02/2021, 6:48 PM
    I have a question regarding if I can get into any “trouble” using async in this context (Using async to do 2 stuff in parallel to not unnecessarily wait for some network request). I am always unsure about how cancellations etc. are handled so I am posting this question here, more in thread🧵
    n
    e
    • 3
    • 4
  • d

    DALDEI

    10/03/2021, 3:56 AM
    A few years into Kotlin and Coroutines -- starting in backend/server now also in android -- I still struggle with a fundamental design problem - how to 'cross the coroutine divide' ... Its a simple and complex problem : Given: one shouldn't make all functions suspend - therefore you will have a mix of suspend and non-suspend functions calling *each other * Given: <in my current domians> -- the 'majority/main' application is not 'coroutine based' -- its "traditional". Coroutines are added as needed, but the scale of existing code (custom and 3rd party, java/JVM and kotlin) is such that its not practical (yet) to have a coherant coroutine architecture and convention everything follows: Given: one should 'Avoid GlobalScope' -- and given its not trivial to create app coroutine scopes such that they are all self-contained -- real world code is not that pretty. Given: It is not obvious or easy to to tell if a non-suspend function was called by a suspend function, or is 'running in a coroutine' without passing down a CoroutineScope or context to every function (see 2.) Given: 'traditional' non-coroutine aware functions expect functions to only return when complete -- quite often you have to (and should) scope coroutine calls so they are blocking from the perspective of the parent non-coroutine-aware caller. This all leads to the need to have a good convention to follow for: How to create blocking scopes from within non-coroutine-aware functions. (without GlobalScope?) How to know if a non-suspend function is in fact in the call-chain of a suspend function or coroutine, so for example, you could call a suspend function without having to launch a new coroutine -- in what scope ? ( presuming the the caller is non-coroutine-aware it will not generally pass in a coroutine scope for you to use --) A few examples: Android: in onCreate() {} I need to call a suspend function (maybe it pre-existed as a suspend even if I dont need suspension for this call, 'suspend' is in it signature) -- But -- I should not return from onCreate until all its work is done. How ? <what scope>?.<what builder> { call suspend function } return // Do NOT return until above is complete If I have a scope (like lifecycleScope) life is easier -- Much, but if not, Then I have to make or borrow one, and then arrange somehow so its scope/lifecycle is well behaved. Could use GlobalScope. shouldnt probably but maybe 'this time' ? What builder? both launch and async -- are async so I have to wait for them. I cant do that outside the scope (the return statement) -- so where? Punt to yet another scope (as seen in sample code) val job = <scope>.launch { .. } <????> { job.join() } // This solves nothing -- Now I need to know how to wait for ??? Is 'runBlocking' the answer ? Its 'to be avoided' -- but precisely under which conditions? ------ Server side: A web request is processed and calls some "doGET" method -- from legacy java code expecting blocking behaviour. fun doGET() { // same question .. what scope ? launch/async ? runBlocking? } ----- The BIGGER problem In comes 'typical mature application' of some thousands of functions written over a decade by different people long gone .. Kotlin/Coroutines was introduced by some a while ago, haphazardly, Now we have a mess because its not at all clear which non-suspend functions are called by suspend functions upstream top() { module() library(callback) callback() --->| back to module onCallback() -> library() subsystem() -> Fancy Coroutine Stuff over blocking IO --> callback() // Developer comment about #@#%% old code and so on -- at any given point maybe a suspend fun maybe not, with no easy/quick way of telling if it was or could be called* by a coroutine or suspend func, or downstream, is there some 5 level deep suspend func calling back into the module without its context to hang on to ? Can one block the thread ? what other choice is there ? I end up having to solve these one by one ad-hoc with no real global architecture or understanding of what could go wrong. e.g. how bad is this in a library function: fun someFunHasToCallASuspendFun() { GlobalScope.runBlocking{ aQuickSuspendFunc() } } To avoid that Ive had to make massive invasive changes to pass in the appropriate scope from 5 levels up and then that can have a huge blast radius (let alone the ire of my teammates for such a huge change for a 'simple fix') Alternatively .. I've added a few 'suspend' modifiers to the fun and its parents etc. But the effect is very much like C++ "const rot" Once you add one suspend, then its parent has to be suspend, then everything that calls it has to also, then their parents -- it can and does explode quickly until often ALL methods have to be suspend just to call 1 10 levels deep. Another idiom: try to establish some well named 'global scopes' that are not 'GlobalScope' -- at some point this just seems silly -- inventing all these scopes just to avoid 1 -- when they do nothing different. It also requires opening up all libraries and modules to more dependencies, to the point sometimes of having to combine libraries just to share a common scope. The closest 'to ideal' I have found is Android Only, and UI Thread Only, <scope>.launch( Dispatchers.Main.immediate ){} If Im really careful I don't have to wait for this -- it inherently blocks the caller -- with some care even child coroutines are managed. But it seems so wrong that this is subtle and complicated -- surely this is the key difficulty in integrating coroutines -- yet most of the docs are written in a way that presumes everything is already coroutine aware - leaving the 'corner cases' as minor issues someone else can work out. Any commentary welcome - am I missing something big here ? Surely theres a good strategy to manage this problem
    :thread-please: 6
    s
    e
    +2
    • 5
    • 29
  • t

    theapache64

    10/03/2021, 1:06 PM
    🌊 How do I test a
    viewModelScope
    controlled
    SharedFlow
    ? 🤔 😒tackoverflow: More details: https://stackoverflow.com/questions/69425075/how-do-i-test-sharedflow-controlled-by-a-viewmodelscope
    k
    m
    • 3
    • 2
  • m

    marcinmoskala

    10/03/2021, 5:11 PM
    Hey, I am testing the difference between the performance of different dispatchers. I got the same results before, but now I confirm it with a benchmark - CPU intensive operation is faster with more threads then Dispatchers.Main, but (on IO the fastest, on executor with 100 threads it is faster than on Default). My only explanation is that it is because those threads are fighting with other threads on my computer, like IntelliJ, browser etc. Is it so, or is there any other explanation?
    d
    e
    • 3
    • 10
  • s

    Slackbot

    10/03/2021, 5:11 PM
    This message was deleted.
    :thread-please: 2
    e
    m
    • 3
    • 4
  • u

    ursus

    10/03/2021, 5:12 PM
    Hi,
    Activity {
    	private val scope = MainScope()
    
    	onCreate {
    		scope.launch {
    			userDao.user
    				.collect {
    					if (it == null) {
    						resetBackstack()
    					}
    				}
    		}
    	}
    }
    
    SomeScreenViewModel {
    	private val scope = MainScope()
    
    	fun init() {
    		scope.launch {
    			userDao.user
    				.collect {
    					...
    				}
    		}
    	}
    }
    UserDao.user
    emits on its io dispatcher, both activity and some screen viewmodel are driven by main threaded scope (android) Occasionally in crashlytics I see crashes related, basically to the fact that the screen collects the emit first Is there a way I could enforce order of collections? (I presume if it were a replayed hot Flow, then order would be the order of subscriptions, however I presume that posting to main thread will always be race-y?)
    m
    d
    • 3
    • 5
  • i

    int02h

    10/04/2021, 9:19 AM
    Hello! I have a few depencies in my Android project which transitively depends on
    kotlin-coroutines-core
    different versions. I’ve used
    resolutionStrategy
    Gradle feature to force using the same 1.4.3 version of coroutines but during release build I get the R8 (ProGuard) error like:
    ERROR:/Users/*****/***********/app/build/intermediates/module_and_runtime_deps_classes/preliveGooglePlayRelease/base.jar: R8: Type kotlinx.coroutines.debug.AgentPremain is defined multiple times: /Users/*****/***********/app/build/intermediates/module_and_runtime_deps_classes/preliveGooglePlayRelease/base.jar:kotlinx/coroutines/debug/AgentPremain.class, /Users/*****/***********/voip_ui/build/intermediates/module_and_runtime_deps_classes/preliveGooglePlayRelease/feature-voip_ui.jar:kotlinx/coroutines/debug/AgentPremain.class
    As I see
    AgentPremain
    indeed contained in both
    kotlin-coroutines-core
    and
    kotlin-coroutines-core-jvm
    with sligtly different byte code. The interesting thing is that
    kotlin-coroutines-core
    introduces the dependency to
    kotlin-coroutines-core-jvm
    according to Gradle dependency graph:
    +--- org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.7 -> 1.4.3
    |    \--- org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.4.3
    and the POM file says the same:
    <dependency>
    	<groupId>org.jetbrains.kotlinx</groupId>
    	<artifactId>kotlinx-coroutines-core-jvm</artifactId>
    	<version>1.4.3</version>
    	<scope>compile</scope>
    </dependency>
    I wonder how two dependent libraries could have the same class but with different code inside them? Maybe I do something wrong?
    ✅ 1
    l
    • 2
    • 2
  • m

    myanmarking

    10/04/2021, 4:27 PM
    Can some1 help me understand when the awaitClose gets called:
    @Test
    fun `test flow` (): Unit = runBlocking{
        val flow = callbackFlow {
            trySend(Unit)
            delay(5000)
            close()
    
            awaitClose {
                println("Closed")
            }
        }
    
        val job = launch {
            flow.collect {  }
        }
        job.cancel()
    }
    The doc says on close and cancellation. But in this case, it is not called. Why ? When it is cancelled, awaitClose not called.
    j
    • 2
    • 4
  • c

    Colton Idle

    10/05/2021, 1:35 AM
    Trying to use Flow for the first time for a piece of app state. my app state is basically whether or not the user is logged in. I'm creating my flow this way
    @Singleton
    class AppUserManager
    @Inject
    constructor(@ApplicationContext val context: Context) {
        var loggedInFlow = flow<Boolean> { false }
    ...
    and then there is a login call in the appUserManager
    fun login(token: String) {
        loggedInFlow = flow { true }
    ...
    }
    then in my apps ViewModel I listen to this flow via
    @HiltViewModel
    class HomeScreenViewModel
    @Inject
    constructor(
        var service: ApiService,
        private val appUserManager: AppUserManager
    ) : ViewModel() {
    
        init {
            reload()
            viewModelScope.launch { appUserManager.loggedInFlow.collect {
                reload()
            } }
        }
    ...
    This doesn't work. I'm assuming I'm missing something fundamental here about how to observe a flow. Any tips for me?
    ✅ 1
    j
    • 2
    • 4
  • r

    rrva

    10/05/2021, 8:05 PM
    What are the consequences for a Kotlin/JVM application when I use a library that depends on the -native-mt version of coroutines lib?
    j
    • 2
    • 1
  • n

    natario1

    10/06/2021, 11:15 AM
    Is there an established convention on how to propagate structured concurrency in a hierarchy of classes (with progressively smaller lifecycle)? When using functions this comes for free. With objects, you have a handful of options, • pass parent job to the constructor, object can define its private scope using job as a parent • have object implement CoroutineScope, parent should cancel() it • have object expose a cancel/release function • probably more The first seems less error prone to me, but also something I have rarely seen in other people's code.
    👍 1
    👀 1
    k
    • 2
    • 1
  • o

    Ovsyannikov Alexey

    10/06/2021, 12:06 PM
    Hello everybody :) I wished to ask about flows behaviour, especially in context of
    MutableSharedFlow
    - what shared flow will do in case if nobody is listening for its stream? As I understand, currently all hot flows will throw out data when there are nobody to get it. Or I missed something? If not - is there some built-in flow which allow to prohibit trashing of data in case when nobody listetning that flow?
    y
    a
    • 3
    • 17
  • a

    Anshulupadhyay03

    10/06/2021, 2:21 PM
    What is the difference between GlobalScope.launch(Dispatchers.IO) {} And CoroutineScope(Dispatchers.IO).launch{} I have read from this StackOverflow https://stackoverflow.com/questions/65008486/globalscope-vs-coroutinescope-vs-lifecyclescope that both are same except the syntax differs but there has to be some difference so that we can use each one accordingly.
    d
    j
    u
    • 4
    • 4
  • j

    Justin Tullgren

    10/06/2021, 5:51 PM
    Is there a certain trick to debugging (on android) coroutines that may be executing on different threads when using break points? I see the switch thread option, it opens to that executing line and i start stepping but then it hangs (probably switched back to the other coroutine would be my guess)
    s
    • 2
    • 2
  • j

    Jiri Bruchanov

    10/06/2021, 7:08 PM
    Hi anyone who would know ? I'm trying to do a flow of ints and trying to detect if the sequence is increasing or decreasing based on last 2 numbers...
    c
    • 2
    • 2
  • k

    K Merle

    10/07/2021, 7:49 AM
    Is there any dangers of using .stateIn Flow extension with custom background CoroutineScope inside your ViewModel?
    j
    n
    • 3
    • 15
  • j

    julioromano

    10/07/2021, 9:31 AM
    Is it possible to transform a flow into a flow that emits a rolling window of N elements? As if:
    flowOf<Int>(0,1,2,3,4,5,6,7,8,9).magicOperator(2).collect { values: List<Int> ->
     print("${values}; ")
    }
    Would print:
    0,1; 1,2; 2,3; 3,4; 4,5; 5,6; 6,7; 7,8; 8,9
    e
    j
    m
    • 4
    • 6
  • a

    Andrew Ebling

    10/07/2021, 8:01 PM
    if proguard obfuscation is breaking coroutines
    await()
    (it doesn’t wait 😂 ), where should I start digging? Tried these in `proguard-rules.pro`:
    -keepclassmembernames class kotlinx.** {
        volatile <fields>;
    }
    -keepclassmembers class io.ktor.** {
        volatile <fields>;
    }
    -keepclasseswithmembers class io.netty.** {
        *;
    }
    -keepnames class kotlinx.** { *; }
    -keepnames class io.netty.** {
        *;
    }
    -keep class io.ktor.** { *; }
    -keep class kotlinx.coroutines.** { *; }
    -keep class kotlin.reflect.jvm.internal.** {*;}
    -keep class kotlin.text.RegexOption {*;}
    l
    • 2
    • 1
  • d

    Danish Ansari

    10/10/2021, 8:03 AM
    what is the correct way to use suspend functions inside
    androidTest
    source set Currently I'm testing Room DB Dao in Android and writing test like this
    runBlocking {
        movieDao.insertCategory(popularMovies)
    }
    I think that there might be a better way to write test for such scenario because if I have 20 test than I have to write
    runBlocking
    20 times
    j
    j
    • 3
    • 5
  • a

    AmrJyniat

    10/10/2021, 6:21 PM
    How to get a boolean value from 
    dataStore Preference
     *synchronously*(one-shot) without delay?
    o
    l
    j
    • 4
    • 20
  • u

    ursus

    10/12/2021, 4:36 PM
    I want to downlaod list of assets in parallel, is this correct?
    private suspend fun prefetchStories(stories: List<Story>) {
        coroutineScope {
            val fetches = mutableListOf<Deferred<Unit>>()
            for (story in stories) {
                fetches += async {
                    prefetchStory(story)
                }
            }
            fetches.awaitAll()
        }
    }
    Unsure about the
    coroutineScope
    . Most example don't use it but I presume that means they're using GlobalScope, right?
    j
    u
    • 3
    • 8
  • m

    Minsoo Cheong

    10/13/2021, 5:31 AM
    Hi guys, is alteration to different fields of a same object thread - safe? lets say I have this dto class:
    data class Example(
    var fieldA: Int,
    var fieldB: Int
    )
    and that i change fieldA in thread 1 and fieldB in thread 2 (of a same object of class Example). would this be thread-safe? and if not so, how would I achieve thread safety?
    j
    a
    d
    • 4
    • 7
  • j

    Jeff Lockhart

    10/13/2021, 6:39 AM
    Is there a reason trySendBlocking() is JVM only? How can I achieve this behavior in Kotlin/Native? My use case is adapting a callback API to Flow, without potentially dropping emissions as
    trySend()
    could.
    val flow = callbackFlow {
        val job = addListener {
            trySendBlocking(it) // unavailable in Kotlin/Native
        }
        awaitClose {
            removeListener(job)
        }
    }
    o
    j
    b
    • 4
    • 20
  • g

    GUIGAL Allan

    10/13/2021, 6:06 PM
    Hey! I am working with Redisson locks and Kotlin coroutines. The issue is that the locks are based and acquired by a thread (with its thread id), however considering different coroutines can actually be scheduled on the same thread the lock mechanism won’t work across different coroutines (cause they have the same thread id). Do you have any idea on how I could fix this ? Is there a way to have a coroutine id for example ? Thanks
    e
    d
    • 3
    • 6
  • n

    Nino

    10/14/2021, 11:01 AM
    Hello, I don't understand why I can't
    delay
    the emission of values in a flow, during a unit test... The following code fails :
    import kotlinx.coroutines.CoroutineDispatcher
    import kotlinx.coroutines.Dispatchers
    import kotlinx.coroutines.delay
    import kotlinx.coroutines.flow.flow
    import kotlinx.coroutines.flow.flowOn
    import kotlinx.coroutines.flow.toList
    import kotlinx.coroutines.test.TestCoroutineDispatcher
    import kotlinx.coroutines.test.TestCoroutineScope
    import kotlinx.coroutines.test.resetMain
    import kotlinx.coroutines.test.runBlockingTest
    import kotlinx.coroutines.test.setMain
    import org.junit.Assert.assertEquals
    import org.junit.Rule
    import org.junit.Test
    import org.junit.rules.TestRule
    import org.junit.runner.Description
    import org.junit.runners.model.Statement
    
    class CatRepository(
        private val coroutineDispatcher: CoroutineDispatcher
    ) {
        fun getMeowsFlow() = flow {
            emit("Meow #1")
    
            delay(3_000)
    
            emit("Meow #2")
        }.flowOn(coroutineDispatcher)
    }
    
    class CatRepositoryTest {
        @get:Rule
        val testCoroutineRule = TestCoroutineRule()
    
        private val catRepository = CatRepository(testCoroutineRule.testCoroutineDispatcher)
    
        @Test
        fun catShouldMeowOnce() = testCoroutineRule.runBlockingTest {
            pauseDispatcher()
    
            assertEquals(
                listOf("Meow #1"),
                catRepository.getMeowsFlow().toList()
            )
        }
    }
    
    class TestCoroutineRule : TestRule {
        val testCoroutineDispatcher = TestCoroutineDispatcher()
        private val testCoroutineScope = TestCoroutineScope(testCoroutineDispatcher)
    
        override fun apply(base: Statement, description: Description?) = object : Statement() {
            @Throws(Throwable::class)
            override fun evaluate() {
                Dispatchers.setMain(testCoroutineDispatcher)
    
                base.evaluate()
    
                Dispatchers.resetMain() // reset main dispatcher to the original Main dispatcher
                testCoroutineScope.cleanupTestCoroutines()
            }
        }
    
        fun runBlockingTest(block: suspend TestCoroutineScope.() -> Unit) =
            testCoroutineScope.runBlockingTest { block() }
    }
    It prints
    expected:<[Meow #1]> but was:<[Meow #1, Meow #2]>
    Expected :[Meow #1]
    Actual   :[Meow #1, Meow #2]
    It makes no sense at all ! Since the virtual time is at 0ms, how come the
    delay
    is completely ignored ? Could someone explain ?
    j
    e
    m
    • 4
    • 11
Powered by Linen
Title
n

Nino

10/14/2021, 11:01 AM
Hello, I don't understand why I can't
delay
the emission of values in a flow, during a unit test... The following code fails :
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.test.TestCoroutineDispatcher
import kotlinx.coroutines.test.TestCoroutineScope
import kotlinx.coroutines.test.resetMain
import kotlinx.coroutines.test.runBlockingTest
import kotlinx.coroutines.test.setMain
import org.junit.Assert.assertEquals
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TestRule
import org.junit.runner.Description
import org.junit.runners.model.Statement

class CatRepository(
    private val coroutineDispatcher: CoroutineDispatcher
) {
    fun getMeowsFlow() = flow {
        emit("Meow #1")

        delay(3_000)

        emit("Meow #2")
    }.flowOn(coroutineDispatcher)
}

class CatRepositoryTest {
    @get:Rule
    val testCoroutineRule = TestCoroutineRule()

    private val catRepository = CatRepository(testCoroutineRule.testCoroutineDispatcher)

    @Test
    fun catShouldMeowOnce() = testCoroutineRule.runBlockingTest {
        pauseDispatcher()

        assertEquals(
            listOf("Meow #1"),
            catRepository.getMeowsFlow().toList()
        )
    }
}

class TestCoroutineRule : TestRule {
    val testCoroutineDispatcher = TestCoroutineDispatcher()
    private val testCoroutineScope = TestCoroutineScope(testCoroutineDispatcher)

    override fun apply(base: Statement, description: Description?) = object : Statement() {
        @Throws(Throwable::class)
        override fun evaluate() {
            Dispatchers.setMain(testCoroutineDispatcher)

            base.evaluate()

            Dispatchers.resetMain() // reset main dispatcher to the original Main dispatcher
            testCoroutineScope.cleanupTestCoroutines()
        }
    }

    fun runBlockingTest(block: suspend TestCoroutineScope.() -> Unit) =
        testCoroutineScope.runBlockingTest { block() }
}
It prints
expected:<[Meow #1]> but was:<[Meow #1, Meow #2]>
Expected :[Meow #1]
Actual   :[Meow #1, Meow #2]
It makes no sense at all ! Since the virtual time is at 0ms, how come the
delay
is completely ignored ? Could someone explain ?
j

Joffrey

10/14/2021, 11:04 AM
I'm not entirely sure how the virtual time works in
kotlinx-coroutines-test
, but in any case
toList()
would hang forever if the
flow
didn't progress
n

Nino

10/14/2021, 11:04 AM
Doesn't it collect only the available values at that time ?
j

Joffrey

10/14/2021, 11:05 AM
No, it's a terminal operator that goes to flow completion
There is no real notion of "values at this time" in a flow, because there could be no buffer at all. The collector is the one that asks for elements from the producer
n

Nino

10/14/2021, 11:06 AM
So time manipulation has no effect on flows ? 😞
@Test
    fun catShouldMeow2After3000ms() = testCoroutineRule.runBlockingTest {
        advanceTimeBy(3_001)

        assertEquals(
            "Meow #2",
            catRepository.getMeowsFlow().first()
        )
    }
This test fails too, that's so disappointing 😞
j

Joffrey

10/14/2021, 11:08 AM
But you're asking for the first element of the flow here, flows are cold, not hot (at least most of them). By default, they don't progress independently of their collector. So
first()
here should return the first element, no matter how long you wait before calling it.
e

ephemient

10/14/2021, 8:10 PM
exactly. the flow starts when the collector starts, advancing time before that happens has no impact
if you want to test what's happening during collection, try https://github.com/cashapp/turbine
m

myanmarking

10/15/2021, 9:40 AM
you are advancing the time before collecting. Try collecting inside a launch, and then advance the time
or just
launch{flow.toList} delay(x)
View count: 19