public IOCompletionManager(int?numberOfCompletionPortThreads = null, string name = "<unnamed>") { Contract.Requires(!numberOfCompletionPortThreads.HasValue || numberOfCompletionPortThreads > 0); int effectiveNumberOfCompletionPortThreads = numberOfCompletionPortThreads ?? GetDefaultNumberOfCompletionPortThreads(); Contract.Assume(effectiveNumberOfCompletionPortThreads > 0); m_completionPort = FileSystemWin.CreateIOCompletionPort(); m_completionPortRefCount = 1; // Referenced by this manager. m_completionPortWorkers = new Thread[effectiveNumberOfCompletionPortThreads]; m_overlappedPool = new OverlappedPool(); long handleValue = m_completionPort.DangerousGetHandle().ToInt64(); ThreadStart workerEntry = CompletionWorkerThreadProc; for (int i = 0; i < effectiveNumberOfCompletionPortThreads; i++) { var newThread = new Thread(workerEntry) { Name = I($"IOCompletionManagerWorker (port handle 0x{handleValue:X}; '{name}')"), IsBackground = true }; m_completionPortWorkers[i] = newThread; newThread.Start(); } }
/// <inheritdoc /> public void BindFileHandle(SafeFileHandle handle) { Contract.Requires(handle != null); Contract.Requires(!handle.IsInvalid); FileSystemWin.BindFileHandleToIOCompletionPort(handle, m_completionPort, completionKey: IntPtr.Zero); }
public void OsVersionCheckConsistency() { // On Windows 8.1, this will return Windows 8. OperatingSystem version = Environment.OSVersion; XAssert.IsTrue(FileSystemWin.StaticIsOSVersionGreaterOrEqual(version.Version.Major, version.Version.Minor)); }
/// <summary> /// Revokes access using the SetPrivilege native methods instead of calling icacls /// </summary> public static void RevokeAccessNative(string testFilePath, LoggingContext loggingContext) { testFilePath = FileSystemWin.ToLongPathIfExceedMaxPath(testFilePath); // Restore name privilege is required to change the owner of the file FileUtilitiesWin.NativeMethods.SetPrivilege(FileUtilitiesWin.NativeMethods.SE_RESTORE_NAME, enablePrivilege: true, testFilePath, loggingContext); var fileInfo = new FileInfo(testFilePath); var fileSecurity = fileInfo.GetAccessControl(); // Remove any existing rules for the current user fileSecurity.RemoveAccessRuleAll( new FileSystemAccessRule( $"{Environment.UserDomainName}\\{Environment.UserName}", FileSystemRights.FullControl, AccessControlType.Allow)); // Add a new deny rule fileSecurity.AddAccessRule( new FileSystemAccessRule( $"{Environment.UserDomainName}\\{Environment.UserName}", FileSystemRights.FullControl, AccessControlType.Deny)); // Update the owner to SYSTEM fileSecurity.SetOwner(new NTAccount(@"NT AUTHORITY\SYSTEM")); fileInfo.SetAccessControl(fileSecurity); // Remove restore name privilege once complete FileUtilitiesWin.NativeMethods.SetPrivilege(FileUtilitiesWin.NativeMethods.SE_RESTORE_NAME, enablePrivilege: false, testFilePath, loggingContext); }
public void TestBadPathNameGetFileAttributes() { uint attrs = FileSystemWin.GetFileAttributesW(@"\\mscorlib.dll"); var hr = Marshal.GetLastWin32Error(); XAssert.AreEqual(NativeIOConstants.InvalidFileAttributes, attrs); XAssert.AreEqual(NativeIOConstants.ErrorBadPathname, hr); }
public void OsVersionCheckNegative() { // This comment is a time capsule to the person who has to fix this test when 999.5 is a shipping Windows version // (perhaps Windows Aquamarine Space Station Edition): Sorry! // The major.minor version 999.5 is definitely not a valid Windows version as of writing (we are at 10.0 currently), // and shouldn't be for a long while. But it would be very unfortunate if IsOSVersionGreaterOrEqual always returned true. XAssert.IsFalse(FileSystemWin.StaticIsOSVersionGreaterOrEqual(999, 5)); }
/// <inheritdoc /> public unsafe Overlapped *ReadFileOverlapped( IIOCompletionTarget target, SafeFileHandle handle, byte *pinnedBuffer, int bytesToRead, long fileOffset) { Contract.Requires(target != null); Contract.Requires(handle != null && !handle.IsInvalid); Contract.Requires(pinnedBuffer != null); TaggedOverlapped *overlapped = m_overlappedPool.ReserveOverlappedWithTarget(target); TraceStartIfEnabled(overlapped, target); bool needOverlappedRelease = true; bool overlappedHasBeenReleased = false; try { FileAsyncIOResult result = FileSystemWin.ReadFileOverlapped( handle, pinnedBuffer, bytesToRead, fileOffset, (Overlapped *)overlapped); if (result.Status != FileAsyncIOStatus.Pending) { Contract.Assert( result.Status == FileAsyncIOStatus.Succeeded || result.Status == FileAsyncIOStatus.Failed); // We could call the target directly. // However, since the target may itself issue more I/O, we need to prevent unbounded stack growth. // TODO: We could set a recursion-limit to allow some fraction of repeated IOs to complete synchronously, without // queueing to the threadpool. Sync completions are the common case for files cached in memory. ReleaseOvelappedAndQueueCompletionNotification(overlapped, result); overlappedHasBeenReleased = true; } // At this point overlapped is either needed (pending status) // or already released by ReleaseOvelappedAndQueueCompletionNotification needOverlappedRelease = false; return(!overlappedHasBeenReleased ? (Overlapped *)overlapped : null); } finally { if (needOverlappedRelease) { IIOCompletionTarget releasedTarget = ReleaseOverlappedAndGetTarget(overlapped); Contract.Assume(releasedTarget == target); } } }
private unsafe void CompletionWorkerThreadProc() { { int count; do { count = Volatile.Read(ref m_completionPortRefCount); if (count < 1) { // Manager disposed before this thread started. return; } }while (Interlocked.CompareExchange(ref m_completionPortRefCount, count + 1, comparand: count) != count); } try { while (true) { FileSystemWin.IOCompletionPortDequeueResult result = FileSystemWin.GetQueuedCompletionStatus(m_completionPort); Contract.Assume( result.Status != FileSystemWin.IOCompletionPortDequeueStatus.CompletionPortClosed, "We terminate all workers before closing the port (otherwise we risk a handle-recycle race)."); Contract.Assert(result.Status == FileSystemWin.IOCompletionPortDequeueStatus.Succeeded); if (result.DequeuedOverlapped == null) { // Completion port is being closed; this is a poison message. Contract.Assume(result.CompletionKey == s_queueCloseCompletionKey); break; } // The OVERLAPPED* attached to each packet is unique to each I/O request. It should be one // that we allocated earlier with AllocateOverlapped. We took care to place a request identifier // immediately after the OVERLAPPED, so we can find the completion target. Overlapped *deqeuedOverlapped = result.DequeuedOverlapped; var taggedOverlapped = (TaggedOverlapped *)deqeuedOverlapped; ReleaseOvelappedAndQueueCompletionNotification(taggedOverlapped, result.CompletedIO); } DecrementCompletionPortRefCount(); } catch (Exception ex) { ExceptionUtilities.FailFast("Catastrophic failure in I/O completion worker", ex); throw; } }
/// <inheritdoc/> public unsafe void CancelOverlapped(SafeFileHandle handle, Overlapped *overlapped) { Contract.Requires(!handle.IsInvalid); Contract.Requires(overlapped != null); TaggedOverlapped *taggedOverlapped = (TaggedOverlapped *)overlapped; if (taggedOverlapped->EntryId == TaggedOverlapped.AvailableMarker) { return; } // Best effort. Analysis.IgnoreResult(FileSystemWin.TryCancelIoWithOverlapped(handle, overlapped, out int _)); }
private void DisposeInternal() { // Cleanup is delicate. We must ensure that no workers are blocked on calls to GetQueuedCompletionStatus and that // no workers will newly call GetQueuedCompletionStatus, before it is safe to close the completion port handle. // Though we can inspect the result of GetQueuedCompletionStatus to determine that a port was closed *while blocked*, // we only depend on that as a sanity check that this cleanup works correctly; otherwise, we could have been unlucky // and instead tried to newly block concurrently with handle close (thus blocking on a potentially-recycled handle value). // So, we co-ordinate to ensure that all workers threads have exited before the completion port is closed. // Send enough poison messages to the port so that all workers wake up and exit. // We do this even when !disposing i.e., in the finalizer; this is okay since m_completionPort is a CriticalFinalizerObject // and therefore will have its finalizer called after this one (FileStream also depends on this for its SafeFileHandle, for example). for (int i = 0; i < m_completionPortWorkers.Length; i++) { FileSystemWin.PostQueuedCompletionStatus(m_completionPort, s_queueCloseCompletionKey); } // After all threads are finished, the reference count should end up at 1 (the manager reference). // We may have to yield to other threads to let them finish. Since we don't want to use any // synchronization calls in the finializer thread, just poll and yield. SpinWait spinner = default(SpinWait); while (Volatile.Read(ref m_completionPortRefCount) > 1) { spinner.SpinOnce(); } // All workers are expected to exit (or not start). The ref count will eventually reach 1. // Note that via these acrobatics we avoid blocking the finalizer thread on worker exit or calling anything on Thread. m_completionPortRefCount = 0; // All threads are completed and we can get rid of the pool. // This will prevent any new IO from starting. m_overlappedPool.Dispose(); // The port can be closed as well m_completionPort.Dispose(); }
/// <inheritdoc /> protected override bool ReleaseHandle() { return(FileSystemWin.CloseHandle(handle)); }