diff --git a/Duplicati/Library/Main/Operation/Restore/Channels.cs b/Duplicati/Library/Main/Operation/Restore/Channels.cs index 04320ac48..93ae32fc4 100644 --- a/Duplicati/Library/Main/Operation/Restore/Channels.cs +++ b/Duplicati/Library/Main/Operation/Restore/Channels.cs @@ -52,6 +52,7 @@ namespace Duplicati.Library.Main.Operation.Restore /// public readonly IChannel<(long, string, TempFile)> DecryptRequest = ChannelManager.CreateChannel<(long, string, TempFile)>(buffersize: BufferSize); + // TODO DecompressionAck could be made into a channel of `long`, but because of the ReadFromEitherPrioritized workaround, it has to be an `object`. /// /// Channel between and to keep track of volumes that are actively being read. /// diff --git a/Duplicati/Library/Main/Operation/Restore/VolumeManager.cs b/Duplicati/Library/Main/Operation/Restore/VolumeManager.cs index e8cdbd94b..8fb9a832c 100644 --- a/Duplicati/Library/Main/Operation/Restore/VolumeManager.cs +++ b/Duplicati/Library/Main/Operation/Restore/VolumeManager.cs @@ -43,12 +43,13 @@ namespace Duplicati.Library.Main.Operation.Restore private static readonly string LOGTAG = Logging.Log.LogTagFromType(); /// - /// Helper class to read from either of two channels. + /// Helper class to read from either of three channels, in a prioritized manner, where channel1 is consumed first. /// This is a workaround for the fact that CoCoL seems to deadlock on ReadFroAnyAsync /// /// The first channel to read from. /// The second channel to read from. - private sealed class ReadFromEither(IReadChannel channel1, IReadChannel channel2) + /// The third channel to read from. + private sealed class ReadFromEitherPrioritized(IReadChannel channel1, IReadChannel channel2, IReadChannel channel3) { /// /// The first task that is reading from the channels. @@ -58,6 +59,10 @@ namespace Duplicati.Library.Main.Operation.Restore /// The second task that is reading from the channels. /// private Task t2; + /// + /// The third task that is reading from the channels. + /// + private Task t3; /// /// Reads from either of the two channels asynchronously. @@ -71,17 +76,30 @@ namespace Duplicati.Library.Main.Operation.Restore // This is safe here, because the shutdown only happens on failure termination t1 ??= channel1.ReadAsync(); t2 ??= channel2.ReadAsync(); + t3 ??= channel3.ReadAsync(); - var r = await Task.WhenAny(t1, t2).ConfigureAwait(false); - if (r == t1) + // Wait for any of the tasks to complete. + await Task.WhenAny(t1, t2, t3).ConfigureAwait(false); + + // Check the channels in priority + // If t1 is completed, we process it first + if (t1.IsCompletedSuccessfully) { + var result = await t1.ConfigureAwait(false); t1 = null; - return await r.ConfigureAwait(false); + return result; } - else + else if (t2.IsCompletedSuccessfully) { + var result = await t2.ConfigureAwait(false); t2 = null; - return await r.ConfigureAwait(false); + return result; + } + else // t3.IsCompletedSuccessfully + { + var result = await t3.ConfigureAwait(false); + t3 = null; + return result; } } }