Skip to content

Migrate Socket between processes #48637

@shirhatti

Description

@shirhatti

Background and Motivation

As part of the hot reload scenarios in .NET 6, ASP.NET Core is looking at moving the socket creation code to an external process.
Having the listen socket bound to in a separate process allows for inner-loop improvements by potentially parallelizing operations in the startup path since the OS will hold connections prior to your application calling accept. For e.g., you do not need to wait for the application to successfully start before launching your browser.

To allow binding to a socket in a different process than the listener would require a mechanism to pass the socket between processes. Normally this achieved by just forking the process and inheriting the file descriptors. Unfortunately, implementing this in .NET is slightly challenging since the O_CLOEXEC flag is set on all Sockets. Providing an API to duplicate sockets would make this is a lot easier.

On Windows, there is already an API that wraps WSADuplicateSocket, but unfortunately it duplicates and closes the socket. We'd need an API that just duplicates the socket.

Proposed API

namespace System.Net.Sockets
{
  public partial class Socket
  {
+    public SocketInformation DuplicateSocketWindows(int targetProcessId)
+    public SafeSocketHandle DuplicateSocketLinux()
  }
}

I don't actually propose naming these DuplicateWindows and DuplicateLinux, but I couldn't think of good suggestions

Usage Examples

var ipEndPoint = new IPEndPoint(IPAddress.Loopback, port);
using var listenSocket = new Socket(ipEndPoint.AddressFamily,
                                    SocketType.Stream,
                                    ProtocolType.Tcp);
listenSocket.Bind(ipEndPoint);
var duplicatedSocket = listenSocket.DuplicateSocketLinux();
var psi = new ProcessStartInfo("my-web-server");
psi.EnvironmentVariables["LISTEN_FD"] = duplicatedSocket.DangerousGetHandle().ToInt32().ToString();
var process = Process.Start(psi);

A fully fleshed example that uses the Windows and Linux variant:

Linux: https://github.com/shirhatti/zocket/blob/main/src/zocket/Program.cs#L52
Windows: https://github.com/shirhatti/zocket/blob/main/src/zocket/Program.cs#L90

Alternative Designs

I proposed duplicating (and not setting O_CLOEXEC on the duplicated socket) as opposed calling fcntl on the original socket to preserve some semblance of symmetry between the Windows and Linux use cases.

This is common feature of development server in other language ecosystem: Werkzeug, Lithos, systemfd

Risks

Improper usage of this API could result in leaking file descriptors, but it's hard to mitigate that since the goal of this API is intentional leakage of the file descriptor.

cc @jkotalik @pranavkm

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions