Add Timer class polyfill for .NET Standard 1.0–1.1 via Task.Delay#81
Add Timer class polyfill for .NET Standard 1.0–1.1 via Task.Delay#81
Timer class polyfill for .NET Standard 1.0–1.1 via Task.Delay#81Conversation
Co-authored-by: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com>
|
@copilot no need for tests since we can't run tests against such old targets |
Co-authored-by: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com>
Timer class polyfill for .NET Standard 1.0–1.1 via Task.Delay
Timer class polyfill for .NET Standard 1.0–1.1 via Task.DelayTimer class polyfill for .NET Standard 1.0–1.1 via Task.Delay
There was a problem hiding this comment.
Pull request overview
Adds a System.Threading.Timer polyfill intended to enable netstandard1.0–1.1 support (implemented via Task.Delay), and removes conditional exclusions so Timer/TimeProvider.CreateTimer APIs are available on those targets.
Changes:
- Added
PolyShim/NetCore10/Timer.csimplementing aTimertype fornetstandard1.0–1.1. - Updated
PolyShim/NetCore30/Timer.csto includeDisposeAsyncpolyfill without excludingnetstandard1.0–1.1. - Updated
PolyShim/Net80/TimeProvider.csto exposeCreateTimer/SystemTimeProviderTimeronnetstandard1.0–1.1and updatedSignatures.mdaccordingly.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 5 comments.
| File | Description |
|---|---|
| PolyShim/NetCore10/Timer.cs | Introduces a Timer polyfill for netstandard1.0–1.1 using Task.Delay. |
| PolyShim/NetCore30/Timer.cs | Removes the netstandard1.0–1.1 exclusion so DisposeAsync extension applies there too. |
| PolyShim/Net80/TimeProvider.cs | Removes netstandard1.0–1.1 exclusion for CreateTimer and timer wrapper implementation. |
| PolyShim/Signatures.md | Updates signature counts and lists Timer as a polyfilled type. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
PolyShim/NetCore10/Timer.cs
Outdated
| if (dueTime > TimeSpan.Zero) | ||
| await Task.Delay(dueTime, cts.Token); | ||
|
|
||
| if (cts.IsCancellationRequested) | ||
| return; | ||
|
|
||
| _callback(_state); | ||
|
|
||
| if (period == Timeout.InfiniteTimeSpan || period <= TimeSpan.Zero) | ||
| return; |
There was a problem hiding this comment.
period <= TimeSpan.Zero is treated as “no periodic callbacks”. In the BCL, period == TimeSpan.Zero is valid and schedules callbacks back-to-back, while negative values (other than -1 / Infinite) should throw. Similarly, negative dueTime values (other than Infinite) should throw rather than being treated as immediate execution.
PolyShim/NetCore10/Timer.cs
Outdated
| public void Dispose() | ||
| { | ||
| if (_disposed) | ||
| return; | ||
| _disposed = true; | ||
| var cts = Interlocked.Exchange(ref _cts, new CancellationTokenSource()); | ||
| cts.Cancel(); | ||
| cts.Dispose(); |
There was a problem hiding this comment.
Dispose() swaps _cts with a newly-created CancellationTokenSource and never disposes that new instance. This leaves an undisposed CTS (and potentially its wait handle) rooted by the Timer instance. Consider setting _cts to null (making the field nullable) or reusing/discarding the existing CTS without allocating a replacement.
There was a problem hiding this comment.
Fixed in fed084c — Dispose now cancels and disposes _cts directly without allocating a replacement.
|
@copilot please address unresolved review comments |
Co-authored-by: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com>
Addressed in 2be4f52:
|
PolyShim/NetCore10/Timer.cs
Outdated
| public Timer(TimerCallback callback) | ||
| : this(callback, null, Timeout.InfiniteTimeSpan, Timeout.InfiniteTimeSpan) { } | ||
|
|
||
| private static void ValidateTimes(TimeSpan dueTime, TimeSpan period) |
There was a problem hiding this comment.
If it's only used in one place, inline it
There was a problem hiding this comment.
Done in fed084c — the validation is now inlined directly into CreateAndStart.
PolyShim/NetCore10/Timer.cs
Outdated
| public void Dispose() | ||
| { | ||
| if (_disposed) | ||
| return; | ||
| _disposed = true; | ||
| var cts = Interlocked.Exchange(ref _cts, new CancellationTokenSource()); | ||
| cts.Cancel(); | ||
| cts.Dispose(); |
PolyShim/NetCore10/Timer.cs
Outdated
| public void Dispose() | ||
| { | ||
| if (_disposed) | ||
| return; | ||
| _disposed = true; | ||
| var cts = Interlocked.Exchange(ref _cts, new CancellationTokenSource()); | ||
| cts.Cancel(); | ||
| cts.Dispose(); |
Co-authored-by: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com>
…uctor Co-authored-by: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 4 out of 4 changed files in this pull request and generated 6 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…e race recheck Co-authored-by: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com>
Co-authored-by: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #81 +/- ##
============================
============================
☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
PolyShim/NetCore10/Timer.cs- Timer class polyfill for netstandard 1.0-1.1 using Task.DelayPolyShim/NetCore30/Timer.cs- Remove exclusion of netstandard 1.0-1.1PolyShim/Net80/TimeProvider.cs- Remove exclusion of netstandard 1.0-1.1(callback, state); null check in the full-arg constructor chainStartcalled after CTS exchange and old CTS cancellation;tokencaptured before exchangeoldCts?.Cancel()wrapped in try/catch forObjectDisposedException(race withDispose()disposing the old CTS concurrently)_disposedrecheck also wrapscts.Cancel()in try/catch forObjectDisposedExceptionStart(CancellationToken)- pure loop-starter, returns voidSchedulevalidates args, exchanges CTS, cancels old, then starts new loopChangethrows (ObjectDisposedException / ArgumentOutOfRangeException)period == 0treated same asTimeout.InfiniteTimeSpan— fires once then stopsOperationCanceledExceptiononly caught fromTask.DelayOriginal prompt
🔒 GitHub Advanced Security automatically protects Copilot coding agent pull requests. You can protect all pull requests by enabling Advanced Security for your repositories. Learn more about Advanced Security.