`seedUniquifier` ritual doesn't uniquify seeds
bbjubjub2494 opened this issue · 0 comments
bbjubjub2494 commented
In random.rng.Utils.longFromSeed
, a @volatile
counter variable is used. The intent is presumably to guarantee that two RNG constructions initiated during the same nanosecond will result in two independent RNGs. The problem is that the @volatile
construct isn't enough to secure a proper concurrent counter variable: it is very possible for two clients to receive the same value from the counter. Thus, the uniquifier isn't doing what it says on the label. This is showcased by the attached ammonite script. (Please run a few times if the result is ∅ at first)
This could be solved using different concurrency primitives, such as synchronized
or monix.execution.atomic
.
import $ivy.`org.typelevel::spire:0.17.0-M1`
import spire.random.rng.Utils.longFromTime
import concurrent.{Future, ExecutionContext, Await}
import concurrent.duration._
val nanoTime = 0L // fake frozen timer
// collect 100 values from the uniquifier successively
def observeUniquifier()(implicit ec: ExecutionContext): Future[Vector[Long]] =
Future { Vector.fill(100) { longFromTime(nanoTime) } }
@main def main(): Unit = {
implicit val ec = ExecutionContext.global
// collect two concurrent sequences from the uniquifier
val (l1, l2) = Await.result(observeUniquifier() zip observeUniquifier(), Duration.Inf)
// There should be no overlap, but there usually is
println(l1.toSet & l2.toSet)
}