From 11d63c6aefe06a62e4cd1560e82373102a83163c Mon Sep 17 00:00:00 2001 From: counter2015 Date: Mon, 28 Oct 2024 19:59:52 +0800 Subject: [PATCH 1/5] check target array length before allocating. --- .../src/main/scala/cats/effect/ArrayStack.scala | 13 ++++++++++++- .../main/scala/cats/effect/PlatformStatics.scala | 8 ++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 core/jvm-native/src/main/scala/cats/effect/PlatformStatics.scala diff --git a/core/jvm-native/src/main/scala/cats/effect/ArrayStack.scala b/core/jvm-native/src/main/scala/cats/effect/ArrayStack.scala index ae5d5f7e25..6375cbccb3 100644 --- a/core/jvm-native/src/main/scala/cats/effect/ArrayStack.scala +++ b/core/jvm-native/src/main/scala/cats/effect/ArrayStack.scala @@ -17,6 +17,7 @@ package cats.effect import Platform.static +import PlatformStatics.VM_MaxArraySize private final class ArrayStack[A <: AnyRef]( private[this] var buffer: Array[AnyRef], @@ -72,7 +73,17 @@ private final class ArrayStack[A <: AnyRef]( private[this] def checkAndGrow(): Unit = if (index >= buffer.length) { val len = buffer.length - val buffer2 = new Array[AnyRef](len * 2) + val targetLen = len * 2 + + val resizeLen = + if (targetLen < 0) + throw new Exception(s"Overflow while resizing array. Request length: $targetLen") + else if (len > VM_MaxArraySize / 2) + VM_MaxArraySize + else + targetLen + + val buffer2 = new Array[AnyRef](resizeLen) System.arraycopy(buffer, 0, buffer2, 0, len) buffer = buffer2 } diff --git a/core/jvm-native/src/main/scala/cats/effect/PlatformStatics.scala b/core/jvm-native/src/main/scala/cats/effect/PlatformStatics.scala new file mode 100644 index 0000000000..e7ddfc579d --- /dev/null +++ b/core/jvm-native/src/main/scala/cats/effect/PlatformStatics.scala @@ -0,0 +1,8 @@ +package cats.effect + +// copy from scala.runtime.PStatics +private[effect] object PlatformStatics { + // `Int.MaxValue - 8` traditional soft limit to maximize compatibility with diverse JVMs + // See https://stackoverflow.com/a/8381338 for example + final val VM_MaxArraySize = 2147483639 +} From d212861b209ee1b48c3577756efa4da91c6c311f Mon Sep 17 00:00:00 2001 From: counter2015 Date: Wed, 30 Oct 2024 17:41:41 +0800 Subject: [PATCH 2/5] add copy right header. --- .../main/scala/cats/effect/PlatformStatics.scala | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/core/jvm-native/src/main/scala/cats/effect/PlatformStatics.scala b/core/jvm-native/src/main/scala/cats/effect/PlatformStatics.scala index e7ddfc579d..a156ae08dc 100644 --- a/core/jvm-native/src/main/scala/cats/effect/PlatformStatics.scala +++ b/core/jvm-native/src/main/scala/cats/effect/PlatformStatics.scala @@ -1,3 +1,19 @@ +/* + * Copyright 2020-2024 Typelevel + * + * Licensed 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 cats.effect // copy from scala.runtime.PStatics From 65b93a3064ba2f809f53ac28cc62777b18e5165e Mon Sep 17 00:00:00 2001 From: counter2015 Date: Wed, 30 Oct 2024 19:23:57 +0800 Subject: [PATCH 3/5] add ArrayStack benchmark. --- .../benchmarks/ArrayStackBenchmark.scala | 57 +++++++++++++++++++ .../main/scala/cats/effect/ArrayStack.scala | 2 +- 2 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 benchmarks/src/main/scala/cats/effect/benchmarks/ArrayStackBenchmark.scala diff --git a/benchmarks/src/main/scala/cats/effect/benchmarks/ArrayStackBenchmark.scala b/benchmarks/src/main/scala/cats/effect/benchmarks/ArrayStackBenchmark.scala new file mode 100644 index 0000000000..1897a99e6b --- /dev/null +++ b/benchmarks/src/main/scala/cats/effect/benchmarks/ArrayStackBenchmark.scala @@ -0,0 +1,57 @@ +/* + * Copyright 2020-2024 Typelevel + * + * Licensed 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 cats.effect.benchmarks + +import cats.effect.ArrayStack +import org.openjdk.jmh.annotations.* + +import java.util.concurrent.TimeUnit + +/** + * To do comparative benchmarks between versions: + * + * benchmarks/run-benchmark ArrayStackBenchmark + * + * This will generate results in `benchmarks/results`. + * + * Or to run the benchmark from within sbt: + * + * Jmh / run -i 10 -wi 10 -f 2 -t 1 cats.effect.benchmarks.ArrayStackBenchmark + * + * Which means "10 iterations", "10 warm-up iterations", "2 forks", "1 thread". Please note that + * benchmarks should be usually executed at least in 10 iterations (as a rule of thumb), but + * more is better. + */ +@State(Scope.Thread) +@BenchmarkMode(Array(Mode.Throughput)) +@OutputTimeUnit(TimeUnit.SECONDS) +class ArrayStackBenchmark { + + @Param(Array("1000000")) + var size: Int = _ + + @Benchmark + def push(): Unit = { + val stack: ArrayStack[Integer] = ArrayStack[Integer](size) + + var i = 0 + while (i < size) { + stack.push(i) + i += 1 + } + } +} diff --git a/core/jvm-native/src/main/scala/cats/effect/ArrayStack.scala b/core/jvm-native/src/main/scala/cats/effect/ArrayStack.scala index 6375cbccb3..e3987791e9 100644 --- a/core/jvm-native/src/main/scala/cats/effect/ArrayStack.scala +++ b/core/jvm-native/src/main/scala/cats/effect/ArrayStack.scala @@ -19,7 +19,7 @@ package cats.effect import Platform.static import PlatformStatics.VM_MaxArraySize -private final class ArrayStack[A <: AnyRef]( +private[effect] final class ArrayStack[A <: AnyRef]( private[this] var buffer: Array[AnyRef], private[this] var index: Int) { From 57b496a6d9a40fd9e0d746a5d592d7a4f5708b3a Mon Sep 17 00:00:00 2001 From: counter2015 Date: Thu, 31 Oct 2024 10:49:57 +0800 Subject: [PATCH 4/5] run prePR command. --- .../scala/cats/effect/benchmarks/ArrayStackBenchmark.scala | 3 ++- core/jvm-native/src/main/scala/cats/effect/ArrayStack.scala | 3 ++- core/jvm-native/src/main/scala/cats/effect/CallbackStack.scala | 3 +-- core/shared/src/main/scala/cats/effect/IO.scala | 3 ++- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/benchmarks/src/main/scala/cats/effect/benchmarks/ArrayStackBenchmark.scala b/benchmarks/src/main/scala/cats/effect/benchmarks/ArrayStackBenchmark.scala index 1897a99e6b..d19c3e8445 100644 --- a/benchmarks/src/main/scala/cats/effect/benchmarks/ArrayStackBenchmark.scala +++ b/benchmarks/src/main/scala/cats/effect/benchmarks/ArrayStackBenchmark.scala @@ -17,7 +17,8 @@ package cats.effect.benchmarks import cats.effect.ArrayStack -import org.openjdk.jmh.annotations.* + +import org.openjdk.jmh.annotations._ import java.util.concurrent.TimeUnit diff --git a/core/jvm-native/src/main/scala/cats/effect/ArrayStack.scala b/core/jvm-native/src/main/scala/cats/effect/ArrayStack.scala index e3987791e9..c19114ca02 100644 --- a/core/jvm-native/src/main/scala/cats/effect/ArrayStack.scala +++ b/core/jvm-native/src/main/scala/cats/effect/ArrayStack.scala @@ -16,9 +16,10 @@ package cats.effect -import Platform.static import PlatformStatics.VM_MaxArraySize +import Platform.static + private[effect] final class ArrayStack[A <: AnyRef]( private[this] var buffer: Array[AnyRef], private[this] var index: Int) { diff --git a/core/jvm-native/src/main/scala/cats/effect/CallbackStack.scala b/core/jvm-native/src/main/scala/cats/effect/CallbackStack.scala index bad66991eb..91ce2520e9 100644 --- a/core/jvm-native/src/main/scala/cats/effect/CallbackStack.scala +++ b/core/jvm-native/src/main/scala/cats/effect/CallbackStack.scala @@ -20,8 +20,7 @@ import scala.annotation.tailrec import java.util.concurrent.atomic.{AtomicBoolean, AtomicReference} -import CallbackStack.Handle -import CallbackStack.Node +import CallbackStack.{Handle, Node} import Platform.static private final class CallbackStack[A](private[this] var callback: A => Unit) diff --git a/core/shared/src/main/scala/cats/effect/IO.scala b/core/shared/src/main/scala/cats/effect/IO.scala index ee3a9da1fc..140da7866c 100644 --- a/core/shared/src/main/scala/cats/effect/IO.scala +++ b/core/shared/src/main/scala/cats/effect/IO.scala @@ -36,7 +36,6 @@ import cats.{ StackSafeMonad, Traverse } -import cats.data.Ior import cats.effect.instances.spawn import cats.effect.kernel.CancelScope import cats.effect.kernel.GenTemporal.handleDuration @@ -57,6 +56,8 @@ import java.util.concurrent.Executor import Platform.static +import cats.data.Ior + /** * A pure abstraction representing the intention to perform a side effect, where the result of * that side effect may be obtained synchronously (via return) or asynchronously (via callback). From 3f00eed3ab0e1e68d1941aa57e74f3e57690feec Mon Sep 17 00:00:00 2001 From: counter2015 Date: Fri, 22 Nov 2024 13:29:56 +0800 Subject: [PATCH 5/5] run clean prePR command. --- core/jvm-native/src/main/scala/cats/effect/ArrayStack.scala | 1 - core/shared/src/main/scala/cats/effect/IO.scala | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/core/jvm-native/src/main/scala/cats/effect/ArrayStack.scala b/core/jvm-native/src/main/scala/cats/effect/ArrayStack.scala index c19114ca02..6ea74dcbdf 100644 --- a/core/jvm-native/src/main/scala/cats/effect/ArrayStack.scala +++ b/core/jvm-native/src/main/scala/cats/effect/ArrayStack.scala @@ -17,7 +17,6 @@ package cats.effect import PlatformStatics.VM_MaxArraySize - import Platform.static private[effect] final class ArrayStack[A <: AnyRef]( diff --git a/core/shared/src/main/scala/cats/effect/IO.scala b/core/shared/src/main/scala/cats/effect/IO.scala index 140da7866c..ee3a9da1fc 100644 --- a/core/shared/src/main/scala/cats/effect/IO.scala +++ b/core/shared/src/main/scala/cats/effect/IO.scala @@ -36,6 +36,7 @@ import cats.{ StackSafeMonad, Traverse } +import cats.data.Ior import cats.effect.instances.spawn import cats.effect.kernel.CancelScope import cats.effect.kernel.GenTemporal.handleDuration @@ -56,8 +57,6 @@ import java.util.concurrent.Executor import Platform.static -import cats.data.Ior - /** * A pure abstraction representing the intention to perform a side effect, where the result of * that side effect may be obtained synchronously (via return) or asynchronously (via callback).