1 // Licensed to the .NET Foundation under one or more agreements. 2 // The .NET Foundation licenses this file to you under the MIT license. 3 // See the LICENSE file in the project root for more information. 4 5 using Microsoft.Win32.SafeHandles; 6 using System.Diagnostics; 7 using System.Runtime.InteropServices; 8 using System.Threading; 9 using System.IO; 10 11 namespace System.Threading 12 { 13 // 14 // Implementation of ThreadPoolBoundHandle that sits on top of the Win32 ThreadPool 15 // 16 public sealed class ThreadPoolBoundHandle : IDisposable, IDeferredDisposable 17 { 18 private readonly SafeHandle _handle; 19 private readonly SafeThreadPoolIOHandle _threadPoolHandle; 20 private DeferredDisposableLifetime<ThreadPoolBoundHandle> _lifetime; 21 22 #if MONO ThreadPoolBoundHandle()23 static ThreadPoolBoundHandle() 24 { 25 if (!Environment.IsRunningOnWindows) 26 throw new PlatformNotSupportedException(); 27 } 28 #endif 29 ThreadPoolBoundHandle(SafeHandle handle, SafeThreadPoolIOHandle threadPoolHandle)30 private ThreadPoolBoundHandle(SafeHandle handle, SafeThreadPoolIOHandle threadPoolHandle) 31 { 32 _threadPoolHandle = threadPoolHandle; 33 _handle = handle; 34 } 35 36 public SafeHandle Handle 37 { 38 get { return _handle; } 39 } 40 BindHandle(SafeHandle handle)41 public static ThreadPoolBoundHandle BindHandle(SafeHandle handle) 42 { 43 if (handle == null) 44 throw new ArgumentNullException(nameof(handle)); 45 46 if (handle.IsClosed || handle.IsInvalid) 47 throw new ArgumentException(SR.Argument_InvalidHandle, nameof(handle)); 48 49 IntPtr callback = AddrofIntrinsics.AddrOf<Interop.NativeIoCompletionCallback>(OnNativeIOCompleted); 50 SafeThreadPoolIOHandle threadPoolHandle = Interop.mincore.CreateThreadpoolIo(handle, callback, IntPtr.Zero, IntPtr.Zero); 51 if (threadPoolHandle.IsInvalid) 52 { 53 int errorCode = Marshal.GetLastWin32Error(); 54 if (errorCode == Interop.Errors.ERROR_INVALID_HANDLE) // Bad handle 55 throw new ArgumentException(SR.Argument_InvalidHandle, nameof(handle)); 56 57 if (errorCode == Interop.Errors.ERROR_INVALID_PARAMETER) // Handle already bound or sync handle 58 throw new ArgumentException(SR.Argument_AlreadyBoundOrSyncHandle, nameof(handle)); 59 60 throw Win32Marshal.GetExceptionForWin32Error(errorCode); 61 } 62 63 return new ThreadPoolBoundHandle(handle, threadPoolHandle); 64 } 65 66 [CLSCompliant(false)] AllocateNativeOverlapped(IOCompletionCallback callback, object state, object pinData)67 public unsafe NativeOverlapped* AllocateNativeOverlapped(IOCompletionCallback callback, object state, object pinData) 68 { 69 if (callback == null) 70 throw new ArgumentNullException(nameof(callback)); 71 72 AddRef(); 73 try 74 { 75 Win32ThreadPoolNativeOverlapped* overlapped = Win32ThreadPoolNativeOverlapped.Allocate(callback, state, pinData, preAllocated: null); 76 overlapped->Data._boundHandle = this; 77 78 Interop.mincore.StartThreadpoolIo(_threadPoolHandle); 79 80 return Win32ThreadPoolNativeOverlapped.ToNativeOverlapped(overlapped); 81 } 82 catch 83 { 84 Release(); 85 throw; 86 } 87 } 88 89 [CLSCompliant(false)] AllocateNativeOverlapped(PreAllocatedOverlapped preAllocated)90 public unsafe NativeOverlapped* AllocateNativeOverlapped(PreAllocatedOverlapped preAllocated) 91 { 92 if (preAllocated == null) 93 throw new ArgumentNullException(nameof(preAllocated)); 94 95 bool addedRefToThis = false; 96 bool addedRefToPreAllocated = false; 97 try 98 { 99 addedRefToThis = AddRef(); 100 addedRefToPreAllocated = preAllocated.AddRef(); 101 102 Win32ThreadPoolNativeOverlapped.OverlappedData data = preAllocated._overlapped->Data; 103 if (data._boundHandle != null) 104 throw new ArgumentException(SR.Argument_PreAllocatedAlreadyAllocated, nameof(preAllocated)); 105 106 data._boundHandle = this; 107 108 Interop.mincore.StartThreadpoolIo(_threadPoolHandle); 109 110 return Win32ThreadPoolNativeOverlapped.ToNativeOverlapped(preAllocated._overlapped); 111 } 112 catch 113 { 114 if (addedRefToPreAllocated) 115 preAllocated.Release(); 116 if (addedRefToThis) 117 Release(); 118 throw; 119 } 120 } 121 122 [CLSCompliant(false)] FreeNativeOverlapped(NativeOverlapped* overlapped)123 public unsafe void FreeNativeOverlapped(NativeOverlapped* overlapped) 124 { 125 if (overlapped == null) 126 throw new ArgumentNullException(nameof(overlapped)); 127 128 Win32ThreadPoolNativeOverlapped* threadPoolOverlapped = Win32ThreadPoolNativeOverlapped.FromNativeOverlapped(overlapped); 129 Win32ThreadPoolNativeOverlapped.OverlappedData data = GetOverlappedData(threadPoolOverlapped, this); 130 131 if (!data._completed) 132 { 133 Interop.mincore.CancelThreadpoolIo(_threadPoolHandle); 134 Release(); 135 } 136 137 data._boundHandle = null; 138 data._completed = false; 139 140 if (data._preAllocated != null) 141 data._preAllocated.Release(); 142 else 143 Win32ThreadPoolNativeOverlapped.Free(threadPoolOverlapped); 144 } 145 146 [CLSCompliant(false)] GetNativeOverlappedState(NativeOverlapped* overlapped)147 public static unsafe object GetNativeOverlappedState(NativeOverlapped* overlapped) 148 { 149 if (overlapped == null) 150 throw new ArgumentNullException(nameof(overlapped)); 151 152 Win32ThreadPoolNativeOverlapped* threadPoolOverlapped = Win32ThreadPoolNativeOverlapped.FromNativeOverlapped(overlapped); 153 Win32ThreadPoolNativeOverlapped.OverlappedData data = GetOverlappedData(threadPoolOverlapped, null); 154 155 return data._state; 156 } 157 GetOverlappedData(Win32ThreadPoolNativeOverlapped* overlapped, ThreadPoolBoundHandle expectedBoundHandle)158 private static unsafe Win32ThreadPoolNativeOverlapped.OverlappedData GetOverlappedData(Win32ThreadPoolNativeOverlapped* overlapped, ThreadPoolBoundHandle expectedBoundHandle) 159 { 160 Win32ThreadPoolNativeOverlapped.OverlappedData data = overlapped->Data; 161 162 if (data._boundHandle == null) 163 throw new ArgumentException(SR.Argument_NativeOverlappedAlreadyFree, nameof(overlapped)); 164 165 if (expectedBoundHandle != null && data._boundHandle != expectedBoundHandle) 166 throw new ArgumentException(SR.Argument_NativeOverlappedWrongBoundHandle, nameof(overlapped)); 167 168 return data; 169 } 170 171 [NativeCallable(CallingConvention = CallingConvention.StdCall)] OnNativeIOCompleted(IntPtr instance, IntPtr context, IntPtr overlappedPtr, uint ioResult, UIntPtr numberOfBytesTransferred, IntPtr ioPtr)172 private static unsafe void OnNativeIOCompleted(IntPtr instance, IntPtr context, IntPtr overlappedPtr, uint ioResult, UIntPtr numberOfBytesTransferred, IntPtr ioPtr) 173 { 174 var wrapper = ThreadPoolCallbackWrapper.Enter(); 175 Win32ThreadPoolNativeOverlapped* overlapped = (Win32ThreadPoolNativeOverlapped*)overlappedPtr; 176 177 ThreadPoolBoundHandle boundHandle = overlapped->Data._boundHandle; 178 if (boundHandle == null) 179 throw new InvalidOperationException(SR.Argument_NativeOverlappedAlreadyFree); 180 181 boundHandle.Release(); 182 183 Win32ThreadPoolNativeOverlapped.CompleteWithCallback(ioResult, (uint)numberOfBytesTransferred, overlapped); 184 wrapper.Exit(); 185 } 186 AddRef()187 private bool AddRef() 188 { 189 return _lifetime.AddRef(this); 190 } 191 Release()192 private void Release() 193 { 194 _lifetime.Release(this); 195 } 196 Dispose()197 public void Dispose() 198 { 199 _lifetime.Dispose(this); 200 GC.SuppressFinalize(this); 201 } 202 ~ThreadPoolBoundHandle()203 ~ThreadPoolBoundHandle() 204 { 205 #if MONO 206 if (!Environment.IsRunningOnWindows) 207 throw new PlatformNotSupportedException(); 208 #endif 209 210 // 211 // During shutdown, don't automatically clean up, because this instance may still be 212 // reachable/usable by other code. 213 // 214 if (!Environment.HasShutdownStarted) 215 Dispose(); 216 } 217 IDeferredDisposable.OnFinalRelease(bool disposed)218 void IDeferredDisposable.OnFinalRelease(bool disposed) 219 { 220 if (disposed) 221 _threadPoolHandle.Dispose(); 222 } 223 } 224 } 225