<p dir="auto">I've written a repro test here:<br>
<a href="https://github.com/prashantv/listener-repro/blob/master/main.go">https://github.com/prashantv/listener-repro/blob/master/main.go</a></p>
<p dir="auto">It basically does the following:</p>
<ul dir="auto">
<li>Start a listener</li>
<li>Create <code class="notranslate">connectionsBeforeClose</code> connections to the listener (which should succeed)</li>
<li>Close the listener</li>
<li>Create another connection to the listener</li>
</ul>
<p dir="auto">On the listener side, I expect to receive at most <code class="notranslate">connectionsBeforeClose</code> successful connections. Any extra connections causes the <code class="notranslate">runTest</code> method to return an error.</p>
<p dir="auto">This works as expected on OSX, but on Linux, it seems to receive a successful connection after the listener has been closed.</p>
<p dir="auto">Some things I noticed:</p>
<ul dir="auto">
<li>If <code class="notranslate">connectionsBeforeClose</code> is set to 0, then no unexpected connections are made after the listener is closed</li>
<li>The issue also disappears if I <code class="notranslate">enableSleep</code></li>
</ul>
<p dir="auto">I would expect that a Listener will not accept any further socket attempts after <code class="notranslate">Close</code> has returned.</p>
<p dir="auto">Go version:  go1.4.2<br>
OS: Linux 3.13 x86_64</p>
<p dir="auto">The following test demonstrates what I consider a bug:</p>
<div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="package foo

import (
    &quot;log&quot;
    &quot;net&quot;
    &quot;testing&quot;
)

const addr = &quot;127.0.0.1:12345&quot;

func TestXxx(t *testing.T) {
    for i := 0; i &lt; 100000; i++ {
        log.Printf(&quot;Iteration %d&quot;, i+1)
        l, err := net.Listen(&quot;tcp4&quot;, addr)
        if err != nil {
            t.Fatalf(&quot;Failed to listen on %s: %v&quot;, addr, err)
        }
        go func(l net.Listener) {
            for {
                _, err := l.Accept()
                if err != nil {
                    return
                }
            }
        }(l)
        if err := l.Close(); err != nil {
            t.Fatalf(&quot;Failed to close server: %v&quot;, err)
        }
    }
}"><pre class="notranslate"><span class="pl-k">package</span> foo

<span class="pl-k">import</span> (
    <span class="pl-s">"log"</span>
    <span class="pl-s">"net"</span>
    <span class="pl-s">"testing"</span>
)

<span class="pl-k">const</span> <span class="pl-s1">addr</span> <span class="pl-c1">=</span> <span class="pl-s">"127.0.0.1:12345"</span>

<span class="pl-k">func</span> <span class="pl-en">TestXxx</span>(<span class="pl-s1">t</span> <span class="pl-c1">*</span>testing.<span class="pl-smi">T</span>) {
    <span class="pl-k">for</span> <span class="pl-s1">i</span> <span class="pl-c1">:=</span> <span class="pl-c1">0</span>; <span class="pl-s1">i</span> <span class="pl-c1">&lt;</span> <span class="pl-c1">100000</span>; <span class="pl-s1">i</span><span class="pl-c1">++</span> {
        <span class="pl-s1">log</span>.<span class="pl-en">Printf</span>(<span class="pl-s">"Iteration %d"</span>, <span class="pl-s1">i</span><span class="pl-c1">+</span><span class="pl-c1">1</span>)
        <span class="pl-s1">l</span>, <span class="pl-s1">err</span> <span class="pl-c1">:=</span> <span class="pl-s1">net</span>.<span class="pl-en">Listen</span>(<span class="pl-s">"tcp4"</span>, <span class="pl-s1">addr</span>)
        <span class="pl-k">if</span> <span class="pl-s1">err</span> <span class="pl-c1">!=</span> <span class="pl-c1">nil</span> {
            <span class="pl-s1">t</span>.<span class="pl-en">Fatalf</span>(<span class="pl-s">"Failed to listen on %s: %v"</span>, <span class="pl-s1">addr</span>, <span class="pl-s1">err</span>)
        }
        <span class="pl-k">go</span> <span class="pl-k">func</span>(<span class="pl-s1">l</span> net.<span class="pl-smi">Listener</span>) {
            <span class="pl-k">for</span> {
                <span class="pl-s1">_</span>, <span class="pl-s1">err</span> <span class="pl-c1">:=</span> <span class="pl-s1">l</span>.<span class="pl-en">Accept</span>()
                <span class="pl-k">if</span> <span class="pl-s1">err</span> <span class="pl-c1">!=</span> <span class="pl-c1">nil</span> {
                    <span class="pl-k">return</span>
                }
            }
        }(<span class="pl-s1">l</span>)
        <span class="pl-k">if</span> <span class="pl-s1">err</span> <span class="pl-c1">:=</span> <span class="pl-s1">l</span>.<span class="pl-en">Close</span>(); <span class="pl-s1">err</span> <span class="pl-c1">!=</span> <span class="pl-c1">nil</span> {
            <span class="pl-s1">t</span>.<span class="pl-en">Fatalf</span>(<span class="pl-s">"Failed to close server: %v"</span>, <span class="pl-s1">err</span>)
        }
    }
}</pre></div>
<p dir="auto">Briefly, within a loop it Listens on tcp port 12345, invokes a goroutine that calls Accept on the listener, and then Closes the listener. My expectation was that this would always pass regardless of scheduler behavior or number of loop iterations as I expected Close() would only return once the underlying socket is no longer in use.</p>
<p dir="auto">Instead what I see is that it will sometimes pass, but often fails on the net.Listen call of subsequent loop iterations with an error that the address is already in use. I believe I understand the cause of the failure. netFD underlies the TCPListener and it's a reference counted object. A goroutine blocked on Accept will increment the reference count. Close() flips a flag on netFD to indicate the socket is considered closed which will make other netFD operations like Accept return errors indicating the socket is closed. The underlying OS socket is only closed once the reference count for the netFD has reached zero.</p>
<p dir="auto">The end result is that an OS socket is only closed after TCPListener.Close() is invoked AND all goroutines that are blocked in TCPListener.Accept() are scheduled and return the expected error condition. It seems like more intuitive behavior would be for TCPListener.Close() to immediately close the underlying OS socket, and goroutines blocked in TCPListener.Accept() to return errors whenever they were next scheduled/run.</p>