Bradleycorn
03/06/2023, 9:24 PMobject MyObject {
suspend fun repeatWork() {
while (true) {
// do something
delay(5_000L)
}
}
}
That’s fine, but now multiple things can call it, and do “multiple copies” of the work:
class SomeClass {
fun doWork() {
someClassScope.launch { MyObject.repeatWork() }
}
}
class AnotherClass {
fun doAnotherWork() {
anotherClassScope.launch { MyObject.repeatWork() }
}
}
SomeClass().doWork()
AnotherClass().doAnotherWork()
I’m trying to ensure that only one “instance” of the repeatWork routine is running at a time, no matter how may things try to call it. I was thinking something like this:
object MyObject {
private var repeatWorkJob: Job? = null
suspend fun repeatWork() = coroutineScope {
if (repeatWorkJob?.isActive == true) return
repeatWorkJob = launch {
while (true) {
// do something
delay(5_000L)
}
}
}
}
Is this the best/right way to ensure there’s only one “instance” of the repeatWork routine running at one time?Francesc
03/06/2023, 9:40 PMephemient
03/07/2023, 12:41 AMobject MyObject {
private val repeatWorkJob = GlobalScope.launch(start = CoroutineStart.LAZY) {
while (true) {
// do something
delay(5_000L)
}
}
fun repeatWork() {
repeatWorkJob.start()
}
}
uli
03/07/2023, 9:36 AMBradleycorn
03/07/2023, 2:38 PMuli
03/07/2023, 2:53 PMBradleycorn
03/07/2023, 3:01 PMPatrick Steiger
03/07/2023, 5:14 PMclass JobSwapper {
private var currentJob: Job? = null
private val mutex = Mutex()
private val jobMutex = Mutex()
operator fun invoke(
scope: CoroutineScope,
block: suspend CoroutineScope.() -> Unit,
) {
scope.launch {
mutex.withLock {
currentJob?.cancel()
currentJob = launch {
jobMutex.withLock {
coroutineScope {
block()
}
}
}
}
}
}
}
val jobSwapper = JobSwapper()
…
jobSwapper(scope) {
// only runs when previous job is completed
}
Bradleycorn
03/07/2023, 5:30 PMJobSwapper
will cancel the current job if it’s called again:
currentJob?.cancel()
I’m looking for something that would be more similar to `join`ing the job if it’s already running. But even that doesn’t solve all problems, because the job would be tied to the original caller’s scope. when that scope gets canceled the job would cancel, even though a second caller might still be interested in results.
I think I might have to do something like @uli suggested with a SharedFlow
or something similar.ephemient
03/07/2023, 5:33 PMGlobalScope.launch(LAZY)
will do (or async
if you need the results), since it's outside of its launchers' scopes. but if you want to be able to restart it, or cancel it if all current waiters are cancelled, that'll be trickierBradleycorn
03/07/2023, 5:49 PMephemient
03/07/2023, 5:55 PMFlow.shareIn(started = SharingStarted.WhileSubscribed())
might be the simplest solutionBradleycorn
03/07/2023, 5:56 PM