public void Flush(UIntPtr capacity) { if (_viewHandle != null) { unsafe { byte *firstPagePtr = null; try { _viewHandle.AcquirePointer(ref firstPagePtr); bool success = Interop.mincore.FlushViewOfFile((IntPtr)firstPagePtr, capacity) != 0; if (success) { return; // This will visit the finally block. } // It is a known issue within the NTFS transaction log system that // causes FlushViewOfFile to intermittently fail with ERROR_LOCK_VIOLATION // As a workaround, we catch this particular error and retry the flush operation // a few milliseconds later. If it does not work, we give it a few more tries with // increasing intervals. Eventually, however, we need to give up. In ad-hoc tests // this strategy successfully flushed the view after no more than 3 retries. int error = Marshal.GetLastWin32Error(); bool canRetry = (!success && error == Interop.mincore.Errors.ERROR_LOCK_VIOLATION); SpinWait spinWait = new SpinWait(); for (int w = 0; canRetry && w < MaxFlushWaits; w++) { int pause = (1 << w); // MaxFlushRetries should never be over 30 MemoryMappedFile.ThreadSleep(pause); for (int r = 0; canRetry && r < MaxFlushRetriesPerWait; r++) { success = Interop.mincore.FlushViewOfFile((IntPtr)firstPagePtr, capacity) != 0; if (success) { return; // This will visit the finally block. } spinWait.SpinOnce(); error = Marshal.GetLastWin32Error(); canRetry = (error == Interop.mincore.Errors.ERROR_LOCK_VIOLATION); } } // We got to here, so there was no success: throw Win32Marshal.GetExceptionForWin32Error(error); } finally { if (firstPagePtr != null) { _viewHandle.ReleasePointer(); } } } } }