public IOCompletionPoller(nint port) { Debug.Assert(port != 0); _port = port; if (!UnsafeInlineIOCompletionCallbacks) { _nativeEvents = (Interop.Kernel32.OVERLAPPED_ENTRY *) NativeMemory.Alloc(NativeEventCapacity, (nuint)sizeof(Interop.Kernel32.OVERLAPPED_ENTRY)); _events = new(default);
public IOCompletionPoller(nint port) { Debug.Assert(port != 0); _port = port; if (!UnsafeInlineIOCompletionCallbacks) { _nativeEvents = (Interop.Kernel32.OVERLAPPED_ENTRY *) NativeMemory.Alloc(NativeEventCapacity, (nuint)sizeof(Interop.Kernel32.OVERLAPPED_ENTRY)); _events = new ThreadPoolTypedWorkItemQueue <Event, Callback>(); // These threads don't run user code, use a smaller stack size _thread = new Thread(Poll, SmallStackSizeBytes); // Poller threads are typically expected to be few in number and have to compete for time slices with all // other threads that are scheduled to run. They do only a small amount of work and don't run any user code. // In situations where frequently, a large number of threads are scheduled to run, a scheduled poller thread // may be delayed artificially quite a bit. The poller threads are given higher priority than normal to // mitigate that issue. It's unlikely that these threads would starve a system because in such a situation // IO completions would stop occurring. Since the number of IO pollers is configurable, avoid having too // many poller threads at higher priority. if (IOCompletionPollerCount * 4 < Environment.ProcessorCount) { _thread.Priority = ThreadPriority.AboveNormal; } } else { // These threads may run user code, use the default stack size _thread = new Thread(PollAndInlineCallbacks); } _thread.IsThreadPoolThread = true; _thread.IsBackground = true; _thread.Name = ".NET ThreadPool IO"; // Thread pool threads must start in the default execution context without transferring the context, so // using UnsafeStart() instead of Start() _thread.UnsafeStart(); }