From 2a3716d7517264ed4674230f9ec84e6f63f473ea Mon Sep 17 00:00:00 2001 From: Isaac Levy Date: Fri, 13 Sep 2024 15:38:32 -0400 Subject: [PATCH 1/4] Fix ThreadLocalRandom, again --- lila/src/main/scala/Random.scala | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/lila/src/main/scala/Random.scala b/lila/src/main/scala/Random.scala index 1291289..442a290 100644 --- a/lila/src/main/scala/Random.scala +++ b/lila/src/main/scala/Random.scala @@ -2,16 +2,21 @@ package scalalib import scala.collection.mutable.StringBuilder -private final val store = - java.lang.ThreadLocal.withInitial(() => RandomApi(java.util.concurrent.ThreadLocalRandom.current)) +object ThreadLocalRandom extends RandomApi: + protected def impl = java.util.concurrent.ThreadLocalRandom.current() -def ThreadLocalRandom = store.get +object SecureRandom extends RandomApi: + protected val impl = java.security.SecureRandom.getInstanceStrong() -val SecureRandom = RandomApi(java.security.SecureRandom()) +private abstract class RandomApi: + protected def impl: java.util.Random -final class RandomApi(impl: java.util.Random): - - export impl.{ nextBoolean, nextDouble, nextFloat, nextGaussian, nextInt, nextLong } + def nextBoolean = impl.nextBoolean + def nextDouble = impl.nextDouble + def nextFloat = impl.nextFloat + def nextGaussian = impl.nextGaussian + def nextInt = impl.nextInt + def nextLong = impl.nextLong def nextBytes(len: Int): Array[Byte] = val bytes = new Array[Byte](len) @@ -20,7 +25,7 @@ final class RandomApi(impl: java.util.Random): private val chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" private inline def nextAlphanumeric(): Char = - chars.charAt(nextInt(chars.length)) // Constant time + chars.charAt(impl.nextInt(chars.length)) // Constant time def nextString(len: Int): String = val sb = StringBuilder(len) @@ -31,9 +36,9 @@ final class RandomApi(impl: java.util.Random): scala.util.Random(impl).shuffle(xs) def oneOf[A](vec: Vector[A]): Option[A] = - if vec.nonEmpty then vec.lift(nextInt(vec.size)) else None + if vec.nonEmpty then vec.lift(impl.nextInt(vec.size)) else None // odds(1) = 100% true // odds(2) = 50% true // odds(3) = 33% true - def odds(n: Int): Boolean = nextInt(n) == 0 + def odds(n: Int): Boolean = impl.nextInt(n) == 0 From 89b82f3dfb413cff02739d50621dffcf5aa0ebbb Mon Sep 17 00:00:00 2001 From: Isaac Levy Date: Fri, 13 Sep 2024 15:50:59 -0400 Subject: [PATCH 2/4] Fix paren convention --- lila/src/main/scala/Random.scala | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lila/src/main/scala/Random.scala b/lila/src/main/scala/Random.scala index 442a290..85949a4 100644 --- a/lila/src/main/scala/Random.scala +++ b/lila/src/main/scala/Random.scala @@ -11,12 +11,12 @@ object SecureRandom extends RandomApi: private abstract class RandomApi: protected def impl: java.util.Random - def nextBoolean = impl.nextBoolean - def nextDouble = impl.nextDouble - def nextFloat = impl.nextFloat - def nextGaussian = impl.nextGaussian - def nextInt = impl.nextInt - def nextLong = impl.nextLong + def nextBoolean() = impl.nextBoolean + def nextDouble() = impl.nextDouble + def nextFloat() = impl.nextFloat + def nextGaussian() = impl.nextGaussian + def nextInt() = impl.nextInt + def nextLong() = impl.nextLong def nextBytes(len: Int): Array[Byte] = val bytes = new Array[Byte](len) From 042c3784ddaa8bfedddc23a73151c07f7761a341 Mon Sep 17 00:00:00 2001 From: Isaac Levy Date: Fri, 13 Sep 2024 15:53:43 -0400 Subject: [PATCH 3/4] Add nextInt(n) and nextLong(l) --- lila/src/main/scala/Random.scala | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/lila/src/main/scala/Random.scala b/lila/src/main/scala/Random.scala index 85949a4..bbdabec 100644 --- a/lila/src/main/scala/Random.scala +++ b/lila/src/main/scala/Random.scala @@ -11,12 +11,14 @@ object SecureRandom extends RandomApi: private abstract class RandomApi: protected def impl: java.util.Random - def nextBoolean() = impl.nextBoolean - def nextDouble() = impl.nextDouble - def nextFloat() = impl.nextFloat - def nextGaussian() = impl.nextGaussian - def nextInt() = impl.nextInt - def nextLong() = impl.nextLong + def nextBoolean() = impl.nextBoolean + def nextDouble() = impl.nextDouble + def nextFloat() = impl.nextFloat + def nextGaussian() = impl.nextGaussian + def nextInt() = impl.nextInt + def nextInt(n: Int) = impl.nextInt(n) + def nextLong() = impl.nextLong + def nextLong(l: Long) = impl.nextLong(l) def nextBytes(len: Int): Array[Byte] = val bytes = new Array[Byte](len) From a22ef54364978cf2f7503d548797b6e255fe69b1 Mon Sep 17 00:00:00 2001 From: Isaac Levy Date: Fri, 13 Sep 2024 15:58:58 -0400 Subject: [PATCH 4/4] Delete ThreadLocalRandom test The test isn't meaningful anymore. ThreadLocalRandom uses a singleton interface so it's not possible to check that different threads are using a different instance. --- lila/src/main/scala/Maths.scala | 2 -- lila/src/main/scala/cuteName.scala | 2 -- lila/src/test/scala/RandomTest.scala | 12 ------------ 3 files changed, 16 deletions(-) delete mode 100644 lila/src/test/scala/RandomTest.scala diff --git a/lila/src/main/scala/Maths.scala b/lila/src/main/scala/Maths.scala index e460eb4..f5e129b 100644 --- a/lila/src/main/scala/Maths.scala +++ b/lila/src/main/scala/Maths.scala @@ -1,7 +1,5 @@ package scalalib -import scalalib.ThreadLocalRandom - import scala.Numeric.Implicits.* import scala.reflect.ClassTag import scala.util.Sorting diff --git a/lila/src/main/scala/cuteName.scala b/lila/src/main/scala/cuteName.scala index 1e1ae9b..3830ac3 100644 --- a/lila/src/main/scala/cuteName.scala +++ b/lila/src/main/scala/cuteName.scala @@ -1,8 +1,6 @@ package scalalib package cuteName -import scalalib.ThreadLocalRandom - // children friendly names only // this is used by /class object CuteNameGenerator: diff --git a/lila/src/test/scala/RandomTest.scala b/lila/src/test/scala/RandomTest.scala deleted file mode 100644 index 2b2095d..0000000 --- a/lila/src/test/scala/RandomTest.scala +++ /dev/null @@ -1,12 +0,0 @@ -package scalalib - -class RandomTest extends munit.FunSuite: - - test("different threads get different randoms"): - val r1 = ThreadLocalRandom - - @volatile var r2: RandomApi = null - val thread2 = new Thread(() => r2 = ThreadLocalRandom) - thread2.start() - thread2.join() - assertNotEquals(r1, r2)