Skip to content

Commit 3231e4f

Browse files
authored
Fix infinite timeout passed to CancellationTokenSource.CancelAfter (#84009)
The recent change to introduce ITimer broke CTS.CancelAfter(Timeout.InfiniteTimeSpan). The TimeSpan's milliseconds were extracted as a uint but then ended up being cast to a long, such that rather than representing -1 milliseconds, it represented 4294967295 milliseconds. With the uint representation in Timer, Timeout.UnsignedInfinite needs to be special-cased.
1 parent ed921ee commit 3231e4f

File tree

2 files changed

+29
-1
lines changed

2 files changed

+29
-1
lines changed

src/libraries/System.Private.CoreLib/src/System/Threading/CancellationTokenSource.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -446,7 +446,9 @@ private void CancelAfter(uint millisecondsDelay)
446446
}
447447
}
448448

449-
timer.Change(TimeSpan.FromMilliseconds(millisecondsDelay), Timeout.InfiniteTimeSpan);
449+
timer.Change(
450+
millisecondsDelay == Timeout.UnsignedInfinite ? Timeout.InfiniteTimeSpan : TimeSpan.FromMilliseconds(millisecondsDelay),
451+
Timeout.InfiniteTimeSpan);
450452
}
451453

452454
/// <summary>

src/libraries/System.Threading.Tasks/tests/CancellationTokenTests.cs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1077,6 +1077,32 @@ public static void CancellationTokenSourceWithTimer_Negative()
10771077
Assert.Throws<ObjectDisposedException>(() => { cts.CancelAfter(reasonableTimeSpan); });
10781078
}
10791079

1080+
[Fact]
1081+
public static void CancellationTokenSourceWithTimer_SupportsInfinite()
1082+
{
1083+
var cts = new CancellationTokenSource(TimeSpan.FromDays(1));
1084+
Assert.False(cts.IsCancellationRequested);
1085+
1086+
cts.CancelAfter(Timeout.InfiniteTimeSpan);
1087+
Assert.False(cts.IsCancellationRequested);
1088+
1089+
Assert.True(cts.TryReset());
1090+
1091+
cts.CancelAfter(TimeSpan.FromDays(1));
1092+
Assert.False(cts.IsCancellationRequested);
1093+
1094+
cts.CancelAfter(Timeout.Infinite);
1095+
Assert.False(cts.IsCancellationRequested);
1096+
1097+
Assert.True(cts.TryReset());
1098+
1099+
cts = new CancellationTokenSource(Timeout.InfiniteTimeSpan);
1100+
Assert.False(cts.IsCancellationRequested);
1101+
1102+
cts = new CancellationTokenSource(Timeout.Infinite);
1103+
Assert.False(cts.IsCancellationRequested);
1104+
}
1105+
10801106
[Fact]
10811107
public static void CancellationTokenSource_TryReset_ReturnsFalseIfAlreadyCanceled()
10821108
{

0 commit comments

Comments
 (0)