diff options
| author | Shadowghost <Ghost_of_Stone@web.de> | 2026-09-05 09:58:04 +0200 |
|---|---|---|
| committer | Shadowghost <Ghost_of_Stone@web.de> | 2026-09-05 09:59:12 +0200 |
| commit | f8470630be0f3fa7b8052ebd822f23531d30da2f (patch) | |
| tree | dc7cf005acfdb2d9ee22a21f886176d3ada25cf5 | |
| parent | e5cd3381acefbe13aa36a26059c28629e23e1944 (diff) | |
Retire runners that are cancelled before they start
2 files changed, 6 insertions, 4 deletions
diff --git a/MediaBrowser.Controller/LibraryTaskScheduler/LimitedConcurrencyLibraryScheduler.cs b/MediaBrowser.Controller/LibraryTaskScheduler/LimitedConcurrencyLibraryScheduler.cs index 3c6ffe3cbd..be75117b6f 100644 --- a/MediaBrowser.Controller/LibraryTaskScheduler/LimitedConcurrencyLibraryScheduler.cs +++ b/MediaBrowser.Controller/LibraryTaskScheduler/LimitedConcurrencyLibraryScheduler.cs @@ -191,12 +191,14 @@ public sealed class LimitedConcurrencyLibraryScheduler : ILimitedConcurrencyLibr // Keyed on its own stop source, because cancelling that is what reaches the linked // source the runner waits on. Cancellation does not travel the other way. + // Started without the runner's own token: a task cancelled before it is scheduled + // never runs its body, so it would never take itself out of _taskRunners again. _taskRunners.Add( stopToken, Task.Factory.StartNew( ItemWorker, (stopToken, combinedSource), - combinedSource.Token, + CancellationToken.None, TaskCreationOptions.PreferFairness, TaskScheduler.Default)); } diff --git a/tests/Jellyfin.Controller.Tests/LibraryTaskScheduler/LimitedConcurrencyLibrarySchedulerTests.cs b/tests/Jellyfin.Controller.Tests/LibraryTaskScheduler/LimitedConcurrencyLibrarySchedulerTests.cs index f776e893a0..686d839f4f 100644 --- a/tests/Jellyfin.Controller.Tests/LibraryTaskScheduler/LimitedConcurrencyLibrarySchedulerTests.cs +++ b/tests/Jellyfin.Controller.Tests/LibraryTaskScheduler/LimitedConcurrencyLibrarySchedulerTests.cs @@ -65,7 +65,7 @@ namespace Jellyfin.Controller.Tests.LibraryTaskScheduler /// so a shutdown has to reach them. It does not travel from the linked source back to the one /// the cleanup cancels, which is what made them immortal. /// </summary> - /// <returns><placeholder>A <see cref="Task"/> representing the asynchronous unit test.</placeholder></returns> + /// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns> [Fact] public async Task ApplicationStopping_RetiresRunners() { @@ -88,7 +88,7 @@ namespace Jellyfin.Controller.Tests.LibraryTaskScheduler /// The cleanup used to be a one shot: it never released the scheduling slot it took, so /// every runner spawned after the first pass stayed around for the lifetime of the server. /// </summary> - /// <returns><placeholder>A <see cref="Task"/> representing the asynchronous unit test.</placeholder></returns> + /// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns> [Fact] public async Task Enqueue_RetiresIdleRunnersAfterEveryOperation() { @@ -110,7 +110,7 @@ namespace Jellyfin.Controller.Tests.LibraryTaskScheduler /// Disposing used to sit out the rest of the cleanup grace period, holding up shutdown for /// up to a minute. /// </summary> - /// <returns><placeholder>A <see cref="Task"/> representing the asynchronous unit test.</placeholder></returns> + /// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns> [Fact] public async Task DisposeAsync_DoesNotWaitOutTheGracePeriod() { |
