https://kotlinlang.org logo
#coroutines
Title
# coroutines
b

Big Chungus

01/14/2020, 8:29 AM
On K/N (single-threaded) the following coroutine does not execute anything, what am I missing?
Copy code
fun runPeriodicallyAsync(intervalMS: Long, action: () -> Unit): () -> Unit {
  val job = GlobalScope.launch {
    coroutineScope {
      while (true) {
        println("Executing")
        action()
        println("Delaying $intervalMS ms")
        delay(intervalMS)
      }
    }
  }
  println("Returning")
  return {
    println("Disposing")
    job.cancel()
  }
}
Does not execute anything as in: • "Executing" is never printed • "Delaying" is never printed • "Returning" IS printed
m

mzgreen

01/14/2020, 8:38 AM
I think delay needs multi threaded coroutines to work. Try using
1.3.3-native-mt
version of coroutines-native
b

Big Chungus

01/14/2020, 8:41 AM
But in that case wouldn't it execute once before crashing anyways?
m

mzgreen

01/14/2020, 8:53 AM
I have almost identical code but without
coroutineScope
block inside
launch
and it works fine. Also my
action
block is marked with
suspend
keyword.
a

Animesh Sahu

01/14/2020, 9:02 AM
@Big Chungus why are you cancelling the job
b

Big Chungus

01/14/2020, 9:03 AM
I'm not, I'm returning a function that's when invoked will cancel it. As in "unsubscribe" function
a

Animesh Sahu

01/14/2020, 9:03 AM
global scope does not wait, as long as main function has returned(came to end point) all daemon threads are destroyed and cancelled, you need something in main thread so that global scope coroutine will run in back.
b

Big Chungus

01/14/2020, 9:06 AM
@Animesh Sahu I'm not, have a look at return type, it's a function.
Also "Disposing" is not printed
a

Animesh Sahu

01/14/2020, 9:07 AM
Ohh, i just didnt saw lambda
👍 1
b

Big Chungus

01/14/2020, 9:08 AM
@mzgreen can you share your code? also which version of coroutines are you using?
a

Animesh Sahu

01/14/2020, 9:14 AM
Hey it just works fine in my case
b

Big Chungus

01/14/2020, 9:15 AM
Which version of coroutines?
m

mzgreen

01/14/2020, 9:15 AM
I’m using 1.3.3-native-mt because with other versions (not supporting multithreading) delay didn’t work . My code looks like this:
Copy code
GlobalScope.launch {
    while(isActive) {
        action() 
        delay(1000)
    }
}
Where
action
is:
Copy code
suspended fun action() { 
    // some work
}
a

Animesh Sahu

01/14/2020, 9:15 AM
1.3.2
b

Big Chungus

01/14/2020, 9:17 AM
Odd... 😕
a

Animesh Sahu

01/14/2020, 9:17 AM
Did you tried adding some sleep calls(in main function) for checking if main exits before coroutine is launched
b

Big Chungus

01/14/2020, 9:18 AM
It certainly does not as it's a GUI app
a

Animesh Sahu

01/14/2020, 9:18 AM
ohh
might be a bug
b

Big Chungus

01/14/2020, 9:20 AM
the issue seems to be related to content switching
a

Animesh Sahu

01/14/2020, 9:21 AM
Or specifically for K/N(i didnt tested), K/JVM works fine
b

Big Chungus

01/14/2020, 9:22 AM
K/JVM has entirely different memory model 😄 Most of the K/JVM stuff will not work on K/N
m

mzgreen

01/14/2020, 9:23 AM
I run my code on K/N specifically on iOS. Have you tried printing something before and after GlobalScope.launch block?
b

Big Chungus

01/14/2020, 9:24 AM
Yes, both prints
m

mzgreen

01/14/2020, 9:28 AM
I would try to simplify the code and check if it works and when you get it working you can start building your desired solution one part at a time and see what exactly breaks it
u

uli

01/14/2020, 2:47 PM
Does it work without `coroutineScope`for you, as @mzgreen reported? Then we'd have something to look into :-)
b

Big Chungus

01/14/2020, 3:19 PM
No, neither worked.
In the end I scrapped that approach and managed to get what i need with this:
Copy code
@SharedImmutable
val engineRunning = AtomicInt(0)
@SharedImmutable
private val engineDispatcher = newSingleThreadContext("engine")

actual fun startEngine(appConfigStore: AppConfigStoreImpl, universeStore: UniverseStoreImpl) {
  if (engineRunning.value == 0) {
    engineRunning.increment()
    val stateChannel = Channel<AppConfigState>().freeze()
    val dispatchChannel = Channel<Int>()
    val universeStorePtr = StableRef.create(universeStore).freeze()
    GlobalScope.launch {
      for (action in dispatchChannel) {
        universeStorePtr.get().dispatch(UniverseAction.Tick)
      }
    }
    appConfigStore.subscribe {
      GlobalScope.launch(engineDispatcher) { stateChannel.send(it.freeze()) }
    }
    GlobalScope.launch(engineDispatcher) {
      var state = AppConfigState()
      while (isActive) {
        if (!stateChannel.isEmpty) {
          state = stateChannel.receive()
        }
        if (state.running) {
          dispatchChannel.send(0)
        }
        delay((1000 / state.fps).toLong())
      }
    }
  } else {
    println("engine already running")
  }
}
j

julian

01/14/2020, 6:15 PM
Could it be that
job
is being garbage collected once you return from
runPeriodicallyAsync
?
b

Big Chungus

01/14/2020, 6:15 PM
Well the returned lambda keeps reference to it
j

julian

01/14/2020, 6:21 PM
Yeah, you're right. I just verified that that a lambda does keep a reference.
2 Views