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

Disposing socket on Linux does not interrupt poll/select #26957

Closed
drieseng opened this issue Jul 25, 2018 · 4 comments
Closed

Disposing socket on Linux does not interrupt poll/select #26957

drieseng opened this issue Jul 25, 2018 · 4 comments
Assignees
Labels
area-System.Net.Sockets bug os-linux Linux OS (any supported distro)
Milestone

Comments

@drieseng
Copy link
Contributor

On Linux, disposing a socket on a given thread does not interrupt poll/select on another thread.

To reproduce, compile & run the following code and press enter when prompted:

// Comment this define to test using Socket.Select(...)
#define POLL

using System;
using System.Net;
using System.Net.Sockets;
using System.Threading.Tasks;

class Program
{
    static void Main()
    {
        var endPoint = new IPEndPoint(IPAddress.Loopback, 8080);
        var listener = new TcpListener(endPoint);
        listener.Start();

        var client = new Socket(endPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp) {NoDelay = true};
        client.Connect(endPoint);

        var listenerTask = Task.Factory.StartNew(s =>
            {
                var socket = (Socket) s;

#if POLL
                if (socket.Poll(-1, SelectMode.SelectRead))
                {
                    if (!socket.Connected)
                    {
                        Console.WriteLine("Connection closed.");
                    }
                }
#else
                var readSockets = new System.Collections.Generic.List<Socket> {socket};
                Socket.Select(readSockets, null, null, -1);
                if (!socket.Connected)
                {
                    Console.WriteLine("Connection closed.");
                }
#endif
            }, client, TaskCreationOptions.LongRunning);

        Console.WriteLine("Press enter to dispose the socket.");
        Console.ReadLine();

        Console.WriteLine("Disposing socket...");
        client.Dispose();
        Console.WriteLine("Socket disposed.");

        if (!listenerTask.Wait(5000))
            Console.WriteLine("Socket did not break out of Select call within 5 seconds.");

        Console.WriteLine("Done.");
    }
}

Expected result:

The following output is written to stdout:

Disposing socket...
Connection closed.
Socket disposed.
Done.

This is the result you get on the desktop CLR, and .NET Core on Windows.

Actual result:

Disposing socket...
(hang)

Info

.NET Core version: 2.1.302
OS: Ubuntu 14.04

@stephentoub
Copy link
Member

Have you tried the equivalent in C? Generally closing a socket while it's in use by synchronous operations is considered undefined behavior by the Berkeley sockets specification. On Windows it may work most of the time, but it's also possible to lead to crashes and other corruption. On Linux, it often results in hangs.

@wfurt has been looking into ways to make this better when used from managed code, but it would only be working around the inherent limitations of the underlying APIs / OSes, rather than it being a bug in managed code.

@wfurt
Copy link
Member

wfurt commented Jul 30, 2018

dotnet/corefx#26898 should resolve this and you can call shutdown() as workaround. As @stephentoub mentioned, we are trying to bend OS behavior where blocking IO operation behaves different on Linux/MacOS.

@wfurt wfurt self-assigned this Sep 18, 2018
@wfurt
Copy link
Member

wfurt commented Sep 24, 2018

I verified that dotnet/corefx#32087 fixes this issue.
In mean time you can workaround it by adding Shutdown() before the Dispose()

        Console.WriteLine("Disposing socket...");
+       client.Shutdown(SocketShutdown.Both);
        client.Dispose();
        Console.WriteLine("Socket disposed.");

wfurt referenced this issue in wfurt/corefx Sep 24, 2018
johnsimons referenced this issue in OctopusDeploy/SSH.NET Jun 11, 2019
@wfurt
Copy link
Member

wfurt commented Aug 6, 2019

fixed by dotnet/corefx#38804

@wfurt wfurt closed this as completed Aug 6, 2019
@msftgits msftgits transferred this issue from dotnet/corefx Jan 31, 2020
@msftgits msftgits added this to the 5.0 milestone Jan 31, 2020
@ghost ghost locked as resolved and limited conversation to collaborators Dec 16, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-System.Net.Sockets bug os-linux Linux OS (any supported distro)
Projects
None yet
Development

No branches or pull requests

4 participants