Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] New RandomNumberGenerator API #1757

Open
wants to merge 12 commits into
base: main
Choose a base branch
from
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,11 @@ import pekko.actor.typed._
import pekko.actor.typed.internal._
import pekko.actor.{ ActorPath, ActorRefProvider, InvalidMessageException }
import pekko.annotation.InternalApi
import pekko.util.Helpers
import pekko.util.{ Helpers, RandomNumberGenerator }
import pekko.{ actor => classic }
import org.slf4j.{ Logger, Marker }
import org.slf4j.helpers.{ MessageFormatter, SubstituteLoggerFactory }

import java.util.concurrent.ThreadLocalRandom.{ current => rnd }
import scala.collection.immutable.TreeMap
import scala.concurrent.ExecutionContextExecutor
import scala.concurrent.duration.FiniteDuration
Expand Down Expand Up @@ -75,7 +74,7 @@ private[pekko] final class FunctionRef[-T](override val path: ActorPath, send: (
extends ActorContextImpl[T] {

def this(system: ActorSystemStub, name: String, currentBehaviorProvider: () => Behavior[T]) = {
this(system, (system.path / name).withUid(rnd().nextInt()), currentBehaviorProvider)
this(system, (system.path / name).withUid(RandomNumberGenerator.get().nextInt()), currentBehaviorProvider)
}

def this(name: String, currentBehaviorProvider: () => Behavior[T]) = {
Expand Down Expand Up @@ -111,7 +110,8 @@ private[pekko] final class FunctionRef[-T](override val path: ActorPath, send: (

override def spawnAnonymous[U](behavior: Behavior[U], props: Props = Props.empty): ActorRef[U] = {
checkCurrentActorThread()
val btk = new BehaviorTestKitImpl[U](system, (path / childName.next()).withUid(rnd().nextInt()), behavior)
val btk = new BehaviorTestKitImpl[U](system,
(path / childName.next()).withUid(RandomNumberGenerator.get().nextInt()), behavior)
_children += btk.context.self.path.name -> btk
btk.context.self
}
Expand All @@ -120,7 +120,8 @@ private[pekko] final class FunctionRef[-T](override val path: ActorPath, send: (
_children.get(name) match {
case Some(_) => throw classic.InvalidActorNameException(s"actor name $name is already taken")
case None =>
val btk = new BehaviorTestKitImpl[U](system, (path / name).withUid(rnd().nextInt()), behavior)
val btk =
new BehaviorTestKitImpl[U](system, (path / name).withUid(RandomNumberGenerator.get().nextInt()), behavior)
_children += name -> btk
btk.context.self
}
Expand Down Expand Up @@ -172,7 +173,7 @@ private[pekko] final class FunctionRef[-T](override val path: ActorPath, send: (
@InternalApi private[pekko] def internalSpawnMessageAdapter[U](f: U => T, name: String): ActorRef[U] = {

val n = if (name != "") s"${childName.next()}-$name" else childName.next()
val p = (path / n).withUid(rnd().nextInt())
val p = (path / n).withUid(RandomNumberGenerator.get().nextInt())
val i = new BehaviorTestKitImpl[U](system, p, BehaviorImpl.ignore)
_children += p.name -> i

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ import pekko.actor.testkit.typed.{ CapturedLogEvent, Effect }
import pekko.actor.typed.receptionist.Receptionist
import pekko.actor.typed.{ ActorRef, Behavior, Signal }
import pekko.annotation.{ ApiMayChange, DoNotInherit }
import com.typesafe.config.Config
import pekko.util.RandomNumberGenerator

import java.util.concurrent.ThreadLocalRandom
import com.typesafe.config.Config

object BehaviorTestKit {

Expand All @@ -37,7 +37,7 @@ object BehaviorTestKit {
@ApiMayChange
def create[T](initialBehavior: Behavior[T], name: String, config: Config): BehaviorTestKit[T] = {
val system = new ActorSystemStub("StubbedActorContext", config)
val uid = ThreadLocalRandom.current().nextInt()
val uid = RandomNumberGenerator.get().nextInt()
new BehaviorTestKitImpl(system, (system.path / name).withUid(uid), initialBehavior)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,25 +13,24 @@

package org.apache.pekko.actor.testkit.typed.javadsl

import java.util.concurrent.ThreadLocalRandom

import scala.collection.immutable

import org.apache.pekko
import pekko.actor.testkit.typed.internal.TestInboxImpl
import pekko.actor.typed.ActorRef
import pekko.annotation.DoNotInherit
import pekko.util.ccompat.JavaConverters._
import pekko.util.RandomNumberGenerator

object TestInbox {
import pekko.actor.testkit.typed.scaladsl.TestInbox.address

def create[T](name: String): TestInbox[T] = {
val uid = ThreadLocalRandom.current().nextInt()
val uid = RandomNumberGenerator.get().nextInt()
new TestInboxImpl((address / name).withUid(uid))
}
def create[T](): TestInbox[T] = {
val uid = ThreadLocalRandom.current().nextInt()
val uid = RandomNumberGenerator.get().nextInt()
new TestInboxImpl((address / "inbox").withUid(uid))
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,10 @@ import pekko.actor.testkit.typed.{ CapturedLogEvent, Effect }
import pekko.actor.typed.receptionist.Receptionist
import pekko.actor.typed.{ ActorRef, Behavior, Signal, TypedActorContext }
import pekko.annotation.{ ApiMayChange, DoNotInherit }
import pekko.util.RandomNumberGenerator

import com.typesafe.config.Config

import java.util.concurrent.ThreadLocalRandom
import scala.collection.immutable
import scala.reflect.ClassTag

Expand All @@ -32,7 +33,7 @@ object BehaviorTestKit {

def apply[T](initialBehavior: Behavior[T], name: String, config: Config): BehaviorTestKit[T] = {
val system = new ActorSystemStub("StubbedActorContext", config)
val uid = ThreadLocalRandom.current().nextInt()
val uid = RandomNumberGenerator.get().nextInt()
new BehaviorTestKitImpl(system, (system.path / name).withUid(uid), initialBehavior)
}
def apply[T](initialBehavior: Behavior[T], name: String): BehaviorTestKit[T] = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,19 @@

package org.apache.pekko.actor.testkit.typed.scaladsl

import java.util.concurrent.ThreadLocalRandom

import scala.collection.immutable

import org.apache.pekko
import pekko.actor.{ Address, RootActorPath }
import pekko.actor.testkit.typed.internal.TestInboxImpl
import pekko.actor.typed.ActorRef
import pekko.annotation.{ ApiMayChange, DoNotInherit }
import pekko.util.RandomNumberGenerator

@ApiMayChange
object TestInbox {
def apply[T](name: String = "inbox"): TestInbox[T] = {
val uid = ThreadLocalRandom.current().nextInt()
val uid = RandomNumberGenerator.get().nextInt()
new TestInboxImpl((address / name).withUid(uid))
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.apache.pekko.util

import com.typesafe.config.ConfigFactory
import org.scalatest.matchers.should.Matchers
import org.scalatest.wordspec.AnyWordSpec

class RandomNumberGeneratorJava21Spec extends AnyWordSpec with Matchers {

"RandomNumberGenerator (Java 21+)" should {

"support config" in {
val config = ConfigFactory.parseString(
"""pekko.random.generator-implementation = "Xoroshiro128PlusPlus"""")
val rng = RandomNumberGenerator.createGenerator(config)
rng shouldBe a[Jep356RandomNumberGenerator]
rng.nextInt(10) should (be >= 0 and be < 10)
}

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.apache.pekko.util

import org.scalatest.matchers.should.Matchers
import org.scalatest.wordspec.AnyWordSpec

class RandomNumberGeneratorSpec extends AnyWordSpec with Matchers {

"RandomNumberGenerator" should {

"default to ThreadLocalRandom" in {
val rng = RandomNumberGenerator.get()
rng shouldEqual ThreadLocalRandomNumberGenerator
rng.nextInt(10) should (be >= 0 and be < 10)
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
package org.apache.pekko.actor.typed.delivery.internal

import java.util.UUID
import java.util.concurrent.ThreadLocalRandom
import java.util.concurrent.TimeoutException

import scala.reflect.ClassTag
Expand All @@ -39,7 +38,7 @@ import pekko.actor.typed.scaladsl.Behaviors
import pekko.actor.typed.scaladsl.LoggerOps
import pekko.actor.typed.scaladsl.StashBuffer
import pekko.annotation.InternalApi
import pekko.util.Timeout
import pekko.util.{ RandomNumberGenerator, Timeout }

/**
* INTERNAL API
Expand Down Expand Up @@ -404,7 +403,7 @@ private class WorkPullingProducerControllerImpl[A: ClassTag](
if (workers.isEmpty) {
None
} else {
val i = ThreadLocalRandom.current().nextInt(workers.size)
val i = RandomNumberGenerator.get().nextInt(workers.size)
Some(workers(i))
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@
package org.apache.pekko.actor.typed
package internal

import java.util.concurrent.ThreadLocalRandom

import scala.concurrent.duration.Deadline
import scala.concurrent.duration.FiniteDuration
import scala.reflect.ClassTag
Expand All @@ -33,7 +31,7 @@ import pekko.actor.typed.scaladsl.Behaviors
import pekko.actor.typed.scaladsl.StashBuffer
import pekko.annotation.InternalApi
import pekko.event.Logging
import pekko.util.OptionVal
import pekko.util.{ OptionVal, RandomNumberGenerator }
import pekko.util.unused

import scala.util.Try
Expand Down Expand Up @@ -187,7 +185,7 @@ private object RestartSupervisor {
minBackoff: FiniteDuration,
maxBackoff: FiniteDuration,
randomFactor: Double): FiniteDuration = {
val rnd = 1.0 + ThreadLocalRandom.current().nextDouble() * randomFactor
val rnd = 1.0 + RandomNumberGenerator.get().nextDouble() * randomFactor
val calculatedDuration = Try(maxBackoff.min(minBackoff * math.pow(2, restartCount)) * rnd).getOrElse(maxBackoff)
calculatedDuration match {
case f: FiniteDuration => f
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,12 @@

package org.apache.pekko.actor.typed.internal.routing

import java.util.concurrent.ThreadLocalRandom

import org.apache.pekko
import pekko.actor.Address
import pekko.actor.typed.ActorRef
import pekko.annotation.InternalApi
import pekko.routing.ConsistentHash
import pekko.util.RandomNumberGenerator

/**
* Kept in the behavior, not shared between instances, meant to be stateful.
Expand Down Expand Up @@ -89,7 +88,7 @@ private[pekko] object RoutingLogics {
private var currentRoutees: Array[ActorRef[T]] = _

override def selectRoutee(msg: T): ActorRef[T] = {
val selectedIdx = ThreadLocalRandom.current().nextInt(currentRoutees.length)
val selectedIdx = RandomNumberGenerator.get().nextInt(currentRoutees.length)
currentRoutees(selectedIdx)
}

Expand Down
9 changes: 9 additions & 0 deletions actor/src/main/resources/reference.conf
Original file line number Diff line number Diff line change
Expand Up @@ -1396,4 +1396,13 @@ pekko {
}
#//#circuit-breaker-default

random {
# The default random number generator used by the Pekko library.
# This setting does not affect SecureRandom use cases.
# This option is ignored if you are not using Java 17+. The default is "ThreadLocalRandom".
# Valid options are listed in
# https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/random/package-summary.html#algorithms
generator-implementation = "ThreadLocalRandom"
}

}
4 changes: 2 additions & 2 deletions actor/src/main/scala/org/apache/pekko/actor/ActorCell.scala
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
package org.apache.pekko.actor

import java.io.{ NotSerializableException, ObjectOutputStream }
import java.util.concurrent.ThreadLocalRandom

import scala.annotation.{ switch, tailrec }
import scala.annotation.nowarn
Expand All @@ -31,6 +30,7 @@ import pekko.dispatch.sysmsg._
import pekko.event.Logging.{ Debug, Error, LogEvent }
import pekko.japi.Procedure
import pekko.util.unused
import pekko.util.RandomNumberGenerator

/**
* The actor context - the view of the actor cell from the actor.
Expand Down Expand Up @@ -392,7 +392,7 @@ private[pekko] object ActorCell {
@tailrec final def newUid(): Int = {
// Note that this uid is also used as hashCode in ActorRef, so be careful
// to not break hashing if you change the way uid is generated
val uid = ThreadLocalRandom.current.nextInt()
val uid = RandomNumberGenerator.get().nextInt()
if (uid == undefinedUid) newUid()
else uid
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -814,7 +814,7 @@ private[pekko] class ActorSystemImpl(
setup: ActorSystemSetup)
extends ExtendedActorSystem {

val uid: Long = ThreadLocalRandom.current.nextLong()
val uid: Long = RandomNumberGenerator.get().nextLong()

if (!name.matches("""^[a-zA-Z0-9][a-zA-Z0-9-_]*$"""))
throw new IllegalArgumentException(
Expand Down
15 changes: 11 additions & 4 deletions actor/src/main/scala/org/apache/pekko/io/dns/IdGenerator.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,12 @@

package org.apache.pekko.io.dns

import org.apache.pekko.annotation.InternalApi
import org.apache.pekko
import pekko.annotation.InternalApi
import pekko.util.RandomNumberGenerator

import java.security.SecureRandom
import java.util.Random
import java.util.concurrent.ThreadLocalRandom

/**
* INTERNAL API
Expand Down Expand Up @@ -52,19 +53,25 @@ private[pekko] object IdGenerator {
}

def apply(policy: Policy): IdGenerator = policy match {
case Policy.ThreadLocalRandom => random(ThreadLocalRandom.current())
case Policy.ThreadLocalRandom => random(RandomNumberGenerator.get())
case Policy.SecureRandom => random(new SecureRandom())
case Policy.EnhancedDoubleHashRandom => new EnhancedDoubleHashGenerator(new SecureRandom())
}

def apply(): IdGenerator = random(ThreadLocalRandom.current())
def apply(): IdGenerator = random(RandomNumberGenerator.get())

/**
* @return a random sequence of ids for production
*/
def random(rand: java.util.Random): IdGenerator =
() => (rand.nextInt(UnsignedShortBound) + Short.MinValue).toShort

/**
* @return a random sequence of ids for production
*/
private def random(rand: RandomNumberGenerator): IdGenerator =
() => (rand.nextInt(UnsignedShortBound) + Short.MinValue).toShort

private[pekko] class EnhancedDoubleHashGenerator(seed: Random) extends IdGenerator {

/**
Expand Down
Loading