The mutex created is fair: lock is granted in first come, first served order. - kotlin doc

GT-Sandbox-Snapshot

Code

package com.glassthought.sandbox
 
import kotlinx.coroutines.*
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlin.time.Duration.Companion.seconds
 
// Demonstrate that Mutex in kotlin is fair - FIFO ordering of lock acquisition.
fun main() = runBlocking {
 
  val mutex = Mutex()
 
  val list = mutableListOf<Int>()
 
  val howManyTasks = Runtime.getRuntime().availableProcessors()
 
  val whenWeAreReadyToStartMillis = System.currentTimeMillis() + 1000
 
  (1..howManyTasks).forEach {
    launch(Dispatchers.Default) {
 
      while (System.currentTimeMillis() < (whenWeAreReadyToStartMillis + it)) {
        // spin wait, without delay so that the tasks get to the lock in the order of
        // their "it" value
      }
 
      mutex.withLock {
        // Delay: to make sure we will get the tasks to queue up on the lock
        delay(howManyTasks.toLong() + 1)
 
        list.add(it)
      }
    }
  }
 
  delay(3.seconds)
  println("Actual   : " + list)
  println("Expected : " + (1..howManyTasks).toList())
}

Command to reproduce:

gt.sandbox.checkout.commit eb2c4982ff34283a9738 \
&& cd "${GT_SANDBOX_REPO}" \
&& cmd.run.announce "./gradlew run --quiet"

Recorded output of command:

Picked up JAVA_TOOL_OPTIONS: -Dkotlinx.coroutines.debug
Picked up JAVA_TOOL_OPTIONS: -Dkotlinx.coroutines.debug
Actual   : [1, 3, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32]
Expected : [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32]