Skip to content

[API Proposal]: Add PipeOptions.FirstPipeInstance enum value #76984

Description

@JamesNK

Background and motivation

It's not possible for an app hosting multiple named pipe connections to verify that no one else is using the pipe name when it starts.

For example, Kestrel is looking at adding a named pipe transport, and when we start Kestrel we want to verify that no one else is using the configured pipe name:

  • We don't want to share the pipe between Kestrel instances. It's common for devs to accidentally launch Kestrel when it's already running. A socket errors if you bind to it when it's already open. We want the same protection with a named pipe.
  • A pipe gets many of its settings when it is first opened. Want to verify that Kestrel's settings are applied to the pipe.

Named pipes have a built-in option for checking this - FILE_FLAG_FIRST_PIPE_INSTANCE. See https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-createnamedpipea

.NET doesn't expose this option. While it is automatically set if the max number of pipe instances is 1, there is no way to explicitly set this option.

int openMode = ((int)direction) |
(maxNumberOfServerInstances == 1 ? Interop.Kernel32.FileOperations.FILE_FLAG_FIRST_PIPE_INSTANCE : 0) |
(int)options |
(int)additionalAccessRights;
.

If the PipeOptions.FirstPipeInstance options was available, then Kestrel would create the first NamedPipeServerStream with it the option to verify that there is no existing pipe with that name and create subsequent NamedPipeServerStream instances without the option.

Note that validation on the NamedPipeServerStream ctor currently prevents the enum value having FILE_FLAG_FIRST_PIPE_INSTANCE value added to it. e.g.

// NamedPipeServerStream errors when passed this pipe option
pipeOptions |= (PipeOptions)0x00080000;

API Proposal

namespace System.IO.Pipes
{
    [Flags]
    public enum PipeOptions
    {
        FirstPipeInstance = unchecked((int)0x00080000)
    }
}

API Usage

private NamedPipeServerStream CreateServerStream(bool firstStream)
{
    var pipeOptions = PipeOptions.Asynchronous | PipeOptions.WriteThrough;
    if (_options.CurrentUserOnly)
    {
        pipeOptions |= PipeOptions.CurrentUserOnly;
    }
    if (firstStream)
    {
        pipeOptions |= PipeOptions.FirstPipeInstance;
    }

    return new NamedPipeServerStream(
        _endpoint.PipeName,
        PipeDirection.InOut,
        NamedPipeServerStream.MaxAllowedServerInstances,
        PipeTransmissionMode.Byte,
        pipeOptions,
        inBufferSize: 0,
        outBufferSize: 0);
}

Alternative Designs

[Flags]
public enum PipeOptions
{
    EnsureFirstPipeInstance = unchecked((int)0x00080000)
}

It still alludes at the name of the Windows API flag and conveys "throw if already exists".

Risks

No response

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions