Hi everyone, what's the idiomatic way to make sure...
# coroutines
g
Hi everyone, what's the idiomatic way to make sure a suspending function runs only once at a time ? In the official document it mentions to use an
actor
but that seems to be obsolete . Also using cancelAndJoin() for a previos job before starting a new one doesn't guarantee that only one coroutine will run. For example in the following code even though I used cancelAndJoin two coroutines run concurrently
Copy code
var job: Job? = null
suspend fun foo(id: String) {
    println("START $id")
    Thread.sleep(500)
    yield()
    println("DONE $id")
}

fun main(): Unit = runBlocking {
    launch {
        println("Launch1")
        job?.cancelAndJoin()
        println("StartJob1")
        job = launch {
            foo("1")
        }
    }

    launch {
        println("Launch2")
        job!!.cancelAndJoin()
        println("StartJob2")
        job = launch {
            foo("2")
        }
    }

    launch {
        println("Launch3")
        job!!.cancelAndJoin()
        println("StartJob3")
        job = launch {
            foo("3")
        }
    }
}

//OUTPUT
Launch1
StartJob1
Launch2
Launch3
StartJob2
StartJob3
START 2
START 3
DONE 2
DONE 3
s
If you call it sequentially, it'll run sequentially:
foo("1"); foo("2"); ...
If you want to make it re-entrant, use a Mutex, which can guard against re-entry from another thread
g
I guess you mean to run sequentially from the same coroutine. With the example above I launched multiple coroutines on purpose, this can happen for example when a user clicks a button on our app that is doing something asynchronously. The mutex seems to be an option but i was wondering if there is any other mechanism for it. I was under the impression that mutex's are not that useful with coroutines.
s
This mutex is not blocking; it is suspending.
m
Note:
Mutex has two states: locked and unlocked. It is non-reentrant, that is invoking lock even from the same thread/coroutine that currently holds the lock still suspends the invoker.
g
I am aware that it won't block the thread 🙂. Thanks for the advice will use a mutex then
y
There's a nice implementation of reentrant mutexes here
👀 1
a
@Gala Bill
Thread.sleep()
is not cooperative hence your job will not be cancelled, use
delay()
instead.