You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
TimeProvider.System.CreateTimer returns a SystemTimeProviderTimer.
Unlike System.Threading.Timer, it is implicitly rooted timer, meaning that when the variable to which it was assigned is eligible for GC, it will still work until Disposed.
This is deliberately not the case with FakeTimeProviders implementation for CreateTimer since it returns a version which is more like System.Threading.Timer and less than SystemTimeProviderTimer. From the comments for Waiter's class:
// We keep all timer state here in order to prevent Timer instances from being self-referential,
// which would block them being collected when someone forgets to call Dispose on the timer. With
// this arrangement, the Timer object will always be collectible, which will end up calling Dispose
// on this object due to the timer's finalizer.
But what if I forget to dispose the the Timer and depend on this implicitly-rooted behavior of timers that is promised to me for TimeProvider.CreateTimer
The return ITimer instance will be implicitly rooted while the timer is still scheduled.
I think the current implementation breaks the contract from the documentation for the abstract class and will cause certain tests to fail where they won't when using the default TimeScheduler.System.
Short reproducible code:
void Main() {
TimeProvider timeProvider = TimeProvider.System;
var timerStarter = new TimerStarter();
timerStarter.Start(timeProvider, _ => Console.WriteLine("System"),
null, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1));
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
Thread.Sleep(5000);
Console.WriteLine("--------------");
// System
var fakeTimeProvider = new FakeTimeProvider();
timerStarter.Start(fakeTimeProvider, _ => Console.WriteLine("Fake"),
null, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1));
// Simulate GC during testing
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
fakeTimeProvider.Advance(TimeSpan.FromSeconds(5));
Thread.Sleep(5000);
}
class TimerStarter {
public void Start(TimeProvider timeProvider,
TimerCallback callback,
object? state,
TimeSpan dueDate,
TimeSpan period
) {
timeProvider.CreateTimer(callback, state, dueDate, period);
}
}
The text was updated successfully, but these errors were encountered:
TimeProvider.System.CreateTimer
returns aSystemTimeProviderTimer
.Unlike
System.Threading.Timer
, it is implicitly rooted timer, meaning that when the variable to which it was assigned is eligible for GC, it will still work untilDisposed
.This is deliberately not the case with
FakeTimeProvider
s implementation forCreateTimer
since it returns a version which is more likeSystem.Threading.Timer
and less thanSystemTimeProviderTimer
. From the comments forWaiter
's class:But what if I forget to dispose the the Timer and depend on this implicitly-rooted behavior of timers that is promised to me for
TimeProvider.CreateTimer
I think the current implementation breaks the contract from the documentation for the abstract class and will cause certain tests to fail where they won't when using the default
TimeScheduler.System
.Short reproducible code:
The text was updated successfully, but these errors were encountered: