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