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();
            }
        }
Beispiel #3
0
        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();
        }
Beispiel #4
0
        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);
            }
        }
Beispiel #5
0
        /// <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;
				}
			}
		}
Beispiel #6
0
        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();
        }
Beispiel #7
0
        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();
        }
Beispiel #11
0
        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();
            }
        }
Beispiel #12
0
        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();
        }