diff --git a/src/Microsoft.AspNetCore.Server.Kestrel/Http/ListenerPrimary.cs b/src/Microsoft.AspNetCore.Server.Kestrel/Http/ListenerPrimary.cs
index f2d9c2f3a9..4f5bb907fb 100644
--- a/src/Microsoft.AspNetCore.Server.Kestrel/Http/ListenerPrimary.cs
+++ b/src/Microsoft.AspNetCore.Server.Kestrel/Http/ListenerPrimary.cs
@@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
+using System.Runtime.InteropServices;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Server.Kestrel.Infrastructure;
using Microsoft.AspNetCore.Server.Kestrel.Networking;
@@ -11,7 +12,7 @@ using Microsoft.Extensions.Logging;
namespace Microsoft.AspNetCore.Server.Kestrel.Http
{
///
- /// A primary listener waits for incoming connections on a specified socket. Incoming
+ /// A primary listener waits for incoming connections on a specified socket. Incoming
/// connections may be passed to a secondary listener to handle.
///
public abstract class ListenerPrimary : Listener
@@ -19,8 +20,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
private readonly List _dispatchPipes = new List();
private int _dispatchIndex;
private string _pipeName;
+ private IntPtr _fileCompletionInfoPtr;
+ private bool _tryDetachFromIOCP = PlatformApis.IsWindows;
- // this message is passed to write2 because it must be non-zero-length,
+ // this message is passed to write2 because it must be non-zero-length,
// but it has no other functional significance
private readonly ArraySegment> _dummyMessage = new ArraySegment>(new[] { new ArraySegment(new byte[] { 1, 2, 3, 4 }) });
@@ -37,6 +40,13 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
{
_pipeName = pipeName;
+ if (_fileCompletionInfoPtr == IntPtr.Zero)
+ {
+ var fileCompletionInfo = new FILE_COMPLETION_INFORMATION() { Key = IntPtr.Zero, Port = IntPtr.Zero };
+ _fileCompletionInfoPtr = Marshal.AllocHGlobal(Marshal.SizeOf(fileCompletionInfo));
+ Marshal.StructureToPtr(fileCompletionInfo, _fileCompletionInfoPtr, false);
+ }
+
await StartAsync(address, thread).ConfigureAwait(false);
await Thread.PostAsync(state => ((ListenerPrimary)state).PostCallback(),
@@ -85,6 +95,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
}
else
{
+ DetachFromIOCP(socket);
var dispatchPipe = _dispatchPipes[index];
var write = new UvWriteReq(Log);
write.Init(Thread.Loop);
@@ -92,7 +103,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
dispatchPipe,
_dummyMessage,
socket,
- (write2, status, error, state) =>
+ (write2, status, error, state) =>
{
write2.Dispose();
((UvStreamHandle)state).Dispose();
@@ -101,12 +112,59 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
}
}
+ private void DetachFromIOCP(UvHandle handle)
+ {
+ if (!_tryDetachFromIOCP)
+ {
+ return;
+ }
+
+ // https://msdn.microsoft.com/en-us/library/windows/hardware/ff728840(v=vs.85).aspx
+ const int FileReplaceCompletionInformation = 61;
+ // https://msdn.microsoft.com/en-us/library/cc704588.aspx
+ const uint STATUS_INVALID_INFO_CLASS = 0xC0000003;
+
+ var statusBlock = new IO_STATUS_BLOCK();
+ var socket = IntPtr.Zero;
+ Thread.Loop.Libuv.uv_fileno(handle, ref socket);
+
+ if (NtSetInformationFile(socket, out statusBlock, _fileCompletionInfoPtr,
+ (uint)Marshal.SizeOf(), FileReplaceCompletionInformation) == STATUS_INVALID_INFO_CLASS)
+ {
+ // Replacing IOCP information is only supported on Windows 8.1 or newer
+ _tryDetachFromIOCP = false;
+ }
+ }
+
+ private struct IO_STATUS_BLOCK
+ {
+ uint status;
+ ulong information;
+ }
+
+ private struct FILE_COMPLETION_INFORMATION
+ {
+ public IntPtr Port;
+ public IntPtr Key;
+ }
+
+ [DllImport("NtDll.dll")]
+ private static extern uint NtSetInformationFile(IntPtr FileHandle,
+ out IO_STATUS_BLOCK IoStatusBlock, IntPtr FileInformation, uint Length,
+ int FileInformationClass);
+
public override async Task DisposeAsync()
{
// Call base first so the ListenSocket gets closed and doesn't
// try to dispatch connections to closed pipes.
await base.DisposeAsync().ConfigureAwait(false);
+ if (_fileCompletionInfoPtr != IntPtr.Zero)
+ {
+ Marshal.FreeHGlobal(_fileCompletionInfoPtr);
+ _fileCompletionInfoPtr = IntPtr.Zero;
+ }
+
if (Thread.FatalError == null && ListenPipe != null)
{
await Thread.PostAsync(state =>