/// <summary> /// After advancing current position to next item in online queue /// check the offline queue and open top reader if it contains /// items with timestamp lower or equal to the <see cref="TopReader"/> /// or just open the first file in the queue if the online queue /// is empty /// </summary> /// <remarks> /// Call it after returning item from top online reader or after /// initial seek populating the offline queue. /// </remarks> private void SyncOfflineQueueForReading() { // need to repeat because FirstTimestampInOfflineQueue is not necessarily what we will get after loading the file // because it's potential; the actual item timestamp may be further from the seek time but we only find it out // after loading the file while (_offlineQueue.Count > 0 && ( TopReader == null || // whatever is in the offline queue is is better than nothing TimestampComparer.Compare(CurrentItemTimestamp, this.FirstTimestampInOfflineQueue) >= 0 // offline is potentially prior to top reader )) { DateTime firstOfflineTimestamp = this.FirstTimestampInOfflineQueue; // if DataItemComparer is set we need to load all readers with the top timestamp // to enforce order do { RepositoryFolderReader readerToGoOnline = _offlineQueue.First.Value; if (readerToGoOnline.LoadNextFile()) { _onlineQueue.AddFirst(readerToGoOnline); // may need to update position when timestamps are equal and the DataItemComparer is set UpdateTopReaderPosition(); } else { AddToExhaustedImpl(readerToGoOnline); } _offlineQueue.RemoveFirst(); }while (this.FirstTimestampInOfflineQueue == firstOfflineTimestamp && this.DataItemComparer != null); } }
/// <summary> /// <see cref="IRepositoryReader.RemoveFolder"/> /// </summary> /// <exception cref="ObjectDisposedException"> /// The reader has been disposed. /// </exception> public void RemoveFolder(IRepositoryFolder folder) { CheckNotDisposed(); Util.Check.DoRequireArgumentNotNull(folder, "folder"); Exceptions.DifferentRepositoriesExceptionHelper.Check(folder.Repository, Repository); RepositoryFolderReader reader = GetExistingReader(folder.FolderKey); Util.Check.DoRequire(reader != null, "The folder is not being read by the reader"); _position.Remove(reader.Position); reader.Unload(); bool topReaderRemoved = this.HasData && TopReader == reader; bool success = !(!_exhaustedReaders.Remove(reader) && !_offlineQueue.Remove(reader) && !_onlineQueue.Remove(reader)); Util.Check.Ensure(success, "Internal error: reader not found."); success = _readers.Remove(folder.FolderKey); Util.Check.Ensure(success); if (topReaderRemoved) { SyncOfflineQueueForReading(); } Invariant(); }
/// <summary> /// Add folder to read and optionally prepare it for reading /// </summary> /// <param name="folder"> /// Folder to add /// </param> /// <param name="position"> /// <see langword="null"/> means do not prepare it for reading /// </param> /// <returns> /// <see langword="false"/> - the folder is already being read /// <see langword="true"/> otherwise /// </returns> /// <remarks> /// If reader has data (<see cref="HasData"/>) <paramref name="position"/> must have value /// </remarks> private bool AddFolderImpl(IRepositoryFolder folder, IFolderReadingPosition position) { Check.RequireArgumentNotNull(folder, "folder"); IFolder folderTyped = RepositoryFolder.CastFolder(folder); Check.Require(object.ReferenceEquals(folder.Repository, Repository)); Check.Require(!HasData || position != null, "If we have data we cannot leave a reader unpositioned"); if (IsAccessing(folder, false)) { return(false); } Check.Require(!_position.FolderPositions.ContainsKey(folder.FolderKey) , "Folder position found in repository reader position for a folder not being read"); RepositoryFolderReader reader = new RepositoryFolderReader(folderTyped, this); reader.Direction = this.Direction; _readers.Add(folder.FolderKey, reader); if (position != null) { SeekFolderReader(reader, position); } return(true); }
/// <summary> /// Add a positioned offline reader to an appropriate queue /// </summary> /// <param name="reader"> /// The reader must be added to all readers collection as necessary; not added here /// </param> /// <remarks> /// The reader will go into either offline queue or exhausted readers collection /// </remarks> private void AddPositionedOfflineReaderToAQueue(RepositoryFolderReader reader) { if (reader.NextFileFound) { AddReaderToOfflineQueue(reader); } else { AddToExhaustedImpl(reader); } }
/// <summary> /// Initiate reading from the <paramref name="reader"/>. /// It must not be in any of the 2 queues (online, offline) or exhausted list. /// If the file contains data since <paramref name="seekTime"/> the reader will /// be placed into offline queue. Otherwise it will go into exhausted readers list. /// </summary> /// <param name="reader"> /// Reader to initiate. It must not be in any of the 2 queues (online, offline) or exhausted list. /// </param> /// <param name="position"> /// Position to start reading from. /// </param> /// <remarks> /// The method ensures that currently loaded reader's data is unloaded first. /// Seeking is done lazily - all go to offline queue and loaded immediately /// before reading. /// </remarks> private void SeekFolderReader(RepositoryFolderReader reader, IFolderReadingPosition position) { Check.RequireArgumentNotNull(position, "position"); Check.RequireArgumentNotNull(reader, "reader"); Check.Require(!_offlineQueue.Contains(reader) && !_exhaustedReaders.Contains(reader) && !_onlineQueue.Contains(reader)); reader.Unload(); reader.SeekDataFile(position); AddPositionedOfflineReaderToAQueue(reader); // as reader.Position is singleton, changes to the reader's position do not require modifying _position _position.SetFolderPosition(reader.Position); }
/// <summary> /// Add the specified reader to the offline queue /// </summary> /// <param name="reader"> /// The reader to add. Its <see cref="RepositoryFolderReader.NextFileFound"/> /// must equal <see langword="true"/>. /// </param> private void AddReaderToOfflineQueue(RepositoryFolderReader reader) { Util.Check.Require(reader.NextFileFound, "Internal error: reader unsuitable for offline queue"); // free memory reader.Unload(); LinkedListNode <RepositoryFolderReader> predecessor = null; if (_offlineQueue.Count > 0) { predecessor = FindPredecessorInQueue(_offlineQueue.First, reader, _offlineReaderComparer); } if (null == predecessor) { _offlineQueue.AddFirst(reader); } else { _offlineQueue.AddAfter(predecessor, reader); } }
/// <summary> /// Find predecessor for the <paramref name="reader"/> in the queue /// of readers sorted by <paramref name="readerComparer"/> ascending starting /// from <paramref name="queueStart"/> /// </summary> /// <param name="queueStart"> /// The first element in the queue to start lookup from; all readers /// in the queue must have current item /// </param> /// <param name="reader"> /// The reader to find place for in the queue /// </param> /// <returns> /// <see langword="null"/> if the <paramref name="queueStart"/> is /// positioned to a greater item than <paramref name="reader"/> /// i.e. <paramref name="reader"/> must be before <paramref name="queueStart"/> /// otherwise the first element in the queue which is less than the /// <paramref name="reader"/> /// </returns> private LinkedListNode <RepositoryFolderReader> FindPredecessorInQueue( LinkedListNode <RepositoryFolderReader> queueStart , RepositoryFolderReader reader , IComparer <RepositoryFolderReader> readerComparer) { LinkedListNode <RepositoryFolderReader> retval; if (readerComparer.Compare(queueStart.Value, reader) >= 0) { // the reader must be before the first item in the queue return(null); } for ( retval = queueStart ; retval.Next != null && readerComparer.Compare(retval.Next.Value, reader) < 0 ; retval = retval.Next) { Util.Check.Ensure(readerComparer.Compare(retval.Value, reader) < 0 , "Internal logic error; queue may not be sorted properly"); } return(retval); }
private void AddToExhaustedImpl(RepositoryFolderReader reader) { reader.Unload(); _exhaustedReaders.AddLast(reader); }