Skip to content

Commit

Permalink
Fix a bug that could leave the coroutine scheduler's PRNG in an inval…
Browse files Browse the repository at this point in the history
…id state (Kotlin#4052)

A potential bug is fixed, where `nextInt` would always return zero due to incorrect initialization of rngState with zero.
This could happen during the creation of a worker with thread id = `1595972770` (JDK >=8), or unpredictable if fallback thread-local random is used (android SDK <34 or JDK <7), approximate probability is 2.4E-10

Also, this slightly optimizes the performance of coroutines initialization. During the creation of `CoroutineScheduler.Worker`, we need to initialize its embedded random number generator.
To avoid additional class-loading costs (Kotlin#4051), `rngState` is now directly initialized from the current nanoTime (that is used as seed).

Fixes Kotlin#4051

Co-authored-by: Vsevolod Tolstopyatov <[email protected]>
  • Loading branch information
2 people authored and knisht committed Apr 15, 2024
1 parent f587aca commit 178d7e2
Showing 1 changed file with 12 additions and 3 deletions.
15 changes: 12 additions & 3 deletions kotlinx-coroutines-core/jvm/src/scheduling/CoroutineScheduler.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import java.util.concurrent.*
import java.util.concurrent.locks.*
import kotlin.jvm.internal.Ref.ObjectRef
import kotlin.math.*
import kotlin.random.*

/**
* Coroutine scheduler (pool of shared threads) which primary target is to distribute dispatched coroutines
Expand Down Expand Up @@ -658,11 +657,21 @@ internal class CoroutineScheduler(
var nextParkedWorker: Any? = NOT_IN_STACK

/*
* The delay until at least one task in other worker queues will become stealable.
* The delay until at least one task in other worker queues will become stealable.
*/
private var minDelayUntilStealableTaskNs = 0L

private var rngState = Random.nextInt()
/**
* The state of embedded Marsaglia xorshift random number generator, used for work-stealing purposes.
* It is initialized with a seed.
*/
private var rngState: Int = run {
// This could've been Random.nextInt(), but we are shaving an extra initialization cost, see #4051
val seed = System.nanoTime().toInt()
// rngState shouldn't be zero, as required for the xorshift algorithm
if (seed != 0) return@run seed
42
}

/**
* Tries to acquire CPU token if worker doesn't have one
Expand Down

0 comments on commit 178d7e2

Please sign in to comment.