private void ReadCompletedCore(BufferedRead bufferedRead, byte[] data) { bufferedRead.Complete(data); lock (_readLock) { // add item to queue _queue.Add(bufferedRead.ChunkIndex, bufferedRead); // signal that a chunk has been read or EOF has been reached; // in both cases, Read() will eventually also unblock the "read-ahead" thread Monitor.PulseAll(_readLock); } // check if server signaled EOF if (data.Length == 0) { // set a flag to stop read-aheads _endOfFileReceived = true; } }
private void StartReadAhead() { ThreadAbstraction.ExecuteThread(() => { while (!_endOfFileReceived && _exception == null) { // check if we should continue with the read-ahead loop // note that the EOF and exception check are not included // in this check as they do not require Read() to be // unblocked (or have already done this) if (!ContinueReadAhead()) { // unblock the Read() lock (_readLock) { Monitor.PulseAll(_readLock); } // break the read-ahead loop break; } // attempt to obtain the semaphore; this may time out when all semaphores are // in use due to pending read-aheads (which in turn can happen when the server // is slow to respond or when the session is broken) if (!_semaphore.Wait(ReadAheadWaitTimeoutInMilliseconds)) { // re-evaluate whether an exception occurred, and - if not - wait again continue; } // don't bother reading any more chunks if we received EOF, an exception has occurred // or the current instance is disposed if (_endOfFileReceived || _exception != null) { break; } // start reading next chunk var bufferedRead = new BufferedRead(_readAheadChunkIndex, _readAheadOffset); try { // even if we know the size of the file and have read up to EOF, we still want // to keep reading (ahead) until we receive zero bytes from the remote host as // we do not want to rely purely on the reported file size // // if the offset of the read-ahead chunk is greater than that file size, then // we can expect to be reading the last (zero-byte) chunk and switch to synchronous // mode to avoid having multiple read-aheads that read beyond EOF if (_fileSize != null && (long)_readAheadOffset > _fileSize.Value) { var asyncResult = _sftpSession.BeginRead(_handle, _readAheadOffset, _chunkSize, null, bufferedRead); var data = _sftpSession.EndRead(asyncResult); ReadCompletedCore(bufferedRead, data); } else { _sftpSession.BeginRead(_handle, _readAheadOffset, _chunkSize, ReadCompleted, bufferedRead); } } catch (Exception ex) { HandleFailure(ex); break; } // advance read-ahead offset _readAheadOffset += _chunkSize; // increment index of read-ahead chunk _readAheadChunkIndex++; } _readAheadCompleted.Set(); }); }