private async Task DownloadRangeAsync() { Debug.Assert( this.state == State.Error || this.state == State.Download, "DownloadRangeAsync called, but state isn't Download or Error"); if (Interlocked.CompareExchange(ref workToken, 0, 1) == 0) { return; } if (State.Error == this.state) { // Some thread has set error message, just return here. return; } if (this.nextDownloadIndex < this.rangeList.Count) { Range rangeData = this.rangeList[this.nextDownloadIndex]; int blockSize = this.SharedTransferData.BlockSize; long blockStartOffset = (rangeData.StartOffset / blockSize) * blockSize; long nextBlockStartOffset = Math.Min(blockStartOffset + blockSize, this.SharedTransferData.TotalLength); TransferDownloadStream downloadStream = null; if ((rangeData.StartOffset > blockStartOffset) && (rangeData.EndOffset < nextBlockStartOffset)) { Debug.Assert(null != this.currentDownloadBuffer, "Download buffer should have been allocated when range start offset is not block size aligned"); downloadStream = new TransferDownloadStream(this.Scheduler.MemoryManager, this.currentDownloadBuffer, (int)(rangeData.StartOffset - blockStartOffset), (int)(rangeData.EndOffset + 1 - rangeData.StartOffset)); } else { // Attempt to reserve memory. If none available we'll // retry some time later. byte[][] memoryBuffer = this.Scheduler.MemoryManager.RequireBuffers(this.SharedTransferData.MemoryChunksRequiredEachTime); if (null == memoryBuffer) { this.SetRangeDownloadHasWork(); return; } if (rangeData.EndOffset >= this.lastTransferOffset) { bool canRead = true; lock (this.transferJob.CheckPoint.TransferWindowLock) { if (this.transferJob.CheckPoint.TransferWindow.Count >= Constants.MaxCountInTransferWindow) { canRead = false; } else { if (this.transferJob.CheckPoint.EntryTransferOffset < this.SharedTransferData.TotalLength) { this.transferJob.CheckPoint.TransferWindow.Add(this.transferJob.CheckPoint.EntryTransferOffset); this.transferJob.CheckPoint.EntryTransferOffset = Math.Min(this.transferJob.CheckPoint.EntryTransferOffset + blockSize, this.SharedTransferData.TotalLength); } } } if (!canRead) { this.Scheduler.MemoryManager.ReleaseBuffers(memoryBuffer); this.SetRangeDownloadHasWork(); return; } } if (rangeData.StartOffset == blockStartOffset) { this.currentDownloadBuffer = new TransferDownloadBuffer(blockStartOffset, (int)Math.Min(blockSize, this.SharedTransferData.TotalLength - blockStartOffset), memoryBuffer); downloadStream = new TransferDownloadStream(this.Scheduler.MemoryManager, this.currentDownloadBuffer, 0, (int)(rangeData.EndOffset + 1 - rangeData.StartOffset)); } else { Debug.Assert(null != this.currentDownloadBuffer, "Download buffer should have been allocated when range start offset is not block size aligned"); TransferDownloadBuffer nextBuffer = new TransferDownloadBuffer(nextBlockStartOffset, (int)Math.Min(blockSize, this.SharedTransferData.TotalLength - nextBlockStartOffset), memoryBuffer); downloadStream = new TransferDownloadStream( this.Scheduler.MemoryManager, this.currentDownloadBuffer, (int)(rangeData.StartOffset - blockStartOffset), (int)(nextBlockStartOffset - rangeData.StartOffset), nextBuffer, 0, (int)(rangeData.EndOffset + 1 - nextBlockStartOffset)); this.currentDownloadBuffer = nextBuffer; } } using (downloadStream) { this.nextDownloadIndex++; this.SetRangeDownloadHasWork(); RangeBasedDownloadState rangeBasedDownloadState = new RangeBasedDownloadState { Range = rangeData, DownloadStream = downloadStream }; await this.DownloadRangeAsync(rangeBasedDownloadState); } this.SetChunkFinish(); return; } this.SetRangeDownloadHasWork(); }
private async Task DownloadRangeAsync() { Debug.Assert( this.state == State.Error || this.state == State.Download, "DownloadRangeAsync called, but state isn't Download or Error"); this.hasWork = false; if (State.Error == this.state) { // Some thread has set error message, just return here. return; } if (this.nextDownloadIndex < this.rangeList.Count) { Range rangeData = this.rangeList[this.nextDownloadIndex]; int blockSize = this.Scheduler.TransferOptions.BlockSize; long blockStartOffset = (rangeData.StartOffset / blockSize) * blockSize; long nextBlockStartOffset = Math.Min(blockStartOffset + blockSize, this.SharedTransferData.TotalLength); TransferDownloadStream downloadStream = null; if ((rangeData.StartOffset > blockStartOffset) && (rangeData.EndOffset < nextBlockStartOffset)) { Debug.Assert(null != this.currentDownloadBuffer, "Download buffer should have been allocated when range start offset is not block size aligned"); downloadStream = new TransferDownloadStream(this.Scheduler.MemoryManager, this.currentDownloadBuffer, (int)(rangeData.StartOffset - blockStartOffset), (int)(rangeData.EndOffset + 1 - rangeData.StartOffset)); } else { // Attempt to reserve memory. If none available we'll // retry some time later. byte[] memoryBuffer = this.Scheduler.MemoryManager.RequireBuffer(); if (null == memoryBuffer) { this.SetRangeDownloadHasWork(); return; } if (rangeData.EndOffset >= this.lastTransferOffset) { bool canRead = true; lock (this.transferJob.CheckPoint.TransferWindowLock) { if (this.transferJob.CheckPoint.TransferWindow.Count >= Constants.MaxCountInTransferWindow) { canRead = false; } else { if (this.transferJob.CheckPoint.EntryTransferOffset < this.SharedTransferData.TotalLength) { this.transferJob.CheckPoint.TransferWindow.Add(this.transferJob.CheckPoint.EntryTransferOffset); this.transferJob.CheckPoint.EntryTransferOffset = Math.Min(this.transferJob.CheckPoint.EntryTransferOffset + this.Scheduler.TransferOptions.BlockSize, this.SharedTransferData.TotalLength); } } } if (!canRead) { this.Scheduler.MemoryManager.ReleaseBuffer(memoryBuffer); this.SetRangeDownloadHasWork(); return; } } if (rangeData.StartOffset == blockStartOffset) { this.currentDownloadBuffer = new TransferDownloadBuffer(blockStartOffset, (int)Math.Min(blockSize, this.SharedTransferData.TotalLength - blockStartOffset), memoryBuffer); downloadStream = new TransferDownloadStream(this.Scheduler.MemoryManager, this.currentDownloadBuffer, 0, (int)(rangeData.EndOffset + 1 - rangeData.StartOffset)); } else { Debug.Assert(null != this.currentDownloadBuffer, "Download buffer should have been allocated when range start offset is not block size aligned"); TransferDownloadBuffer nextBuffer = new TransferDownloadBuffer(nextBlockStartOffset, (int)Math.Min(blockSize, this.SharedTransferData.TotalLength - nextBlockStartOffset), memoryBuffer); downloadStream = new TransferDownloadStream( this.Scheduler.MemoryManager, this.currentDownloadBuffer, (int)(rangeData.StartOffset - blockStartOffset), (int)(nextBlockStartOffset - rangeData.StartOffset), nextBuffer, 0, (int)(rangeData.EndOffset + 1 - nextBlockStartOffset)); this.currentDownloadBuffer = nextBuffer; } } using (downloadStream) { this.nextDownloadIndex++; this.SetRangeDownloadHasWork(); RangeBasedDownloadState rangeBasedDownloadState = new RangeBasedDownloadState { Range = rangeData, DownloadStream = downloadStream }; await this.DownloadRangeAsync(rangeBasedDownloadState); } this.SetChunkFinish(); return; } this.SetRangeDownloadHasWork(); }