private void SaveValues(IEnumerable <T> values, int currentTransaction) { if (stream == null) { throw new InvalidOperationException("No active stream."); } lock (stream) { var indexStreamPosition = stream.Position; // Acquire lock on end of file (for appending) // This will prevent another thread from writing at the same time, or reading before it is flushed. if (LockEnabled && stream is FileStream) { NativeLockFile.LockFile((FileStream)stream, indexStreamPosition, long.MaxValue - indexStreamPosition, true); } try { // Make sure we read up entries up to end of file (or skip it if AutoLoadNewValues is not set) if (AutoLoadNewValues) { RefreshData(stream.Length); } else { stream.Position = stream.Length; } foreach (var value in values) { WriteEntry(stream, value); } stream.Flush(); // Transfer from temporary mapping to real mapping (so that loadedIdMap is updated in right order) lock (lockObject) { RemoveUnsaved(values, currentTransaction); foreach (var value in values) { AddLoaded(value); } } } finally { if (LockEnabled && stream is FileStream) { NativeLockFile.UnlockFile((FileStream)stream, indexStreamPosition, long.MaxValue - indexStreamPosition); } } } }
/// <summary> /// Refreshes URL to ObjectId mapping from the latest results in the index file. /// </summary> /// <returns>True on success.</returns> public bool LoadNewValues() { if (stream == null) { throw new InvalidOperationException("No active stream."); } lock (stream) { var position = stream.Position; var fileSize = stream.Length; if (position == fileSize) { return(true); } // Lock content that will be read. // This lock doesn't prevent concurrent writing since we lock only until current known filesize. // In the case where fileSize was taken at the time of an incomplete append, this lock will also wait for completion of the last write. // Note: Maybe we should release the lock quickly so that two threads can read at the same time? // Or if the previously described case doesn't happen, maybe no lock at all is required? // Otherwise, last possibility would be deterministic filesize (with size encoded at the beginning of each block). if (LockEnabled && stream is FileStream) { NativeLockFile.LockFile((FileStream)stream, position, long.MaxValue - position, false); } try { // update the size after the lock fileSize = stream.Length; RefreshData(fileSize); } finally { // Release the lock if (LockEnabled && stream is FileStream) { NativeLockFile.UnlockFile((FileStream)stream, position, long.MaxValue - position); } } return(true); } }