private void ReadingDataHandler(ReadDataState asyncState, bool endofStream) { this.Controller.CheckCancellation(); if (!this.md5HashStream.MD5HashTransformBlock(asyncState.StartOffset, asyncState.MemoryBuffer, 0, asyncState.BytesRead)) { // Error info has been set in Calculate MD5 action, just return return; } TransferData transferData = new TransferData(this.Scheduler.MemoryManager) { StartOffset = asyncState.StartOffset, Length = asyncState.BytesRead, MemoryBuffer = asyncState.MemoryBuffer }; long currentReadLength = Interlocked.Add(ref this.readLength, asyncState.BytesRead); if ((currentReadLength == this.SharedTransferData.TotalLength) || endofStream) { Interlocked.Exchange(ref this.readCompleted, 1); } if (endofStream && (-1 != this.SharedTransferData.TotalLength) && (currentReadLength != this.SharedTransferData.TotalLength)) { throw new TransferException(Resources.SourceChangedException); } asyncState.MemoryBuffer = null; this.SharedTransferData.AvailableData.TryAdd(transferData.StartOffset, transferData); this.SetChunkFinish(); }
private async Task ReadChunkAsync(ReadDataState asyncState) { Debug.Assert(null != asyncState, "asyncState object expected"); Debug.Assert( this.state == State.ReadStream || this.state == State.Error, "ReadChunkAsync called, but state isn't Upload or Error"); int readBytes = await this.md5HashStream.ReadAsync( asyncState.StartOffset + asyncState.BytesRead, asyncState.MemoryBuffer, asyncState.BytesRead, asyncState.Length - asyncState.BytesRead, this.CancellationToken); // If a parallel operation caused the controller to be placed in // error state exit early to avoid unnecessary I/O. // Note that this check needs to be after the EndRead operation // above to avoid leaking resources. if (this.state == State.Error) { return; } asyncState.BytesRead += readBytes; if (asyncState.BytesRead < asyncState.Length) { await this.ReadChunkAsync(asyncState); } else { this.Controller.CheckCancellation(); if (!this.md5HashStream.MD5HashTransformBlock(asyncState.StartOffset, asyncState.MemoryBuffer, 0, asyncState.Length)) { // Error info has been set in Calculate MD5 action, just return return; } TransferData transferData = new TransferData(this.Scheduler.MemoryManager) { StartOffset = asyncState.StartOffset, Length = asyncState.Length, MemoryBuffer = asyncState.MemoryBuffer }; asyncState.MemoryBuffer = null; this.SharedTransferData.AvailableData.TryAdd(transferData.StartOffset, transferData); this.SetChunkFinish(); } }
private async Task DownloadChunkAsync(ReadDataState asyncState) { Debug.Assert(null != asyncState, "asyncState object expected"); // If a parallel operation caused the controller to be placed in // error state exit early to avoid unnecessary I/O. if (this.Controller.ErrorOccurred) { return; } AccessCondition accessCondition = Utils.GenerateIfMatchConditionWithCustomerCondition( this.sourceBlob.Properties.ETag, this.sourceLocation.AccessCondition); // We're to download this block. asyncState.MemoryStream = new MemoryStream( asyncState.MemoryBuffer, 0, asyncState.Length); await this.sourceBlob.DownloadRangeToStreamAsync( asyncState.MemoryStream, asyncState.StartOffset, asyncState.Length, accessCondition, Utils.GenerateBlobRequestOptions(this.sourceLocation.BlobRequestOptions), Utils.GenerateOperationContext(this.Controller.TransferContext), this.CancellationToken); TransferData transferData = new TransferData(this.Scheduler.MemoryManager) { StartOffset = asyncState.StartOffset, Length = asyncState.Length, MemoryBuffer = asyncState.MemoryBuffer }; this.SharedTransferData.AvailableData.TryAdd(transferData.StartOffset, transferData); // Set memory buffer to null. We don't want its dispose method to // be called once our asyncState is disposed. The memory should // not be reused yet, we still need to write it to disk. asyncState.MemoryBuffer = null; this.SetFinish(); this.SetBlockDownloadHasWork(); }
private async Task ReadChunkAsync(ReadDataState asyncState) { Debug.Assert(null != asyncState, "asyncState object expected"); Debug.Assert( this.state == State.ReadStream || this.state == State.Error, "ReadChunkAsync called, but state isn't Upload or Error"); int readBytes = await this.md5HashStream.ReadAsync( asyncState.StartOffset + asyncState.BytesRead, asyncState.MemoryBuffer, asyncState.BytesRead, asyncState.Length - asyncState.BytesRead, this.CancellationToken); // If a parallel operation caused the controller to be placed in // error state exit early to avoid unnecessary I/O. // Note that this check needs to be after the EndRead operation // above to avoid leaking resources. if (this.state == State.Error) { return; } asyncState.BytesRead += readBytes; if (0 == readBytes) { this.ReadingDataHandler(asyncState, true); return; } if (asyncState.BytesRead < asyncState.Length) { await this.ReadChunkAsync(asyncState); } else { this.ReadingDataHandler(asyncState, false); } }
/// <summary> /// Receive the information from the SDK and controls the communication and data protocol. /// </summary> /// <returns></returns> private ReadDataState ReadData() { ReadDataState state = ReadDataState.COMMAND; byte rowspercol = 0; int col = 0; byte count = 0; byte bytehigh = 0; int adc_value = 0; int tmp; double tmpDouble; if (port.BytesToRead < port.ReceivedBytesThreshold) { return ReadDataState.EXIT; } while (true) { tmp = this.ReadByte(); //Console.Write(tmp + "-"); switch (state) { case ReadDataState.COMMAND: if (tmp == 'M') { count = 0; bytehigh = 0; state = ReadDataState.ROWS_PER_COLUMN; } else if (tmp == 'K') { state = ReadDataState.ERROR; Console.WriteLine("SERIAL PROTOCOL INFO: K received"); } else { state = ReadDataState.ERROR; if (tmp != 'H') { Console.WriteLine ("SERIAL PROTOCOL ERROR: M not received"); } } break; case ReadDataState.ROWS_PER_COLUMN: rowspercol = (byte) tmp; if (tmp == Rows) { state = ReadDataState.COLUMN; } else { state = ReadDataState.ERROR; Console.WriteLine("SERIAL PROTOCOL ERROR: rowspercol != Rows"); } break; case ReadDataState.COLUMN: col = tmp; if (tmp < Columns) { state = ReadDataState.DATA; } else { state = ReadDataState.ERROR; Console.WriteLine("SERIAL PROTOCOL ERROR: col < Columns"); } break; case ReadDataState.DATA: if (bytehigh == 0) { bytehigh = 1; adc_value = tmp; } else { bytehigh = 0; adc_value |= ((int) tmp) << 8; tmpDouble = ConvertADCValue(adc_value); if (tmpDouble <= minimoAceptado) { tmpDouble = 0; } tmpData [count, col] = tmpDouble; count++; if (count == Rows) { state = ReadDataState.END_OF_LINE; } else if (count > Rows) { state = ReadDataState.ERROR; Console.WriteLine("SERIAL PROTOCOL ERROR: count > Rows"); } } break; case ReadDataState.END_OF_LINE: if (tmp == '\n') { if (col == (Columns - 1)) { numRowsRx = rowspercol; numColsRx = col + 1; return ReadDataState.DATA_READY; } if (port.BytesToRead < port.ReceivedBytesThreshold) { return ReadDataState.EXIT; } else { state = ReadDataState.COMMAND; } } else { state = ReadDataState.ERROR; Console.WriteLine("SERIAL PROTOCOL ERROR: \\n not received"); } break; case ReadDataState.ERROR: if (tmp == '\n') { state = ReadDataState.COMMAND; } break; default: state = ReadDataState.ERROR; break; } } }
private async Task ReadStreamAsync() { Debug.Assert( this.state == State.ReadStream || this.state == State.Error, "ReadChunks called, but state isn't ReadStream or Error"); if (Interlocked.CompareExchange(ref workToken, 0, 1) == 0) { return; } // Work with yield is good for overall scheduling efficiency, i.e. other works would be scheduled faster. // While work without yield is good for current files reading efficiency(e.g. transfering only 1 1TB file). // So when file has only one chunk, the possibility of current file influencing overall throughput might be less. // And in this case do Yield, otherwise, temporarily work without yield. // TODO: the threshold to do yield could be further tuned. if (this.EnableOneChunkFileOptimization) { await Task.Yield(); } byte[][] memoryBuffer = this.Scheduler.MemoryManager.RequireBuffers(this.SharedTransferData.MemoryChunksRequiredEachTime); if (null != memoryBuffer) { long startOffset = 0; // Only do calculation related to transfer window when the file contains multiple chunks. if (!this.EnableOneChunkFileOptimization) { if (0 != this.lastTransferWindow.Count) { startOffset = this.lastTransferWindow.Dequeue(); } else { bool canRead = false; // TransferWindow.Count is not necessary to be included in TransferWindowLock block, as current StreamReader // is the only entry for adding TransferWindow size, and the logic adding TransferWindow size is always executed in one thread. if (this.transferJob.CheckPoint.TransferWindow.Count < Constants.MaxCountInTransferWindow) { startOffset = this.transferJob.CheckPoint.EntryTransferOffset; if ((this.SharedTransferData.TotalLength < 0) || (this.transferJob.CheckPoint.EntryTransferOffset < this.SharedTransferData.TotalLength)) { lock (this.transferJob.CheckPoint.TransferWindowLock) { if ((this.SharedTransferData.TotalLength < 0) || (this.transferJob.CheckPoint.EntryTransferOffset < this.SharedTransferData.TotalLength)) { this.transferJob.CheckPoint.TransferWindow.Add(startOffset); } } this.transferJob.CheckPoint.EntryTransferOffset = Math.Min( this.transferJob.CheckPoint.EntryTransferOffset + this.SharedTransferData.BlockSize, this.SharedTransferData.TotalLength < 0 ? long.MaxValue : this.SharedTransferData.TotalLength); canRead = true; } } if (!canRead) { this.Scheduler.MemoryManager.ReleaseBuffers(memoryBuffer); this.workToken = 1; return; } } } if ((this.SharedTransferData.TotalLength > 0) && ((startOffset > this.SharedTransferData.TotalLength) || (startOffset < 0))) { this.Scheduler.MemoryManager.ReleaseBuffers(memoryBuffer); throw new InvalidOperationException(Resources.RestartableInfoCorruptedException); } ReadDataState asyncState = new ReadDataState { MemoryBuffer = memoryBuffer, BytesRead = 0, StartOffset = startOffset, Length = (int)Math.Min(this.SharedTransferData.BlockSize, this.SharedTransferData.TotalLength > 0 ? (this.SharedTransferData.TotalLength - startOffset) : long.MaxValue), MemoryManager = this.Scheduler.MemoryManager, }; using (asyncState) { await this.ReadChunkAsync(asyncState); } } this.SetHasWork(); }
private async Task DownloadBlockBlobAsync() { this.hasWork = false; byte[] memoryBuffer = this.Scheduler.MemoryManager.RequireBuffer(); if (null != memoryBuffer) { long startOffset = 0; if (!this.IsTransferWindowEmpty()) { startOffset = this.lastTransferWindow.Dequeue(); } else { bool canUpload = false; lock (this.transferJob.CheckPoint.TransferWindowLock) { if (this.transferJob.CheckPoint.TransferWindow.Count < Constants.MaxCountInTransferWindow) { startOffset = this.transferJob.CheckPoint.EntryTransferOffset; if (this.transferJob.CheckPoint.EntryTransferOffset < this.SharedTransferData.TotalLength) { this.transferJob.CheckPoint.TransferWindow.Add(startOffset); this.transferJob.CheckPoint.EntryTransferOffset = Math.Min( this.transferJob.CheckPoint.EntryTransferOffset + this.Scheduler.TransferOptions.BlockSize, this.SharedTransferData.TotalLength); canUpload = true; } } } if (!canUpload) { this.hasWork = true; this.Scheduler.MemoryManager.ReleaseBuffer(memoryBuffer); return; } } if ((startOffset > this.SharedTransferData.TotalLength) || (startOffset < 0)) { this.Scheduler.MemoryManager.ReleaseBuffer(memoryBuffer); throw new InvalidOperationException(Resources.RestartableInfoCorruptedException); } this.SetBlockDownloadHasWork(); ReadDataState asyncState = new ReadDataState { MemoryBuffer = memoryBuffer, BytesRead = 0, StartOffset = startOffset, Length = (int)Math.Min(this.Scheduler.TransferOptions.BlockSize, this.SharedTransferData.TotalLength - startOffset), MemoryManager = this.Scheduler.MemoryManager, }; using (asyncState) { await this.DownloadChunkAsync(asyncState); } return; } this.SetBlockDownloadHasWork(); }
private async Task ReadStreamAsync() { Debug.Assert( this.state == State.ReadStream || this.state == State.Error, "ReadChunks called, but state isn't ReadStream or Error"); this.hasWork = false; byte[][] memoryBuffer = this.Scheduler.MemoryManager.RequireBuffers(this.SharedTransferData.MemoryChunksRequiredEachTime); if (null != memoryBuffer) { long startOffset = 0; if (0 != this.lastTransferWindow.Count) { startOffset = this.lastTransferWindow.Dequeue(); } else { bool canRead = false; lock (this.transferJob.CheckPoint.TransferWindowLock) { if (this.transferJob.CheckPoint.TransferWindow.Count < Constants.MaxCountInTransferWindow) { startOffset = this.transferJob.CheckPoint.EntryTransferOffset; if (this.transferJob.CheckPoint.EntryTransferOffset < this.SharedTransferData.TotalLength) { this.transferJob.CheckPoint.TransferWindow.Add(startOffset); this.transferJob.CheckPoint.EntryTransferOffset = Math.Min( this.transferJob.CheckPoint.EntryTransferOffset + this.SharedTransferData.BlockSize, this.SharedTransferData.TotalLength); canRead = true; } } } if (!canRead) { this.Scheduler.MemoryManager.ReleaseBuffers(memoryBuffer); this.hasWork = true; return; } } if ((startOffset > this.SharedTransferData.TotalLength) || (startOffset < 0)) { this.Scheduler.MemoryManager.ReleaseBuffers(memoryBuffer); throw new InvalidOperationException(Resources.RestartableInfoCorruptedException); } ReadDataState asyncState = new ReadDataState { MemoryBuffer = memoryBuffer, BytesRead = 0, StartOffset = startOffset, Length = (int)Math.Min(this.SharedTransferData.BlockSize, this.SharedTransferData.TotalLength - startOffset), MemoryManager = this.Scheduler.MemoryManager, }; using (asyncState) { await this.ReadChunkAsync(asyncState); } } this.SetHasWork(); }
private async Task DownloadChunkAsync(ReadDataState asyncState) { Debug.Assert(null != asyncState, "asyncState object expected"); // If a parallel operation caused the controller to be placed in // error state exit early to avoid unnecessary I/O. if (this.Controller.ErrorOccurred) { return; } AccessCondition accessCondition = Utils.GenerateIfMatchConditionWithCustomerCondition( this.blob.Properties.ETag, this.transferLocation.AccessCondition); // We're to download this block. asyncState.MemoryStream = new MemoryStream( asyncState.MemoryBuffer, 0, asyncState.Length); await this.blob.DownloadRangeToStreamAsync( asyncState.MemoryStream, asyncState.StartOffset, asyncState.Length, accessCondition, Utils.GenerateBlobRequestOptions(this.transferLocation.BlobRequestOptions), Utils.GenerateOperationContext(this.Controller.TransferContext), this.CancellationToken); TransferData transferData = new TransferData(this.Scheduler.MemoryManager) { StartOffset = asyncState.StartOffset, Length = asyncState.Length, MemoryBuffer = asyncState.MemoryBuffer }; this.SharedTransferData.AvailableData.TryAdd(transferData.StartOffset, transferData); // Set memory buffer to null. We don't want its dispose method to // be called once our asyncState is disposed. The memory should // not be reused yet, we still need to write it to disk. asyncState.MemoryBuffer = null; this.SetFinish(); this.SetBlockDownloadHasWork(); }
private async Task DownloadBlockBlobAsync() { this.hasWork = false; byte[] memoryBuffer = this.Scheduler.MemoryManager.RequireBuffer(); if (null != memoryBuffer) { long startOffset = 0; if (!this.IsTransferWindowEmpty()) { startOffset = this.lastTransferWindow.Dequeue(); } else { bool canUpload = false; lock (this.transferJob.CheckPoint.TransferWindowLock) { if (this.transferJob.CheckPoint.TransferWindow.Count < Constants.MaxCountInTransferWindow) { startOffset = this.transferJob.CheckPoint.EntryTransferOffset; if (this.transferJob.CheckPoint.EntryTransferOffset < this.SharedTransferData.TotalLength) { this.transferJob.CheckPoint.TransferWindow.Add(startOffset); this.transferJob.CheckPoint.EntryTransferOffset = Math.Min( this.transferJob.CheckPoint.EntryTransferOffset + this.Scheduler.TransferOptions.BlockSize, this.SharedTransferData.TotalLength); canUpload = true; } } } if (!canUpload) { this.hasWork = true; this.Scheduler.MemoryManager.ReleaseBuffer(memoryBuffer); return; } } if ((startOffset > this.SharedTransferData.TotalLength) || (startOffset < 0)) { this.Scheduler.MemoryManager.ReleaseBuffer(memoryBuffer); throw new InvalidOperationException(Resources.RestartableInfoCorruptedException); } this.SetBlockDownloadHasWork(); ReadDataState asyncState = new ReadDataState { MemoryBuffer = memoryBuffer, BytesRead = 0, StartOffset = startOffset, Length = (int)Math.Min(this.Scheduler.TransferOptions.BlockSize, this.SharedTransferData.TotalLength - startOffset), MemoryManager = this.Scheduler.MemoryManager, }; using (asyncState) { await this.DownloadChunkAsync(asyncState); } return; } this.SetBlockDownloadHasWork(); }
private async Task DownloadChunkAsync(ReadDataState asyncState) { Debug.Assert(null != asyncState, "asyncState object expected"); // Use Yield to return to scheduling main logic immediately, and to improve scheduling efficiency. if (!this.isStateSwitchedInternal) { await Task.Yield(); } // If a parallel operation caused the controller to be placed in // error state exit early to avoid unnecessary I/O. if (this.Controller.ErrorOccurred) { return; } AccessCondition accessCondition = Utils.GenerateIfMatchConditionWithCustomerCondition( this.sourceBlob.Properties.ETag, this.sourceLocation.AccessCondition); if (asyncState.MemoryBuffer.Length == 1) { // We're to download this block. asyncState.MemoryStream = new MemoryStream( asyncState.MemoryBuffer[0], 0, asyncState.Length); await this.sourceBlob.DownloadRangeToStreamAsync( asyncState.MemoryStream, asyncState.StartOffset, asyncState.Length, accessCondition, Utils.GenerateBlobRequestOptions(this.sourceLocation.BlobRequestOptions), Utils.GenerateOperationContext(this.Controller.TransferContext), this.CancellationToken).ConfigureAwait(false); } else { var blockSize = Constants.DefaultTransferChunkSize; // 4MB var startOffset = asyncState.StartOffset; var remainingLength = asyncState.Length; var index = 0; do { var length = Math.Min(blockSize, remainingLength); var memoryStream = new MemoryStream(asyncState.MemoryBuffer[index], 0, length); await this.sourceBlob.DownloadRangeToStreamAsync( memoryStream, startOffset, length, accessCondition, Utils.GenerateBlobRequestOptions(this.sourceLocation.BlobRequestOptions), Utils.GenerateOperationContext(this.Controller.TransferContext), this.CancellationToken); index++; startOffset += length; remainingLength -= length; } while (remainingLength > 0); } TransferData transferData = new TransferData(this.Scheduler.MemoryManager) { StartOffset = asyncState.StartOffset, Length = asyncState.Length, MemoryBuffer = asyncState.MemoryBuffer }; this.SharedTransferData.AvailableData.TryAdd(transferData.StartOffset, transferData); // Set memory buffer to null. We don't want its dispose method to // be called once our asyncState is disposed. The memory should // not be reused yet, we still need to write it to disk. asyncState.MemoryBuffer = null; // When there is only one chunk, after read the only chunk, reader's work has finished, no more work to do. if (this.EnableOneChunkFileOptimization) { this.isFinished = true; } else { this.SetFinish(); this.SetBlockDownloadHasWork(); } }
private async Task DownloadBlockBlobAsync() { if (!this.isStateSwitchedInternal && Interlocked.CompareExchange(ref this.workToken, 0, 1) == 0) { return; } byte[][] memoryBuffer = this.Scheduler.MemoryManager.RequireBuffers(this.SharedTransferData.MemoryChunksRequiredEachTime); if (null != memoryBuffer) { long startOffset = 0; // Only multi-chunk file need transfer window calculation. if (!this.EnableOneChunkFileOptimization) { if (!this.IsTransferWindowEmpty()) { startOffset = this.lastTransferWindow.Dequeue(); } else { bool canUpload = false; lock (this.transferJob.CheckPoint.TransferWindowLock) { if (this.transferJob.CheckPoint.TransferWindow.Count < Constants.MaxCountInTransferWindow) { startOffset = this.transferJob.CheckPoint.EntryTransferOffset; if (this.transferJob.CheckPoint.EntryTransferOffset < this.SharedTransferData.TotalLength) { this.transferJob.CheckPoint.TransferWindow.Add(startOffset); this.transferJob.CheckPoint.EntryTransferOffset = Math.Min( this.transferJob.CheckPoint.EntryTransferOffset + this.SharedTransferData.BlockSize, this.SharedTransferData.TotalLength); canUpload = true; } } } if (!canUpload) { this.workToken = 1; this.Scheduler.MemoryManager.ReleaseBuffers(memoryBuffer); return; } } if ((startOffset > this.SharedTransferData.TotalLength) || (startOffset < 0)) { this.Scheduler.MemoryManager.ReleaseBuffers(memoryBuffer); throw new InvalidOperationException(Resources.RestartableInfoCorruptedException); } this.SetBlockDownloadHasWork(); } ReadDataState asyncState = new ReadDataState { MemoryBuffer = memoryBuffer, BytesRead = 0, StartOffset = startOffset, Length = (int)Math.Min(this.SharedTransferData.BlockSize, this.SharedTransferData.TotalLength - startOffset), MemoryManager = this.Scheduler.MemoryManager, }; using (asyncState) { await this.DownloadChunkAsync(asyncState).ConfigureAwait(false); } return; } this.SetBlockDownloadHasWork(); }
private async Task ReadChunkAsync(ReadDataState asyncState) { Debug.Assert(null != asyncState, "asyncState object expected"); Debug.Assert( this.state == State.ReadStream || this.state == State.Error, "ReadChunkAsync called, but state isn't Upload or Error"); int readBytes = await this.md5HashStream.ReadAsync( asyncState.StartOffset + asyncState.BytesRead, asyncState.MemoryBuffer, asyncState.BytesRead, asyncState.Length - asyncState.BytesRead, this.CancellationToken); // If a parallel operation caused the controller to be placed in // error state exit early to avoid unnecessary I/O. // Note that this check needs to be after the EndRead operation // above to avoid leaking resources. if (this.state == State.Error) { return; } asyncState.BytesRead += readBytes; if (asyncState.BytesRead < asyncState.Length) { await this.ReadChunkAsync(asyncState); } else { this.Controller.CheckCancellation(); if (!this.md5HashStream.MD5HashTransformBlock(asyncState.StartOffset, asyncState.MemoryBuffer, 0, asyncState.Length, null, 0)) { // Error info has been set in Calculate MD5 action, just return return; } TransferData transferData = new TransferData(this.Scheduler.MemoryManager) { StartOffset = asyncState.StartOffset, Length = asyncState.Length, MemoryBuffer = asyncState.MemoryBuffer }; asyncState.MemoryBuffer = null; this.SharedTransferData.AvailableData.TryAdd(transferData.StartOffset, transferData); this.SetChunkFinish(); } }
private async Task ReadStreamAsync() { Debug.Assert( this.state == State.ReadStream || this.state == State.Error, "ReadChunks called, but state isn't ReadStream or Error"); this.hasWork = false; byte[] memoryBuffer = this.Scheduler.MemoryManager.RequireBuffer(); if (null != memoryBuffer) { long startOffset = 0; if (0 != this.lastTransferWindow.Count) { startOffset = this.lastTransferWindow.Dequeue(); } else { bool canRead = false; lock (this.transferJob.CheckPoint.TransferWindowLock) { if (this.transferJob.CheckPoint.TransferWindow.Count < Constants.MaxCountInTransferWindow) { startOffset = this.transferJob.CheckPoint.EntryTransferOffset; if (this.transferJob.CheckPoint.EntryTransferOffset < this.SharedTransferData.TotalLength) { this.transferJob.CheckPoint.TransferWindow.Add(startOffset); this.transferJob.CheckPoint.EntryTransferOffset = Math.Min( this.transferJob.CheckPoint.EntryTransferOffset + this.Scheduler.TransferOptions.BlockSize, this.SharedTransferData.TotalLength); canRead = true; } } } if (!canRead) { this.Scheduler.MemoryManager.ReleaseBuffer(memoryBuffer); this.hasWork = true; return; } } if ((startOffset > this.SharedTransferData.TotalLength) || (startOffset < 0)) { this.Scheduler.MemoryManager.ReleaseBuffer(memoryBuffer); throw new InvalidOperationException(Resources.RestartableInfoCorruptedException); } ReadDataState asyncState = new ReadDataState { MemoryBuffer = memoryBuffer, BytesRead = 0, StartOffset = startOffset, Length = (int)Math.Min(this.Scheduler.TransferOptions.BlockSize, this.SharedTransferData.TotalLength - startOffset), MemoryManager = this.Scheduler.MemoryManager, }; using (asyncState) { await this.ReadChunkAsync(asyncState); } } this.SetHasWork(); }