From 4e5d015e0cce2691ac44cfef8ef85dd1a290fc01 Mon Sep 17 00:00:00 2001 From: Carl Johnsen Date: Tue, 28 Oct 2025 14:41:58 +0100 Subject: [PATCH] Added the deadlock timer task --- .../Main/Operation/Restore/DeadlockTimer.cs | 100 ++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 Duplicati/Library/Main/Operation/Restore/DeadlockTimer.cs diff --git a/Duplicati/Library/Main/Operation/Restore/DeadlockTimer.cs b/Duplicati/Library/Main/Operation/Restore/DeadlockTimer.cs new file mode 100644 index 000000000..fa46e8de6 --- /dev/null +++ b/Duplicati/Library/Main/Operation/Restore/DeadlockTimer.cs @@ -0,0 +1,100 @@ +// Copyright (C) 2025, The Duplicati Team +// https://duplicati.com, hello@duplicati.com +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +#nullable enable + +using System; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace Duplicati.Library.Main.Operation.Restore +{ + /// + /// Class for keeping track of the maximum processing time of a block + /// request making a roundtrip to/from the BlockManager. + /// + /// The times are not Interlocked as reads and writes are atomic for int + /// types and exact accuracy is not required. + /// + /// https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/variables#96-atomicity-of-variable-references + /// + /// The worst case is that the reads are stale by one update, which is + /// acceptable for this use case. The same applies to the MaxProcessingTime + /// property, which means that the BlockManager may use a slightly stale + /// value when checking for timeouts, which is also acceptable. + /// + internal class DeadlockTimer + { + /// + /// The log tag for this class. + /// + private static readonly string LOGTAG = + Logging.Log.LogTagFromType(); + /// + /// Five minutes in milliseconds. + /// + private static readonly int five_minutes_ms = (int)TimeSpan.FromMinutes(5).TotalMilliseconds; + /// + /// Cancellation token for stopping the deadlock timer. + /// + public static CancellationTokenSource Token = new(); + /// + /// Maximum processing time (in milliseconds) recorded for any block + /// request. + /// + public static int MaxProcessingTime = five_minutes_ms; + + /// + /// Runs the deadlock timer process. It runs every second and updates + /// the maximum processing time based on the maximum processing times + /// of the active VolumeDownloaders, VolumeDecryptors and + /// VolumeDecompressors. + /// + /// It will keep running until the cancellation token + /// `DeadlockTimer.token` is cancelled. + /// An awaitable task. + public static async Task Run() + { + try + { + while (!Token.IsCancellationRequested) + { + await Task.Delay(TimeSpan.FromSeconds(1), Token.Token) + .ConfigureAwait(false); + + int download = VolumeDownloader.MaxProcessingTimes.Max(); + int decrypt = VolumeDecryptor.MaxProcessingTimes.Max(); + int decompress = VolumeDecompressor.MaxProcessingTimes.Max(); + + MaxProcessingTime = Math.Max( + five_minutes_ms, + (download + decrypt + decompress) * 2 + ); + } + } + catch (TaskCanceledException) + { + // Ignore + } + } + } +} \ No newline at end of file