From 35b5d9265251dd7ae1b2f70085820d25a4ada1e9 Mon Sep 17 00:00:00 2001 From: David Fowler Date: Mon, 1 May 2017 14:55:32 -0700 Subject: [PATCH] Fix EBUSY errors on uv_loop_close (#1798) * Fix race where ListenerPrimary is disposed before secondary listeners spin up - Since we only add listeners to dispatch pipes after receiving the "ack" message it's possible to have pipes that were created but not acked yet. We might miss disposal of those pipes if they were never added to the list of _dispatchPipes. #1761 --- .../Internal/LibuvThread.cs | 11 +++++++++++ .../Internal/ListenerPrimary.cs | 9 +++++++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv/Internal/LibuvThread.cs b/src/Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv/Internal/LibuvThread.cs index 1591e57074..bc3e0139c5 100644 --- a/src/Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv/Internal/LibuvThread.cs +++ b/src/Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv/Internal/LibuvThread.cs @@ -264,6 +264,17 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal _post.Reference(); _post.Dispose(); + // We need this walk because we call ReadStop on on accepted connections when there's back pressure + // Calling ReadStop makes the handle as in-active which means the loop can + // end while there's still valid handles around. This makes loop.Dispose throw + // with an EBUSY. To avoid that, we walk all of the handles and dispose them. + Walk(ptr => + { + var handle = UvMemory.FromIntPtr(ptr); + // handle can be null because UvMemory.FromIntPtr looks up a weak reference + handle?.Dispose(); + }); + // Ensure the Dispose operations complete in the event loop. _loop.Run(); diff --git a/src/Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv/Internal/ListenerPrimary.cs b/src/Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv/Internal/ListenerPrimary.cs index 2b0fd8b039..7159d82c2c 100644 --- a/src/Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv/Internal/ListenerPrimary.cs +++ b/src/Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv/Internal/ListenerPrimary.cs @@ -18,7 +18,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal /// public class ListenerPrimary : Listener { + // The list of pipes that can be dispatched to (where we've confirmed the _pipeMessage) private readonly List _dispatchPipes = new List(); + // The list of pipes we've created but may not be part of _dispatchPipes + private readonly List _createdPipes = new List(); private int _dispatchIndex; private string _pipeName; private byte[] _pipeMessage; @@ -78,6 +81,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal } var dispatchPipe = new UvPipeHandle(Log); + // Add to the list of created pipes for disposal tracking + _createdPipes.Add(dispatchPipe); try { @@ -191,9 +196,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal { listener.ListenPipe.Dispose(); - foreach (var dispatchPipe in listener._dispatchPipes) + foreach (var pipe in listener._createdPipes) { - dispatchPipe.Dispose(); + pipe.Dispose(); } }, this).ConfigureAwait(false); }