Skip to content

seedUniquifier ritual doesn't uniquify seeds #902

@bbjubjub2494

Description

@bbjubjub2494

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)
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions