You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@pekko.apache.org by "He-Pin (via GitHub)" <gi...@apache.org> on 2026/06/27 22:00:24 UTC

[I] ManualTime.expectNoMessageFor uses Duration.Zero causing race condition [pekko]

He-Pin opened a new issue, #3235:
URL: https://github.com/apache/pekko/issues/3235

   ### Motivation
   `ManualTime.expectNoMessageFor` uses `Duration.Zero` when checking test probes for unexpected messages after advancing the clock. Because scheduled tasks run synchronously on the calling thread inside `timePasses`, but messages they send to test probe actors are delivered asynchronously through the actor system's dispatcher/mailbox, the zero-timeout poll can miss messages that are still in flight. This produces false-negative test results: assertions that "no messages were received" pass when they should fail.
   
   ### Current behavior
   In `actor-testkit-typed/src/main/scala/org/apache/pekko/actor/testkit/typed/scaladsl/ManualTime.scala:76-79`:
   
   ```scala
   def expectNoMessageFor(duration: FiniteDuration, on: TestProbe[?]*): Unit = {
       delegate.timePasses(duration)
       on.foreach(_.expectNoMessage(Duration.Zero))
   }
   ```
   
   The call chain is:
   1. `delegate.timePasses(duration)` runs scheduled `Runnable`s synchronously on the calling thread (`ExplicitlyTriggeredScheduler.scala:98`).
   2. Those runnables may send messages to test probe actors via `!`, enqueuing them in the actor's mailbox.
   3. The test probe actor processes messages on a separate dispatcher thread, calling `queue.offerLast(other)` (`TestProbeImpl.scala:66`).
   4. `expectNoMessage(Duration.Zero)` calls `receiveOne_internal(Duration.Zero)`, which does a non-blocking `queue.pollFirst()` (`TestProbeImpl.scala:202-203`) with zero wait.
   
   Between steps 2 and 4, there is no synchronization or delay. Messages dispatched in step 2 may not yet be in the probe's `LinkedBlockingDeque` when step 4 polls.
   
   The same issue exists in the Java DSL at `actor-testkit-typed/src/main/scala/org/apache/pekko/actor/testkit/typed/javadsl/ManualTime.scala:76-79`.
   
   Notably, `ExplicitlyTriggeredScheduler.timePasses` itself includes `Thread.sleep(100)` at its start (`ExplicitlyTriggeredScheduler.scala:72`) with a comment about giving "dispatchers time to clear," acknowledging that async message delivery races exist in this context. However, no equivalent delay exists between task execution and the `Duration.Zero` check.
   
   ### Expected behavior
   After `timePasses` completes, the `expectNoMessage` check should allow sufficient time for any messages dispatched by the scheduled tasks to be delivered to the test probe actors. The idiomatic fix is to use the probe's default no-message timeout via `expectNoMessage()` (no arguments), which uses `settings.ExpectNoMessageDefaultTimeout` (defined in `TestKitSettings.scala:77`). This timeout is dilated by the test time factor and is specifically designed for "expect no message" assertions.
   
   ### Code evidence
   - `actor-testkit-typed/src/main/scala/org/apache/pekko/actor/testkit/typed/scaladsl/ManualTime.scala:78` — `Duration.Zero` in Scala DSL
   - `actor-testkit-typed/src/main/scala/org/apache/pekko/actor/testkit/typed/javadsl/ManualTime.scala:78` — `Duration.ZERO` in Java DSL
   - `actor-testkit-typed/src/main/scala/org/apache/pekko/actor/testkit/typed/internal/TestProbeImpl.scala:202-203` — zero duration triggers non-blocking `pollFirst`
   - `actor-testkit-typed/src/main/scala/org/apache/pekko/actor/testkit/typed/internal/TestProbeImpl.scala:217-218` — `expectNoMessage()` (no args) uses `ExpectNoMessageDefaultTimeout`
   - `testkit/src/main/scala/org/apache/pekko/testkit/ExplicitlyTriggeredScheduler.scala:72` — existing `Thread.sleep(100)` acknowledging the race pattern
   
   ### Reproduction
   Create a test using `ManualTime` where a scheduled task sends a message to a test probe actor. Call `expectNoMessageFor` with a duration that triggers the scheduled task. Due to the race, the test may intermittently pass (no message detected) even though the probe should have received a message during the time advance.
   
   A deterministic reproduction would require a slow dispatcher or deliberate scheduling to widen the race window, but the fundamental issue is that the zero-timeout check is unsound.
   
   ### Impact
   - **Modules affected:** `pekko-actor-testkit-typed` (both Scala and Java DSLs)
   - **Use cases affected:** Any test using `ManualTime.expectNoMessageFor` to verify that no messages are delivered during a time advance. This is a core testing utility documented in the official Pekko testing examples (`ManualTimerExampleSpec.scala`).
   - **Severity:** Intermittent false-negative test results. Tests may incorrectly pass when a bug exists, reducing confidence in the test suite.


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@pekko.apache.org.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@pekko.apache.org
For additional commands, e-mail: notifications-help@pekko.apache.org


Re: [I] ManualTime.expectNoMessageFor uses Duration.Zero causing race condition [pekko]

Posted by "pjfanning (via GitHub)" <gi...@apache.org>.
pjfanning commented on issue #3235:
URL: https://github.com/apache/pekko/issues/3235#issuecomment-4822628375

   @He-Pin we seem to have 2 open copies of most of the new issues (ignoring the closed ones that were generated in Chinese).
   This one, for instance, is duplicated by #3242 


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@pekko.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@pekko.apache.org
For additional commands, e-mail: notifications-help@pekko.apache.org


Re: [I] ManualTime.expectNoMessageFor uses Duration.Zero causing race condition [pekko]

Posted by "He-Pin (via GitHub)" <gi...@apache.org>.
He-Pin closed issue #3235: ManualTime.expectNoMessageFor uses Duration.Zero causing race condition
URL: https://github.com/apache/pekko/issues/3235


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@pekko.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@pekko.apache.org
For additional commands, e-mail: notifications-help@pekko.apache.org