diff --git a/daemon/monitor.go b/daemon/monitor.go index c05d1336c0..3286d63905 100644 --- a/daemon/monitor.go +++ b/daemon/monitor.go @@ -82,10 +82,10 @@ func (daemon *Daemon) StateChanged(id string, e libcontainerd.StateInfo) error { } return daemon.postRunProcessing(c, e) case libcontainerd.StateExitProcess: - c.Lock() - defer c.Unlock() if execConfig := c.ExecCommands.Get(e.ProcessID); execConfig != nil { ec := int(e.ExitCode) + execConfig.Lock() + defer execConfig.Unlock() execConfig.ExitCode = &ec execConfig.Running = false execConfig.Wait() diff --git a/integration-cli/docker_cli_exec_test.go b/integration-cli/docker_cli_exec_test.go index d74e1a80a8..cac76d96ae 100644 --- a/integration-cli/docker_cli_exec_test.go +++ b/integration-cli/docker_cli_exec_test.go @@ -539,3 +539,63 @@ func (s *DockerSuite) TestExecEnvLinksHost(c *check.C) { c.Assert(out, checker.Contains, "HOSTNAME=myhost") c.Assert(out, checker.Contains, "DB_NAME=/bar/db") } + +func (s *DockerSuite) TestExecWindowsOpenHandles(c *check.C) { + testRequires(c, DaemonIsWindows) + runSleepingContainer(c, "-d", "--name", "test") + exec := make(chan bool) + go func() { + dockerCmd(c, "exec", "test", "cmd", "/c", "start sleep 10") + exec <- true + }() + + for { + top := make(chan string) + var out string + go func() { + out, _ := dockerCmd(c, "top", "test") + top <- out + }() + + select { + case <-time.After(time.Second * 5): + c.Error("timed out waiting for top while exec is exiting") + case out = <-top: + break + } + + if strings.Count(out, "busybox.exe") == 2 && !strings.Contains(out, "cmd.exe") { + // The initial exec process (cmd.exe) has exited, and both sleeps are currently running + break + } + time.Sleep(1 * time.Second) + } + + inspect := make(chan bool) + go func() { + dockerCmd(c, "inspect", "test") + inspect <- true + }() + + select { + case <-time.After(time.Second * 5): + c.Error("timed out waiting for inspect while exec is exiting") + case <-inspect: + break + } + + // Ensure the background sleep is still running + out, _ := dockerCmd(c, "top", "test") + c.Assert(strings.Count(out, "busybox.exe"), checker.Equals, 2) + + // The exec should exit when the background sleep exits + select { + case <-time.After(time.Second * 15): + c.Error("timed out waiting for async exec to exit") + case <-exec: + // Ensure the background sleep has actually exited + out, _ := dockerCmd(c, "top", "test") + c.Assert(strings.Count(out, "busybox.exe"), checker.Equals, 1) + break + } +}