private async Task <List <Task> > Dispatch(FilePartSource partSource, string chunkUploadUrl, string fileName, CancellationToken cancellationToken = default(CancellationToken)) { var incrementLock = new object(); long currentPartSize = partConfig.InitialPartSize; var partSizeCalc = new PartSizeCalculator(concurrentWorkers, partConfig); Func <FilePart, Task> attemptPartUpload = async part => { IStopwatch timer = Stopwatch.StartNew(); try { await UploadPart(chunkUploadUrl, part, fileName, cancellationToken).ConfigureAwait(false); } finally { timer.Stop(); } lock (incrementLock) { currentPartSize = partSizeCalc.NextPartSize(currentPartSize, part.Bytes.Length, timer.Elapsed); } }; var workerTasks = new List <Task>(); try { var activeWorkers = new AsyncSemaphore(concurrentWorkers); bool giveUp = false; while (!giveUp && partSource.HasMore) { if (cancellationToken.IsCancellationRequested) { throw new UploadException( "Upload was cancelled", UploadStatusCode.Cancelled, new ActiveUploadState(UploadSpecification, LastConsecutiveByteUploaded)); } await activeWorkers.WaitAsync().ConfigureAwait(false); if (giveUp) { return(workerTasks); } var part = await partSource.GetNextPart(Interlocked.Read(ref currentPartSize)).ConfigureAwait(false); var task = Task.Run(async() => { try { await AttemptPartUploadWithRetry(attemptPartUpload, part, partConfig.PartRetryCount).ConfigureAwait(false); } catch { giveUp = true; throw; } finally { activeWorkers.Release(); part.Bytes.Dispose(); } }); workerTasks.Add(task); } return(workerTasks); } catch { ObserveExceptions(workerTasks); throw; } }