From 63829efdcc89bee843bbc9d316f0644d2a7e35b8 Mon Sep 17 00:00:00 2001 From: Arman Bilge Date: Sun, 15 Mar 2026 00:47:13 +0000 Subject: [PATCH 1/2] Workaround #4524 by synchronizing tasks set --- .../cats/effect/kernel/testkit/TestContext.scala | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/kernel-testkit/shared/src/main/scala/cats/effect/kernel/testkit/TestContext.scala b/kernel-testkit/shared/src/main/scala/cats/effect/kernel/testkit/TestContext.scala index c4857fceb7..8846fdd1a4 100644 --- a/kernel-testkit/shared/src/main/scala/cats/effect/kernel/testkit/TestContext.scala +++ b/kernel-testkit/shared/src/main/scala/cats/effect/kernel/testkit/TestContext.scala @@ -24,7 +24,7 @@ import scala.concurrent.duration._ import scala.util.{Random, Try} import scala.util.control.NonFatal -import java.util.{Base64, Comparator} +import java.util.{Base64, Comparator, Collections} import java.util.concurrent.ConcurrentSkipListSet import java.util.concurrent.atomic.{AtomicLong, AtomicReference} @@ -60,11 +60,11 @@ import java.util.concurrent.atomic.{AtomicLong, AtomicReference} * }}} */ final class TestContext private (_seed: Long) extends ExecutionContext { self => - import TestContext.{ConcurrentState, Encoder, State, Task} + import TestContext.{InternalState, Encoder, State, Task} private[this] val random = new Random(_seed) - private[this] val stateRef = new ConcurrentState() + private[this] val stateRef = new InternalState() def execute(runnable: Runnable): Unit = { val current = stateRef @@ -280,6 +280,7 @@ object TestContext { def apply(seed: String): TestContext = new TestContext(new String(Decoder.decode(seed)).toLong) + @deprecated("Not intended to be public", "3.7.1") final class ConcurrentState( val currentID: AtomicLong = new AtomicLong(), val currentNanos: AtomicLong = new AtomicLong(), @@ -287,6 +288,13 @@ object TestContext { val lastReportedFailure: AtomicReference[Throwable] = new AtomicReference() ) + private final class InternalState( + val currentID: AtomicLong = new AtomicLong(), + val currentNanos: AtomicLong = new AtomicLong(), + val tasks: java.util.SortedSet[Task] = Collections.synchronizedSortedSet(new ConcurrentSkipListSet(Task.comparator)), + val lastReportedFailure: AtomicReference[Throwable] = new AtomicReference() + ) + /** * Used internally by [[TestContext]], represents the internal state used for task scheduling * and execution. From 7fcf2b8209d804377c9ad002d38189155f4c42f8 Mon Sep 17 00:00:00 2001 From: Arman Bilge Date: Sun, 15 Mar 2026 01:00:15 +0000 Subject: [PATCH 2/2] Format and organize imports --- .../scala/cats/effect/kernel/testkit/TestContext.scala | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/kernel-testkit/shared/src/main/scala/cats/effect/kernel/testkit/TestContext.scala b/kernel-testkit/shared/src/main/scala/cats/effect/kernel/testkit/TestContext.scala index 8846fdd1a4..d1926afc6e 100644 --- a/kernel-testkit/shared/src/main/scala/cats/effect/kernel/testkit/TestContext.scala +++ b/kernel-testkit/shared/src/main/scala/cats/effect/kernel/testkit/TestContext.scala @@ -24,7 +24,7 @@ import scala.concurrent.duration._ import scala.util.{Random, Try} import scala.util.control.NonFatal -import java.util.{Base64, Comparator, Collections} +import java.util.{Base64, Collections, Comparator} import java.util.concurrent.ConcurrentSkipListSet import java.util.concurrent.atomic.{AtomicLong, AtomicReference} @@ -288,10 +288,11 @@ object TestContext { val lastReportedFailure: AtomicReference[Throwable] = new AtomicReference() ) - private final class InternalState( + private final class InternalState( val currentID: AtomicLong = new AtomicLong(), val currentNanos: AtomicLong = new AtomicLong(), - val tasks: java.util.SortedSet[Task] = Collections.synchronizedSortedSet(new ConcurrentSkipListSet(Task.comparator)), + val tasks: java.util.SortedSet[Task] = + Collections.synchronizedSortedSet(new ConcurrentSkipListSet(Task.comparator)), val lastReportedFailure: AtomicReference[Throwable] = new AtomicReference() )