Skip to content

Fix WithinAsync timeout not propagating to EventFilter across async boundaries#7977

Merged
Aaronontheweb merged 6 commits into
akkadotnet:devfrom
Aaronontheweb:fix/within-async-timeout-propagation
Dec 29, 2025
Merged

Fix WithinAsync timeout not propagating to EventFilter across async boundaries#7977
Aaronontheweb merged 6 commits into
akkadotnet:devfrom
Aaronontheweb:fix/within-async-timeout-propagation

Conversation

@Aaronontheweb

Copy link
Copy Markdown
Member

Summary

  • Fixed race condition where WithinAsync timeout was not reliably propagating to EventFilter and other async operations
  • Use AsyncLocal<TimeSpan?> to properly flow timeout across async boundaries

Root Cause

The _testState.End field set by WithinAsync is a plain instance property without volatile semantics. When EventFilter reads this value through RemainingOrDefault on a different thread (via async continuation), it could see a stale cache value due to CPU cache coherency delays. This caused EventFilter to occasionally fall back to the default 3000ms timeout instead of the timeout set by WithinAsync.

Fix

  • Added AsyncLocal<TimeSpan?> field that automatically flows through async contexts via ExecutionContext
  • WithinAsync now sets/restores both the instance field (for sync code paths) and the AsyncLocal (for async code paths)
  • Remaining property and RemainingOr method check AsyncLocal first, then fall back to instance field

Test plan

  • Specific test WithinAsync_timeout_should_propagate_to_EventFilter passes consistently (10/10 runs)
  • Full Akka.TestKit.Tests suite passes (293 passed, 1 skipped, 0 failed)

…oundaries

Use AsyncLocal<TimeSpan?> to properly propagate WithinAsync timeout to
EventFilter and other async operations. The previous implementation used
a plain instance field (_testState.End) which could have memory visibility
issues when accessed from different threads across await boundaries.

The fix:
- Adds AsyncLocal<TimeSpan?> field that flows through async contexts
- WithinAsync sets/restores both instance field and AsyncLocal
- Remaining and RemainingOr check AsyncLocal first, then instance field
- Preserves backward compatibility with sync code paths
@Aaronontheweb Aaronontheweb force-pushed the fix/within-async-timeout-propagation branch 2 times, most recently from d8cbbd6 to 8784771 Compare December 23, 2025 16:25
@Aaronontheweb Aaronontheweb enabled auto-merge (squash) December 23, 2025 17:26
@Aaronontheweb Aaronontheweb added the akka-testkit Akka.NET Testkit issues label Dec 29, 2025

@Arkatufus Arkatufus left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One minor question

Comment thread src/core/Akka.TestKit/TestKitBase.cs Outdated

@Arkatufus Arkatufus left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@Aaronontheweb Aaronontheweb merged commit 2163de1 into akkadotnet:dev Dec 29, 2025
8 of 11 checks passed
@Aaronontheweb Aaronontheweb deleted the fix/within-async-timeout-propagation branch December 29, 2025 20:43
Arkatufus added a commit to Arkatufus/akka.net that referenced this pull request Jan 7, 2026
…oundaries (akkadotnet#7977)

* Fix WithinAsync timeout not propagating to EventFilter across async boundaries

Use AsyncLocal<TimeSpan?> to properly propagate WithinAsync timeout to
EventFilter and other async operations. The previous implementation used
a plain instance field (_testState.End) which could have memory visibility
issues when accessed from different threads across await boundaries.

The fix:
- Adds AsyncLocal<TimeSpan?> field that flows through async contexts
- WithinAsync sets/restores both instance field and AsyncLocal
- Remaining and RemainingOr check AsyncLocal first, then instance field
- Preserves backward compatibility with sync code paths

* remove `static`

---------

Co-authored-by: Gregorius Soedharmo <arkatufus@yahoo.com>
Aaronontheweb added a commit that referenced this pull request Jan 8, 2026
…oundaries (#7977)

* Fix WithinAsync timeout not propagating to EventFilter across async boundaries

Use AsyncLocal<TimeSpan?> to properly propagate WithinAsync timeout to
EventFilter and other async operations. The previous implementation used
a plain instance field (_testState.End) which could have memory visibility
issues when accessed from different threads across await boundaries.

The fix:
- Adds AsyncLocal<TimeSpan?> field that flows through async contexts
- WithinAsync sets/restores both instance field and AsyncLocal
- Remaining and RemainingOr check AsyncLocal first, then instance field
- Preserves backward compatibility with sync code paths

* remove `static`

---------

Co-authored-by: Gregorius Soedharmo <arkatufus@yahoo.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

akka-testkit Akka.NET Testkit issues

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants