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 (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 (stream is FileStream) { NativeLockFile.UnlockFile((FileStream)stream, indexStreamPosition, long.MaxValue - indexStreamPosition); } } } }
private void LockFile(long offset, long count, bool exclusive) { #if !XENKO_PLATFORM_UWP var fileStream = stream as FileStream; if (fileStream == null) { return; } #if XENKO_PLATFORM_ANDROID // Android does not support large file and thus is limited to files // whose sizes are less than 2GB. // We substract the offset to not go beyond the 2GB limit. count = (count + offset > int.MaxValue) ? int.MaxValue - offset: count; #endif #if XENKO_PLATFORM_WINDOWS_DESKTOP var countLow = (uint)count; var countHigh = (uint)(count >> 32); var overlapped = new NativeOverlapped() { InternalLow = IntPtr.Zero, InternalHigh = IntPtr.Zero, OffsetLow = (int)(offset & 0x00000000FFFFFFFF), OffsetHigh = (int)(offset >> 32), EventHandle = IntPtr.Zero, }; if (!NativeLockFile.LockFileEx(fileStream.SafeFileHandle, exclusive ? NativeLockFile.LOCKFILE_EXCLUSIVE_LOCK : 0, 0, countLow, countHigh, ref overlapped)) { throw new IOException("Couldn't lock file."); } #elif XENKO_RUNTIME_CORECLR // There is no implementation of FileStream.Lock on CoreCLR #else bool tryAgain; do { tryAgain = false; try { fileStream.Lock(offset, count); } catch (IOException) { tryAgain = true; } } while (tryAgain); #endif #endif }
/// <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 (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 (stream is FileStream) { NativeLockFile.UnlockFile((FileStream)stream, position, long.MaxValue - position); } } return(true); } }
private void UnlockFile(long offset, long count) { #if !XENKO_PLATFORM_UWP var fileStream = stream as FileStream; if (fileStream == null) { return; } #if XENKO_PLATFORM_ANDROID // See comment on `LockFile`. count = (count + offset > int.MaxValue) ? int.MaxValue - offset: count; #endif #if XENKO_PLATFORM_WINDOWS_DESKTOP var countLow = (uint)count; var countHigh = (uint)(count >> 32); var overlapped = new NativeOverlapped() { InternalLow = IntPtr.Zero, InternalHigh = IntPtr.Zero, OffsetLow = (int)(offset & 0x00000000FFFFFFFF), OffsetHigh = (int)(offset >> 32), EventHandle = IntPtr.Zero, }; if (!NativeLockFile.UnlockFileEx(fileStream.SafeFileHandle, 0, countLow, countHigh, ref overlapped)) { throw new IOException("Couldn't unlock file."); } #elif XENKO_RUNTIME_CORECLR // There is no implementation of FileStream.Unlock on CoreCLR #else fileStream.Unlock(offset, count); #endif #endif }