-
-
Notifications
You must be signed in to change notification settings - Fork 240
Open
Description
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
Labels
No labels