Skip to content

Fix race in ScanIteratorBase.Dispose causing intermittent ObjectDisposedException#1667

Merged
badrishc merged 1 commit into
devfrom
badrishc/insdelins-fix
Apr 6, 2026
Merged

Fix race in ScanIteratorBase.Dispose causing intermittent ObjectDisposedException#1667
badrishc merged 1 commit into
devfrom
badrishc/insdelins-fix

Conversation

@badrishc

@badrishc badrishc commented Apr 3, 2026

Copy link
Copy Markdown
Collaborator

Fixes an intermittent test host crash (ObjectDisposedException: Cannot access a disposed object. Object name: 'CircularDiskReadBuffer') observed in CI during InsDelIns_LocalMemory.

Root cause: In ScanIteratorBase.Dispose(), loadCompletionEvents = default was inside the per-frame loop, nulling the entire array after processing frame 0. With DoublePageBuffering (frameSize=2), frame 1's completion event was never waited on, so its CircularDiskReadBuffer was disposed while AsyncReadPageWithObjectsCallback was still using it on a thread pool thread.

Fix: Move loadCompletionEvents = default from inside the loop to after the loop, ensuring all frames are properly awaited before their read buffers are disposed.

@badrishc badrishc force-pushed the badrishc/insdelins-fix branch from 51e69c5 to 05e9f4b Compare April 3, 2026 02:59
Two changes to Dispose():

1. Move 'loadCompletionEvents = default' from inside the per-frame loop to
   after the loop. With frameSize=2, the old code nulled the entire array
   on the first iteration (i=0), causing iteration i=1 to skip waiting for
   its in-flight async read. The read callback (AsyncReadPageWithObjectsCallback)
   would then access the already-disposed CircularDiskReadBuffer, crashing
   the test host with an unhandled ObjectDisposedException.

2. Separate the Wait (which can throw on cancellation) from resource cleanup.
   The event, CTS, and read buffer disposals now run unconditionally after
   the try/catch, ensuring resources are always cleaned up even when the
   wait fails.

Co-authored-by: Copilot <[email protected]>
@badrishc badrishc force-pushed the badrishc/insdelins-fix branch from 05e9f4b to 08ae91c Compare April 3, 2026 06:43
@badrishc badrishc marked this pull request as ready for review April 6, 2026 00:21
Copilot AI review requested due to automatic review settings April 6, 2026 00:21

Copilot AI 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.

Pull request overview

This PR fixes an intermittent ObjectDisposedException during disk-scan iterator teardown by ensuring all per-frame async page-load completion events are awaited and disposed correctly (especially important under double-page buffering).

Changes:

  • Refactors ScanIteratorBase.Dispose() to wait on each frame’s loadCompletionEvents[i] before disposing associated resources.
  • Moves loadCompletionEvents = default to after the per-frame loop to avoid prematurely nulling the array.
  • Ensures completion events, cancellation token sources, and read buffers are disposed even if the wait throws.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread libs/storage/Tsavorite/cs/src/core/Allocator/ScanIteratorBase.cs
@badrishc badrishc merged commit 3dc3e60 into dev Apr 6, 2026
31 of 34 checks passed
@badrishc badrishc deleted the badrishc/insdelins-fix branch April 6, 2026 00:28
@github-actions github-actions Bot locked and limited conversation to collaborators Jun 5, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants