From 2041e4d08ba1b54307e38a7b630f3afd09a83fb1 Mon Sep 17 00:00:00 2001 From: Stephen Halter Date: Thu, 20 Aug 2015 20:13:10 -0700 Subject: [PATCH] Prevent pipes from being closed prematurely on OS X and Linux This change fixes the in-process marshalling of TCP handles on Mac and Linux that is used to support having multiple threads accepting connections from multiple loops. On these two platforms, the ReadStart callback somtimes gets called with a status and pipe_pending_count equal to zero. Now when the status is zero just exit the callback without closing the pipe. This change more closely follows the example at https://nikhilm.github.io/uvbook/processes.html#sending-file-descriptors-over-pipes --- .../Http/Connection.cs | 6 ++---- .../Http/ListenerSecondary.cs | 18 ++++++++++++++++-- .../Infrastructure/Constants.cs | 3 +++ .../Networking/Libuv.cs | 9 +++++++++ .../Networking/UvPipeHandle.cs | 5 +++++ 5 files changed, 35 insertions(+), 6 deletions(-) diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/Connection.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/Connection.cs index 49e777c59c..5fa34d141d 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/Connection.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/Connection.cs @@ -3,15 +3,13 @@ using System; using System.Diagnostics; +using Microsoft.AspNet.Server.Kestrel.Infrastructure; using Microsoft.AspNet.Server.Kestrel.Networking; namespace Microsoft.AspNet.Server.Kestrel.Http { public class Connection : ConnectionContext, IConnectionControl { - private const int EOF = -4095; - private const int ECONNRESET = -4077; - private static readonly Action _readCallback = ReadCallback; private static readonly Func _allocCallback = AllocCallback; @@ -60,7 +58,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http SocketInput.Unpin(status); var normalRead = error == null && status > 0; - var normalDone = status == 0 || status == ECONNRESET || status == EOF; + var normalDone = status == 0 || status == Constants.ECONNRESET || status == Constants.EOF; var errorDone = !(normalDone || normalRead); if (normalRead) diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/ListenerSecondary.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/ListenerSecondary.cs index f0948b867b..864dee2f63 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/ListenerSecondary.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/ListenerSecondary.cs @@ -1,11 +1,12 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using Microsoft.AspNet.Server.Kestrel.Networking; using System; using System.Diagnostics; using System.Runtime.InteropServices; using System.Threading.Tasks; +using Microsoft.AspNet.Server.Kestrel.Infrastructure; +using Microsoft.AspNet.Server.Kestrel.Networking; namespace Microsoft.AspNet.Server.Kestrel.Http { @@ -60,13 +61,26 @@ namespace Microsoft.AspNet.Server.Kestrel.Http (_1, _2, _3) => buf, (_1, status2, error2, state2) => { - if (status2 == 0) + if (status2 < 0) { + if (status2 != Constants.EOF) + { + Exception ex; + Thread.Loop.Libuv.Check(status2, out ex); + // TODO: Replace Trace.WriteLine with real logging + Trace.WriteLine("DispatchPipe.ReadStart " + ex.Message); + } + DispatchPipe.Dispose(); Marshal.FreeHGlobal(ptr); return; } + if (DispatchPipe.PendingCount() == 0) + { + return; + } + var acceptSocket = CreateAcceptSocket(); try diff --git a/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/Constants.cs b/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/Constants.cs index 06a2bed9d6..0f4b72424e 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/Constants.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/Constants.cs @@ -7,6 +7,9 @@ namespace Microsoft.AspNet.Server.Kestrel.Infrastructure { public const int ListenBacklog = 128; + public const int EOF = -4095; + public const int ECONNRESET = -4077; + /// /// Prefix of host name used to specify Unix sockets in the configuration. /// diff --git a/src/Microsoft.AspNet.Server.Kestrel/Networking/Libuv.cs b/src/Microsoft.AspNet.Server.Kestrel/Networking/Libuv.cs index a266ff8cc8..488780b93f 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Networking/Libuv.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Networking/Libuv.cs @@ -252,6 +252,15 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking Check(_uv_pipe_connect(req, handle, name, cb)); } + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + unsafe protected delegate int uv_pipe_pending_count(UvPipeHandle handle); + protected uv_pipe_pending_count _uv_pipe_pending_count = default(uv_pipe_pending_count); + unsafe public int pipe_pending_count(UvPipeHandle handle) + { + handle.Validate(); + return _uv_pipe_pending_count(handle); + } + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate void uv_alloc_cb(IntPtr server, int suggested_size, out uv_buf_t buf); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] diff --git a/src/Microsoft.AspNet.Server.Kestrel/Networking/UvPipeHandle.cs b/src/Microsoft.AspNet.Server.Kestrel/Networking/UvPipeHandle.cs index e110c57e6d..ea575a64dd 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Networking/UvPipeHandle.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Networking/UvPipeHandle.cs @@ -32,5 +32,10 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking { _uv.pipe_bind(this, name); } + + public int PendingCount() + { + return _uv.pipe_pending_count(this); + } } }