Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix potential infinite loop in FakeTimeProvider when a timer callback changes the current time #4277

Merged
merged 1 commit into from
Aug 11, 2023

Conversation

geeknoid
Copy link
Member

@geeknoid geeknoid commented Aug 11, 2023

Fixes #4275

Microsoft Reviewers: Open in CodeFlow

@ghost ghost assigned geeknoid Aug 11, 2023
@geeknoid geeknoid merged commit 08c93ef into main Aug 11, 2023
6 checks passed
@geeknoid geeknoid deleted the geeknoid/timeprovider branch August 11, 2023 21:12
@ghost ghost added this to the 8.0 RC1 milestone Aug 11, 2023
Copy link
Contributor

@egil egil left a comment

Choose a reason for hiding this comment

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

Looks good to me.

Comment on lines +259 to +268
if (oldTicks != newTicks)
{
// time changed while in the callback, readjust the wake time accordingly
candidate.WakeupTime = newTicks + candidate.Period;
}
else
{
// move on to the next period
candidate.WakeupTime += candidate.Period;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Good solution. This is safe because the current thread that is invoking the waiters/callback is the only one that can change _ now, since it is protected by the lock, and it is only that thread that can reenter the lock. So a change to _now could only come from the same thread.

Is my understanding correct?

Copy link
Member Author

Choose a reason for hiding this comment

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

Exactly :-)

@@ -18,7 +18,7 @@ public class FakeTimeProvider : TimeProvider
internal readonly HashSet<Waiter> Waiters = new();
private DateTimeOffset _now = new(2000, 1, 1, 0, 0, 0, 0, TimeSpan.Zero);
private TimeZoneInfo _localTimeZone = TimeZoneInfo.Utc;
private int _wakeWaitersGate;
private volatile int _wakeWaitersGate;
Copy link
Contributor

Choose a reason for hiding this comment

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

Didn't think volatile was needed when you read the value using Interlocked.CompareExchange?

Is it because it is being set by direct assignment later?

Copy link
Member Author

Choose a reason for hiding this comment

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

Didn't think volatile was needed when you read the value using Interlocked.CompareExchange?

Is it because it is being set by direct assignment later?

Yes, just as an added precaution against funny business in the compiler.

@ghost ghost locked as resolved and limited conversation to collaborators Sep 11, 2023
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.

Changing FakeTimeProvider's current time during a timer callback can lead to lack of forward progress
3 participants