As I understand it, by default, if you start a Kotlin Coroutine via launch or async it'll launch in CommonPool (or if you use GlobalScope). And CommonPool is a ForkJoinPool and that, by default, is in non-async mode so it executes tasks in LIFO order. That seems like a very bad choice for something like asynchronous web server applications where we'd want fair scheduling: we don't want the poor sucker who hit our web server first to wait for all calls that came later.
However, Kotlin coroutines add an additional wrinkle here in that there's some bit of code from the Kotlin standard library that will arrange to have those coroutines executed (some variation of the standard asyc select/epoll loop as I understand it). So maybe the LIFO thing isn't a concern?
I could certainly run some experiments and/or step into the code in a debugger to see how this works but I suspect other's have the same question and I bet somebody "just knows" the answer...
Per discussion on Kotlin Discuss CommonPool is no longer the default and they now default to a "mostly fair" scheduler. Details in the linked discussion.
This shouldn't be a concern, because ForkJoinPool is not really LIFO.
That is, it's LIFO for a single thread in the pool, but that's where things become interesting with "work stealing part". Task queue for each thread is double linked. So, what is LIFO for one thread is FIFO for another thread that became free.
In general, ForkJoinPool is a great solution for small tasks, and usually your coroutines are considered small, if you use suspending functions wisely.
Also, you can read more about asyncMode in documentation, as it's not that "async": https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ForkJoinPool.html
asyncMode - if true, establishes local first-in-first-out scheduling mode for forked tasks that are never joined. This mode may be more appropriate than default locally stack-based mode in applications in which worker threads only process event-style asynchronous tasks. For default value, use false.