Skip to content

Commit

Permalink
Trying an alternate approach to cancel named pipe connection waiting
Browse files Browse the repository at this point in the history
  • Loading branch information
daveaglick committed Sep 27, 2018
1 parent b62fe3e commit 2a9776d
Showing 1 changed file with 64 additions and 3 deletions.
67 changes: 64 additions & 3 deletions src/MsBuildPipeLogger.Server/NamedPipeLoggerServer.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.IO.Pipes;
using System.Threading;
using System.Threading.Tasks;

namespace MsBuildPipeLogger
{
Expand All @@ -9,6 +10,8 @@ namespace MsBuildPipeLogger
/// </summary>
public class NamedPipeLoggerServer : PipeLoggerServer<NamedPipeServerStream>
{
public string PipeName { get; }

/// <summary>
/// Creates a named pipe server for receiving MSBuild logging events.
/// </summary>
Expand All @@ -24,11 +27,69 @@ public NamedPipeLoggerServer(string pipeName)
/// <param name="pipeName">The name of the pipe to create.</param>
/// <param name="cancellationToken">A <see cref="CancellationToken"/> that will cancel read operations if triggered.</param>
public NamedPipeLoggerServer(string pipeName, CancellationToken cancellationToken)
: base(new NamedPipeServerStream(pipeName, PipeDirection.In, -1, PipeTransmissionMode.Byte, PipeOptions.Asynchronous), cancellationToken)
: base(new NamedPipeServerStream(pipeName, PipeDirection.In), cancellationToken)
{
PipeName = pipeName;
CancellationToken.Register(CancelConnectionWait);
}

protected override void Connect()
{
PipeStream.WaitForConnection();
_connected.Set();
}

protected override void Connect() =>
CancellationToken.Try(() => PipeStream.WaitForConnectionAsync(CancellationToken).Wait());
private readonly InterlockedBool _connected = new InterlockedBool(false);

private void CancelConnectionWait()
{
if(!_connected.Set())
{
// This is a crazy hack that stops the WaitForConnection by connecting a dummy client
// We have to do it this way instead of checking for .IsConnected because if we connect
// and then disconnect very quickly, .IsConnected will never observe as true and we'll lock
using (NamedPipeClientStream pipeStream = new NamedPipeClientStream(".", PipeName, PipeDirection.Out))
{
pipeStream.Connect();
}
}
}
}

internal class InterlockedBool
{
private volatile int _set;

public InterlockedBool()
{
_set = 0;
}

public InterlockedBool(bool initialState)
{
_set = initialState ? 1 : 0;
}

// Returns the previous switch state of the switch
public bool Set()
{
#pragma warning disable 420
return Interlocked.Exchange(ref _set, 1) != 0;
#pragma warning restore 420
}

// Returns the previous switch state of the switch
public bool Unset()
{
#pragma warning disable 420
return Interlocked.Exchange(ref _set, 0) != 0;
#pragma warning restore 420
}

// Returns the current state
public static implicit operator bool(InterlockedBool interlockedBool)
{
return interlockedBool._set != 0;
}
}
}

0 comments on commit 2a9776d

Please sign in to comment.