From 83ee7baed689cced8c28e4d4c4631132cad86dbd Mon Sep 17 00:00:00 2001 From: marcbarry Date: Tue, 14 Mar 2017 21:35:12 +0000 Subject: [PATCH] workaround for cpu spin when NamedPipeClientStream.Connect() is called without a timeout --- NamedPipeWrapper/NamedPipeClient.cs | 31 +++++++++++++++++++++++- NamedPipeWrapper/NamedPipeWrapper.csproj | 1 + NamedPipeWrapper/Native/Kernel32.cs | 17 +++++++++++++ 3 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 NamedPipeWrapper/Native/Kernel32.cs diff --git a/NamedPipeWrapper/NamedPipeClient.cs b/NamedPipeWrapper/NamedPipeClient.cs index a1132c5..ed23855 100644 --- a/NamedPipeWrapper/NamedPipeClient.cs +++ b/NamedPipeWrapper/NamedPipeClient.cs @@ -1,10 +1,12 @@ using System; using System.Collections.Generic; +using System.IO; using System.IO.Pipes; using System.Linq; using System.Text; using System.Threading; using NamedPipeWrapper.IO; +using NamedPipeWrapper.Native; using NamedPipeWrapper.Threading; namespace NamedPipeWrapper @@ -219,7 +221,7 @@ public static PipeStreamWrapper Connect(string pip public static NamedPipeClientStream CreateAndConnectPipe(string pipeName, string serverName) { var pipe = CreatePipe(pipeName, serverName); - pipe.Connect(); + ConnectWithRetry(pipe, Path.GetFullPath($@"\\{serverName}\pipe\{pipeName}")); return pipe; } @@ -227,5 +229,32 @@ private static NamedPipeClientStream CreatePipe(string pipeName,string serverNam { return new NamedPipeClientStream(serverName, pipeName, PipeDirection.InOut, PipeOptions.Asynchronous | PipeOptions.WriteThrough); } + + private static void ConnectWithRetry(NamedPipeClientStream pipe, string fullPath) + { + var connected = false; + var resetEvent = new ManualResetEvent(false); + + while (!connected) + { + if (!Kernel32.WaitNamedPipe(fullPath, 0xffffffff)) + { + resetEvent.WaitOne(100); // prevent cpu spin + } + else + { + try + { + // NamedPipeClientStream.Connect() defaults to a timeout value of -1, which blocks and spins the CPU, provide a sensible timeout value for Connect(). + pipe.Connect(15000); + connected = true; + } + catch (TimeoutException) + { + connected = false; + } + } + } + } } } diff --git a/NamedPipeWrapper/NamedPipeWrapper.csproj b/NamedPipeWrapper/NamedPipeWrapper.csproj index 90ec8e9..b86e053 100644 --- a/NamedPipeWrapper/NamedPipeWrapper.csproj +++ b/NamedPipeWrapper/NamedPipeWrapper.csproj @@ -45,6 +45,7 @@ + diff --git a/NamedPipeWrapper/Native/Kernel32.cs b/NamedPipeWrapper/Native/Kernel32.cs new file mode 100644 index 0000000..8092831 --- /dev/null +++ b/NamedPipeWrapper/Native/Kernel32.cs @@ -0,0 +1,17 @@ +using System.Runtime.InteropServices; + +namespace NamedPipeWrapper.Native +{ + internal sealed class Kernel32 + { + /// + /// Waits until either a time-out interval elapses or an instance of the specified named pipe is available for connection + /// + /// The name of the named pipe. The string must include the name of the computer on which the server process is executing. + /// If no instances of the specified named pipe exist, the WaitNamedPipe function returns immediately, regardless of the time-out value. + /// + [return: MarshalAs(UnmanagedType.Bool)] + [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] + internal static extern bool WaitNamedPipe(string lpNamedPipeName, uint nTimeOut); + } +} \ No newline at end of file