Use Stopwatch for LockableContext acquire timeouts#1772
Open
unsafePtr wants to merge 2 commits intomicrosoft:mainfrom
Open
Use Stopwatch for LockableContext acquire timeouts#1772unsafePtr wants to merge 2 commits intomicrosoft:mainfrom
unsafePtr wants to merge 2 commits intomicrosoft:mainfrom
Conversation
Contributor
There was a problem hiding this comment.
Pull request overview
This PR updates Tsavorite's manual lock-acquisition retry loops to measure elapsed timeout using Stopwatch instead of DateTime.UtcNow, so lock and lock-promotion timeouts are based on monotonic time and are no longer affected by wall-clock adjustments.
Changes:
- Replaced wall-clock timeout tracking in
DoManualTryLockwithStopwatch.GetTimestamp()/Stopwatch.GetElapsedTime. - Replaced wall-clock timeout tracking in
DoManualTryPromoteLockwith the same monotonic timing pattern.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
|
||
| // Lock failure is the only place we check the timeout. If we've exceeded that, or if we've had a cancellation, return false. | ||
| if (cancellationToken.IsCancellationRequested || DateTime.UtcNow.Ticks - startTime.Ticks > timeout.Ticks) | ||
| if (cancellationToken.IsCancellationRequested || Stopwatch.GetElapsedTime(startTimestamp) > timeout) |
Contributor
There was a problem hiding this comment.
This does appear to be a real (existing before this change) bug that is worth fixing, Timeout.InfiniteTimeSpan is < 0.
|
|
||
| // CancellationToken can accompany either of the other two mechanisms | ||
| if (cancellationToken.IsCancellationRequested || DateTime.UtcNow.Ticks - startTime.Ticks > timeout.Ticks) | ||
| if (cancellationToken.IsCancellationRequested || Stopwatch.GetElapsedTime(startTimestamp) > timeout) |
kevin-montrose
requested changes
May 8, 2026
|
|
||
| // Lock failure is the only place we check the timeout. If we've exceeded that, or if we've had a cancellation, return false. | ||
| if (cancellationToken.IsCancellationRequested || DateTime.UtcNow.Ticks - startTime.Ticks > timeout.Ticks) | ||
| if (cancellationToken.IsCancellationRequested || Stopwatch.GetElapsedTime(startTimestamp) > timeout) |
Contributor
There was a problem hiding this comment.
This does appear to be a real (existing before this change) bug that is worth fixing, Timeout.InfiniteTimeSpan is < 0.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
DoManualTryLockandDoManualTryPromoteLockuseDateTime.UtcNow.Ticksdeltas to enforce the caller-suppliedTimeSpan timeout. A wall-clock adjustment (NTP step, manual change) lands the comparison on a perturbed value: a backward step keeps retrying past the intended deadline, a forward step gives up early. Both methods are pure local timeout loops — no protocol or persistence concerns — so monotonic time is the right primitive.Switch to
Stopwatch.GetTimestamp()/Stopwatch.GetElapsedTimefor the elapsed-time math. Behavior under stable clocks is unchanged; the timeout contract is now enforced against a clock that can't move sideways.Same NTP-resilience reasoning was applied recently in dotnet/runtime#127303 for
EventCounter's polling timer.Changes
DoManualTryLockandDoManualTryPromoteLockinLockableContext.cscaptureStopwatch.GetTimestamp()at entry and gate retries onStopwatch.GetElapsedTime(startTimestamp) > timeout.Test plan
TryLockTimeSpanLimitTest,TryLockCancellationTest,TryPromoteLockTimeSpanLimitTest,TryPromoteLockCancellationTestpass onnet8.0andnet10.0.Tsavorite.corebuilds clean.