using System; using System.Collections.Generic; using System.Linq; using Duplicati.Library.Main.Operation.Common; using Duplicati.Library.Utility; namespace Duplicati.Library.Main.Backend; #nullable enable partial class BackendManager { /// /// Represents a pending QuotaInfo operation /// private sealed record ProgressHandler(IBackendWriter Stats, ITaskReader TaskReader) { /// /// A lock guarding for single-threaded access to the list of active transfers /// private readonly object _lock = new(); /// /// The progress of the active transfers /// private Dictionary _activeTransferProgress = new(); /// /// Information about an active transfer /// /// The filename of the transfer /// When the transfer started /// The type of the transfer /// The size of the transfer private sealed record TransferInfo(string Filename, DateTime Started, BackendActionType Type, long Size); /// /// The information for the active transfers /// private Dictionary _activeTransferInfo = new(); /// /// The transfer that is currently reported in progress /// private TransferInfo? _currentTransfer; /// /// Handles the progress of a stream /// /// The progress /// The filename public void HandleProgress(long pg, string filename) { // This pauses and throws on cancellation, but ignores stop TaskReader.TransferRendevouz().Await(); lock (_lock) { // Record the progress of the transfer if (_activeTransferProgress.ContainsKey(filename)) _activeTransferProgress[filename] = pg; // Only update the progress if the filename matches the current transfer if (filename != _currentTransfer?.Filename) return; } Stats.BackendProgressUpdater.UpdateProgress(pg); } /// /// Begins the transfer of a file /// /// The filename public void BeginTransfer(BackendActionType type, long size, string filename) { TransferInfo? info = null; lock (_lock) { // Ignore the transfer if it is already in progress if (_activeTransferProgress.ContainsKey(filename)) return; _activeTransferProgress.Add(filename, 0); info = new TransferInfo(filename, DateTime.UtcNow, type, size); _activeTransferInfo.Add(filename, info); // Don't process the event if it is not the current transfer if (_currentTransfer != null) return; // This is now the current transfer _currentTransfer = info; } Stats.BackendProgressUpdater.StartAction(info.Type, info.Filename, info.Size, info.Started); } /// /// Ends the transfer of a file /// /// The filename public void EndTransfer(string filename) { TransferInfo? info = null; long progress = 0; lock (_lock) { _activeTransferProgress.Remove(filename); _activeTransferInfo.Remove(filename); // If the current transfer is not the one that ended, ignore it if (_currentTransfer?.Filename != filename) return; // Find the last started transfer, and report that as the current transfer // We choose the last one, as it will most likely be in-progress for the longest time info = _currentTransfer = _activeTransferInfo.Values.OrderBy(x => x.Started).LastOrDefault(); if (_currentTransfer != null) _activeTransferProgress.TryGetValue(_currentTransfer.Filename, out progress); } if (info != null) { Stats.BackendProgressUpdater.StartAction(info.Type, info.Filename, info.Size, info.Started); Stats.BackendProgressUpdater.UpdateProgress(progress); } } } }