Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Workaround for CPU spin on NamedPipeClientStream.Connect() #13

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 30 additions & 1 deletion NamedPipeWrapper/NamedPipeClient.cs
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -219,13 +221,40 @@ public static PipeStreamWrapper<TRead, TWrite> Connect<TRead, TWrite>(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;
}

private static NamedPipeClientStream CreatePipe(string pipeName,string serverName)
{
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;
}
}
}
}
}
}
1 change: 1 addition & 0 deletions NamedPipeWrapper/NamedPipeWrapper.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
<Compile Include="IO\PipeStreamReader.cs" />
<Compile Include="IO\PipeStreamWrapper.cs" />
<Compile Include="IO\PipeStreamWriter.cs" />
<Compile Include="Native\Kernel32.cs" />
<Compile Include="PipeExceptionEventHandler.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="NamedPipeServer.cs" />
Expand Down
17 changes: 17 additions & 0 deletions NamedPipeWrapper/Native/Kernel32.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using System.Runtime.InteropServices;

namespace NamedPipeWrapper.Native
{
internal sealed class Kernel32
{
/// <summary>
/// Waits until either a time-out interval elapses or an instance of the specified named pipe is available for connection
/// </summary>
/// <param name="lpNamedPipeName">The name of the named pipe. The string must include the name of the computer on which the server process is executing.</param>
/// <param name="nTimeOut">If no instances of the specified named pipe exist, the WaitNamedPipe function returns immediately, regardless of the time-out value.</param>
/// <returns></returns>
[return: MarshalAs(UnmanagedType.Bool)]
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
internal static extern bool WaitNamedPipe(string lpNamedPipeName, uint nTimeOut);
}
}