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.
5 using System.Diagnostics;
6 using System.Runtime.InteropServices;
8 namespace System.Threading
11 // Implementation of ThreadPoolBoundHandle that sits on top of the CLR's ThreadPool and Overlapped infrastructure
15 /// Represents an I/O handle that is bound to the system thread pool and enables low-level
16 /// components to receive notifications for asynchronous I/O operations.
18 public sealed partial class ThreadPoolBoundHandle : IDisposable
20 private readonly SafeHandle _handle;
21 private bool _isDisposed;
23 private ThreadPoolBoundHandle(SafeHandle handle)
29 /// Gets the bound operating system handle.
32 /// A <see cref="SafeHandle"/> object that holds the bound operating system handle.
34 public SafeHandle Handle
36 get { return _handle; }
40 /// Returns a <see cref="ThreadPoolBoundHandle"/> for the specific handle,
41 /// which is bound to the system thread pool.
43 /// <param name="handle">
44 /// A <see cref="SafeHandle"/> object that holds the operating system handle. The
45 /// handle must have been opened for overlapped I/O on the unmanaged side.
48 /// <see cref="ThreadPoolBoundHandle"/> for <paramref name="handle"/>, which
49 /// is bound to the system thread pool.
51 /// <exception cref="ArgumentNullException">
52 /// <paramref name="handle"/> is <see langword="null"/>.
54 /// <exception cref="ArgumentException">
55 /// <paramref name="handle"/> has been disposed.
59 /// <paramref name="handle"/> does not refer to a valid I/O handle.
63 /// <paramref name="handle"/> refers to a handle that has not been opened
64 /// for overlapped I/O.
68 /// <paramref name="handle"/> refers to a handle that has already been bound.
71 /// This method should be called once per handle.
75 /// <see cref="ThreadPoolBoundHandle"/> does not take ownership of <paramref name="handle"/>,
76 /// it remains the responsibility of the caller to call <see cref="SafeHandle.Dispose"/>.
78 public static ThreadPoolBoundHandle BindHandle(SafeHandle handle)
81 throw new ArgumentNullException(nameof(handle));
83 if (handle.IsClosed || handle.IsInvalid)
84 throw new ArgumentException(SR.Argument_InvalidHandle, nameof(handle));
88 // ThreadPool.BindHandle will always return true, otherwise, it throws. See the underlying FCall
89 // implementation in ThreadPoolNative::CorBindIoCompletionCallback to see the implementation.
90 bool succeeded = ThreadPool.BindHandle(handle);
91 Debug.Assert(succeeded);
94 { // BindHandle throws ApplicationException on full CLR and Exception on CoreCLR.
95 // We do not let either of these leak and convert them to ArgumentException to
96 // indicate that the specified handles are invalid.
98 if (ex.HResult == System.HResults.E_HANDLE) // Bad handle
99 throw new ArgumentException(SR.Argument_InvalidHandle, nameof(handle));
101 if (ex.HResult == System.HResults.E_INVALIDARG) // Handle already bound or sync handle
102 throw new ArgumentException(SR.Argument_AlreadyBoundOrSyncHandle, nameof(handle));
107 return new ThreadPoolBoundHandle(handle);
111 /// Returns an unmanaged pointer to a <see cref="NativeOverlapped"/> structure, specifying
112 /// a delegate that is invoked when the asynchronous I/O operation is complete, a user-provided
113 /// object providing context, and managed objects that serve as buffers.
115 /// <param name="callback">
116 /// An <see cref="IOCompletionCallback"/> delegate that represents the callback method
117 /// invoked when the asynchronous I/O operation completes.
119 /// <param name="state">
120 /// A user-provided object that distinguishes this <see cref="NativeOverlapped"/> from other
121 /// <see cref="NativeOverlapped"/> instances. Can be <see langword="null"/>.
123 /// <param name="pinData">
124 /// An object or array of objects representing the input or output buffer for the operation. Each
125 /// object represents a buffer, for example an array of bytes. Can be <see langword="null"/>.
128 /// An unmanaged pointer to a <see cref="NativeOverlapped"/> structure.
132 /// The unmanaged pointer returned by this method can be passed to the operating system in
133 /// overlapped I/O operations. The <see cref="NativeOverlapped"/> structure is fixed in
134 /// physical memory until <see cref="FreeNativeOverlapped(NativeOverlapped*)"/> is called.
137 /// The buffer or buffers specified in <paramref name="pinData"/> must be the same as those passed
138 /// to the unmanaged operating system function that performs the asynchronous I/O.
141 /// The buffers specified in <paramref name="pinData"/> are pinned for the duration of
142 /// the I/O operation.
145 /// <exception cref="ArgumentNullException">
146 /// <paramref name="callback"/> is <see langword="null"/>.
148 /// <exception cref="ObjectDisposedException">
149 /// This method was called after the <see cref="ThreadPoolBoundHandle"/> was disposed.
151 public unsafe NativeOverlapped* AllocateNativeOverlapped(IOCompletionCallback callback, object state, object pinData)
153 if (callback == null)
154 throw new ArgumentNullException(nameof(callback));
158 ThreadPoolBoundHandleOverlapped overlapped = new ThreadPoolBoundHandleOverlapped(callback, state, pinData, preAllocated: null);
159 overlapped._boundHandle = this;
160 return overlapped._nativeOverlapped;
164 /// Returns an unmanaged pointer to a <see cref="NativeOverlapped"/> structure, using the callback,
165 /// state, and buffers associated with the specified <see cref="PreAllocatedOverlapped"/> object.
167 /// <param name="preAllocated">
168 /// A <see cref="PreAllocatedOverlapped"/> object from which to create the NativeOverlapped pointer.
171 /// An unmanaged pointer to a <see cref="NativeOverlapped"/> structure.
175 /// The unmanaged pointer returned by this method can be passed to the operating system in
176 /// overlapped I/O operations. The <see cref="NativeOverlapped"/> structure is fixed in
177 /// physical memory until <see cref="FreeNativeOverlapped(NativeOverlapped*)"/> is called.
180 /// <exception cref="ArgumentNullException">
181 /// <paramref name="preAllocated"/> is <see langword="null"/>.
183 /// <exception cref="ArgumentException">
184 /// <paramref name="preAllocated"/> is currently in use for another I/O operation.
186 /// <exception cref="ObjectDisposedException">
187 /// This method was called after the <see cref="ThreadPoolBoundHandle"/> was disposed, or
188 /// this method was called after <paramref name="preAllocated"/> was disposed.
190 /// <seealso cref="PreAllocatedOverlapped"/>
191 public unsafe NativeOverlapped* AllocateNativeOverlapped(PreAllocatedOverlapped preAllocated)
193 if (preAllocated == null)
194 throw new ArgumentNullException(nameof(preAllocated));
198 preAllocated.AddRef();
201 ThreadPoolBoundHandleOverlapped overlapped = preAllocated._overlapped;
203 if (overlapped._boundHandle != null)
204 throw new ArgumentException(SR.Argument_PreAllocatedAlreadyAllocated, nameof(preAllocated));
206 overlapped._boundHandle = this;
208 return overlapped._nativeOverlapped;
212 preAllocated.Release();
218 /// Frees the unmanaged memory associated with a <see cref="NativeOverlapped"/> structure
219 /// allocated by the <see cref="AllocateNativeOverlapped"/> method.
221 /// <param name="overlapped">
222 /// An unmanaged pointer to the <see cref="NativeOverlapped"/> structure to be freed.
225 /// <note type="caution">
226 /// You must call the <see cref="FreeNativeOverlapped(NativeOverlapped*)"/> method exactly once
227 /// on every <see cref="NativeOverlapped"/> unmanaged pointer allocated using the
228 /// <see cref="AllocateNativeOverlapped"/> method.
229 /// If you do not call the <see cref="FreeNativeOverlapped(NativeOverlapped*)"/> method, you will
230 /// leak memory. If you call the <see cref="FreeNativeOverlapped(NativeOverlapped*)"/> method more
231 /// than once on the same <see cref="NativeOverlapped"/> unmanaged pointer, memory will be corrupted.
234 /// <exception cref="ArgumentNullException">
235 /// <paramref name="overlapped"/> is <see langword="null"/>.
237 /// <exception cref="ObjectDisposedException">
238 /// This method was called after the <see cref="ThreadPoolBoundHandle"/> was disposed.
240 public unsafe void FreeNativeOverlapped(NativeOverlapped* overlapped)
242 if (overlapped == null)
243 throw new ArgumentNullException(nameof(overlapped));
245 // Note: we explicitly allow FreeNativeOverlapped calls after the ThreadPoolBoundHandle has been Disposed.
247 ThreadPoolBoundHandleOverlapped wrapper = GetOverlappedWrapper(overlapped, this);
249 if (wrapper._boundHandle != this)
250 throw new ArgumentException(SR.Argument_NativeOverlappedWrongBoundHandle, nameof(overlapped));
252 if (wrapper._preAllocated != null)
253 wrapper._preAllocated.Release();
255 Overlapped.Free(overlapped);
259 /// Returns the user-provided object specified when the <see cref="NativeOverlapped"/> instance was
260 /// allocated using the <see cref="AllocateNativeOverlapped(IOCompletionCallback, object, byte[])"/>.
262 /// <param name="overlapped">
263 /// An unmanaged pointer to the <see cref="NativeOverlapped"/> structure from which to return the
264 /// asscociated user-provided object.
267 /// A user-provided object that distinguishes this <see cref="NativeOverlapped"/>
268 /// from other <see cref="NativeOverlapped"/> instances, otherwise, <see langword="null"/> if one was
269 /// not specified when the instance was allocated using <see cref="AllocateNativeOverlapped"/>.
271 /// <exception cref="ArgumentNullException">
272 /// <paramref name="overlapped"/> is <see langword="null"/>.
274 public unsafe static object GetNativeOverlappedState(NativeOverlapped* overlapped)
276 if (overlapped == null)
277 throw new ArgumentNullException(nameof(overlapped));
279 ThreadPoolBoundHandleOverlapped wrapper = GetOverlappedWrapper(overlapped, null);
280 Debug.Assert(wrapper._boundHandle != null);
281 return wrapper._userState;
284 private static unsafe ThreadPoolBoundHandleOverlapped GetOverlappedWrapper(NativeOverlapped* overlapped, ThreadPoolBoundHandle expectedBoundHandle)
286 ThreadPoolBoundHandleOverlapped wrapper;
289 wrapper = (ThreadPoolBoundHandleOverlapped)Overlapped.Unpack(overlapped);
291 catch (NullReferenceException ex)
293 throw new ArgumentException(SR.Argument_NativeOverlappedAlreadyFree, nameof(overlapped), ex);
299 public void Dispose()
301 // .NET Native's version of ThreadPoolBoundHandle that wraps the Win32 ThreadPool holds onto
302 // native resources so it needs to be disposable. To match the contract, we are also disposable.
303 // We also implement a disposable state to mimic behavior between this implementation and
304 // .NET Native's version (code written against us, will also work against .NET Native's version).
309 private void EnsureNotDisposed()
312 throw new ObjectDisposedException(GetType().ToString());