He-Pin opened a new pull request, #3086:
URL: https://github.com/apache/pekko/pull/3086

   ### Motivation
   
   When `context.cancelReceiveTimeout()` is called while a classic 
`ReceiveTimeout` message has already been enqueued in the mailbox by the 
scheduler, the typed actor crashes with a `NullPointerException` inside 
`InterceptorImpl.receive`.
   
   **Root cause trace:**
   
   1. `setReceiveTimeout(d, msg)` → sets `receiveTimeoutMsg = msg`, schedules 
`classic.ReceiveTimeout` via `scheduleOnce`
   2. Before the actor processes the enqueued `ReceiveTimeout`, another message 
triggers `cancelReceiveTimeout()`
   3. `cancelReceiveTimeout()` sets `receiveTimeoutMsg = null` and calls 
`classicActorContext.setReceiveTimeout(Duration.Undefined)`
   4. The already-enqueued `classic.ReceiveTimeout` **cannot be retracted** 
from the mailbox (the `Cancellable.cancel()` only prevents future scheduling, 
not already-delivered messages)
   5. `ActorAdapter.aroundReceive` matches `classic.ReceiveTimeout` and calls 
`handleMessage(ctx.receiveTimeoutMsg)` — **with null**
   6. The null message propagates through `Behavior.interpretMessage` → 
`InterceptorImpl.receive` → **`msg.getClass` throws NPE**
   
   This bug was observed in both 2.8.3 and 2.6.21 of the upstream project. A 
reproduction is available at 
https://github.com/lolboxen/akka-receive-timeout-bug.
   
   ### Modification
   
   - `ActorAdapter.aroundReceive`: null-check `ctx.receiveTimeoutMsg` before 
forwarding to `handleMessage`. If null (timeout was cancelled), the stale 
`classic.ReceiveTimeout` is silently discarded.
   - Added `CancelReceiveTimeoutSpec`: a deterministic regression test that 
directly sends `classic.ReceiveTimeout` to a typed actor after 
`cancelReceiveTimeout()` has been called, verifying the actor survives without 
NPE. The test uses an interceptor in the behavior stack to exercise the exact 
`InterceptorImpl.receive` code path where the NPE manifests.
   
   ### Result
   
   Typed actors no longer crash when `cancelReceiveTimeout()` races with an 
already-enqueued stale `ReceiveTimeout`. The fix is a single null-guard at the 
adapter layer — the typed behavior stack never sees a null message.
   
   ### Tests
   
   ```
   sbt "actor-typed-tests / Test / testOnly 
org.apache.pekko.actor.typed.CancelReceiveTimeoutSpec"
   → All tests passed
   
   sbt "actor-typed-tests / Test / testOnly 
org.apache.pekko.actor.typed.ActorContextSpec 
org.apache.pekko.actor.typed.TimerSpec 
org.apache.pekko.actor.typed.CancelReceiveTimeoutSpec"
   → 14 tests passed, 0 failed
   ```
   
   The regression test was verified to **fail without the fix** (NPE at 
`InterceptorImpl.scala:94`) and **pass with the fix**.
   
   ### References
   
   - Reproduction: https://github.com/lolboxen/akka-receive-timeout-bug


-- 
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: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to