But to directly answer your questions:
Are coroutines something like green threads, i.e. they aren’t necessarily native threads but have a m:1 or probably m:n mapping to native os threads?
Yes. The default dispatcher spawns a number of Java threads in a thread pool equal to the number of CPU cores on your machine which it schedules coroutines onto, an M:N mapping.
Is calling a suspending function issueing a context switch of the parent coroutine to execute the next (top-down) child coroutine unless the current coroutine doesn’t share the same dispatcher as the parent coroutine?
When you invoke a suspending function from another suspending function, it inherits the context of the caller unless you explicitly invoke
withContext() to switch to a different one. When you call
withContext() what it will do is:
- Suspend the calling coroutine (i.e. exit and wait for reschedule) and schedule
withContext()'s lambda to be executed within the specified context, i.e. on a different thread pool if you chose a different dispatcher.
- The lambda executes in the specified context
- When the execution completes, the calling coroutine resumes, picking up immediately after the
withContext() call in the original context. Kotlin coroutines automatically handle the copying of data between threads if you modified data from the surrounding scope.
Does having suspending functions imply that Kotlin is cooperatively (green) threaded?
Yes, cancellation is at least. Suspension points may cause the current coroutine to suspend and may throw
CancellationException if the invoked code gets cancelled. If you’re writing CPU-intensive code with no suspension points you may need to check the
isActive value of your current coroutine and exit at an appropriate time, which is available in all suspending functions.
Or are there opportunities to suspend preemptively executions?
The most you can do is call the
cancel() function on a job, which will attempt to stop that job and all child jobs. This means that the coroutines running in a job will have
isActive set to false, and calls such as
yield() may throw
CancellationException to stop the work.
Without suspending functions, are both coroutines guaranteed to run sequentially or is this dispatcher dependent?
It’s dispatcher dependent. If the dispatcher in question is backed by a single thread, the two coroutines will be scheduled on the thread sequentially. However, in the case of
Dispatchers.Default it’s more likely that the two coroutines in your sample code will be concurrently scheduled onto 2 different threads in the default dispatcher’s thread pool.
Hope that helps!