Skip to content

Commit f86cbc7

Browse files
semoroqwwdfsad
andauthored
Fix a bug that could leave the coroutine scheduler's PRNG in an invalid state (#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 (#4051), `rngState` is now directly initialized from the current nanoTime (that is used as seed). Fixes #4051 Co-authored-by: Vsevolod Tolstopyatov <[email protected]>
1 parent 6c21369 commit f86cbc7

File tree

1 file changed

+12
-3
lines changed

1 file changed

+12
-3
lines changed

kotlinx-coroutines-core/jvm/src/scheduling/CoroutineScheduler.kt

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import java.util.concurrent.*
88
import java.util.concurrent.locks.*
99
import kotlin.jvm.internal.Ref.ObjectRef
1010
import kotlin.math.*
11-
import kotlin.random.*
1211

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

660659
/*
661-
* The delay until at least one task in other worker queues will become stealable.
660+
* The delay until at least one task in other worker queues will become stealable.
662661
*/
663662
private var minDelayUntilStealableTaskNs = 0L
664663

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

667676
/**
668677
* Tries to acquire CPU token if worker doesn't have one

0 commit comments

Comments
 (0)