<p dir="auto">by <strong>patrick.allen.higgins</strong>:</p>
<pre class="notranslate">Sorry that this is so wordy and without a simple test case. I haven't been able to
narrow it down to a simpler one yet, and may not have the time to do so. The tl;dr
version is that GDB clearly shows syscall.ForkLock as being locked when I don't believe
it should be.

I have a program which consists of a master which spawns slaves.

The master opens three net.Listener sockets and then spawns the first slave, passing it
the listener FDs.

On receipt of SIGHUP, the master spawns a new slave, passing it the listener FDs.

When a slave finishes initialization and starts serving HTTP requests on the 3
listeners, it sends a SIGUSR1 to the master.

On receipt of SIGUSR1, the master sends a SIGTERM to all but the most recently spawned
slave.

On receipt of SIGTERM, a slave closes all three of its listeners and then exits.

The master has a goroutine for each slave waiting for them to exit.

By running a "kill -HUP $master_pid" several times in rapid succession from
the shell (by hand, not a script), eventually I notice that the slaves do not exit.

Using lsof shows that one of the listeners is being closed, even though I close it.

I tried to insert code to run lsof at key points in the slave lifecycle and find that it
won't run at a certain point because it is waiting to acquire the syscall.ForkLock:

(gdb) gor 1 bt
#0  0x000000000041bf3c in runtime.gosched () at
/home/phiggins/go-tip/src/pkg/runtime/proc.c:967
#1  0x000000000041bfa6 in runtime.park (unlockf=void, lock=void, reason=void) at
/home/phiggins/go-tip/src/pkg/runtime/proc.c:979
#2  0x0000000000423ca6 in semacquireimpl (addr=void, profile=void) at
/home/phiggins/go-tip/src/pkg/runtime/sema.goc:1479
#3  0x0000000000423eae in sync.runtime_Semacquire (addr=void) at
/home/phiggins/go-tip/src/pkg/runtime/sema.goc:1533
#4  0x00000000004c2101 in sync.(*RWMutex).Lock (rw=0x916a98) at
/home/phiggins/go-tip/src/pkg/sync/rwmutex.go:80
#5  0x00000000004c9f51 in syscall.forkExec (argv0="/usr/sbin/lsof", argv=
[]string = {...}, attr=0xc2008370a0, pid=128849834000, err=0)
    at /home/phiggins/go-tip/src/pkg/syscall/exec_unix.go:183
#6  0x00000000004ca547 in syscall.StartProcess (argv0="/usr/sbin/lsof", argv=
[]string = {...}, attr=0xc2008370a0, pid=2, handle=4, err=0)
    at /home/phiggins/go-tip/src/pkg/syscall/exec_unix.go:244
#7  0x00000000004bcd27 in os.startProcess (name="/usr/sbin/lsof", argv=
[]string = {...}, attr=0xc200837050, p=0xc200091d80, err=0)
    at /home/phiggins/go-tip/src/pkg/os/exec_posix.go:45
#8  0x00000000004bbf9c in os.StartProcess (name="/usr/sbin/lsof", argv=
[]string = {...}, attr=0xc200837050, noname=void)
    at /home/phiggins/go-tip/src/pkg/os/doc.go:24
#9  0x00000000004c3f72 in os/exec.(*Cmd).Start (c=void, noname=void) at
/home/phiggins/go-tip/src/pkg/os/exec/exec.go:262
#10 0x00000000004c39a5 in os/exec.(*Cmd).Run (c=0xc200838000, noname=void) at
/home/phiggins/go-tip/src/pkg/os/exec/exec.go:229
#11 0x00000000004039de in main.lsof (msg="pre-close") at
/home/phiggins/src/RecommendationEngine/go/src/recserv/lsof.go:15
#12 0x00000000004073f3 in main.slave (config=0xc2001060e0) at
/home/phiggins/src/RecommendationEngine/go/src/recserv/slave.go:79
#13 0x0000000000406ab2 in main.main () at
/home/phiggins/src/RecommendationEngine/go/src/recserv/recserv.go:66

You can see that ForkLock is actually locked:

(gdb) p 'syscall.ForkLock'
$3 = {w = {state = 1, sema = 0}, writerSem = 0, readerSem = 0, readerCount =
-1073741823, readerWait = 1}

The full gdb session is attached.</pre>
<p dir="auto">Attachments:</p>
<ol dir="auto">
<li><a href="https://storage.googleapis.com/go-attachment/4737/0/recserv-gdb-session.txt" rel="nofollow">recserv-gdb-session.txt</a> (13883 bytes)</li>
</ol>
<pre class="notranslate"><a href="http://tip.golang.org/ref/spec#ShortVarDecl" rel="nofollow">http://tip.golang.org/ref/spec#ShortVarDecl</a> says:

"""a short variable declaration may redeclare variables provided they
were originally declared in the same block with the same type, and at least one of the
non-blank variables is new."""

It is not clear if

  func main() {
    a, a := 1, 2
  }

should be valid.

(gc rejects it, but it accepts "a := 1; b, a, a := 1, 2, 3")</pre>