Merge pull request #31 from thaJeztah/18.06-backport-jjh.37562

[18.06] backport "don't invoke HCS shutdown if terminate called"
diff --git a/libcontainerd/client_local_windows.go b/libcontainerd/client_local_windows.go
index 6e3454e..01435f3 100644
--- a/libcontainerd/client_local_windows.go
+++ b/libcontainerd/client_local_windows.go
@@ -42,18 +42,17 @@
 	// have access to the Spec
 	ociSpec *specs.Spec
 
-	isWindows           bool
-	manualStopRequested bool
-	hcsContainer        hcsshim.Container
+	isWindows    bool
+	hcsContainer hcsshim.Container
 
-	id            string
-	status        Status
-	exitedAt      time.Time
-	exitCode      uint32
-	waitCh        chan struct{}
-	init          *process
-	execs         map[string]*process
-	updatePending bool
+	id               string
+	status           Status
+	exitedAt         time.Time
+	exitCode         uint32
+	waitCh           chan struct{}
+	init             *process
+	execs            map[string]*process
+	terminateInvoked bool
 }
 
 // Win32 error codes that are used for various workarounds
@@ -324,15 +323,15 @@
 	logger.Debug("starting container")
 	if err = hcsContainer.Start(); err != nil {
 		c.logger.WithError(err).Error("failed to start container")
-		ctr.debugGCS()
+		ctr.Lock()
 		if err := c.terminateContainer(ctr); err != nil {
 			c.logger.WithError(err).Error("failed to cleanup after a failed Start")
 		} else {
 			c.logger.Debug("cleaned up after failed Start by calling Terminate")
 		}
+		ctr.Unlock()
 		return err
 	}
-	ctr.debugGCS()
 
 	c.Lock()
 	c.containers[id] = ctr
@@ -524,11 +523,13 @@
 	if err = hcsContainer.Start(); err != nil {
 		c.logger.WithError(err).Error("failed to start container")
 		ctr.debugGCS()
+		ctr.Lock()
 		if err := c.terminateContainer(ctr); err != nil {
 			c.logger.WithError(err).Error("failed to cleanup after a failed Start")
 		} else {
 			c.logger.Debug("cleaned up after failed Start by calling Terminate")
 		}
+		ctr.Unlock()
 		return err
 	}
 	ctr.debugGCS()
@@ -848,8 +849,6 @@
 		return err
 	}
 
-	ctr.manualStopRequested = true
-
 	logger := c.logger.WithFields(logrus.Fields{
 		"container": containerID,
 		"process":   processID,
@@ -861,11 +860,14 @@
 	if processID == InitProcessName {
 		if syscall.Signal(signal) == syscall.SIGKILL {
 			// Terminate the compute system
+			ctr.Lock()
+			ctr.terminateInvoked = true
 			if err := ctr.hcsContainer.Terminate(); err != nil {
 				if !hcsshim.IsPending(err) {
 					logger.WithError(err).Error("failed to terminate hccshim container")
 				}
 			}
+			ctr.Unlock()
 		} else {
 			// Shut down the container
 			if err := ctr.hcsContainer.Shutdown(); err != nil {
@@ -1167,12 +1169,17 @@
 	return ctr, p, nil
 }
 
+// ctr mutex must be held when calling this function.
 func (c *client) shutdownContainer(ctr *container) error {
-	const shutdownTimeout = time.Minute * 5
-	err := ctr.hcsContainer.Shutdown()
+	var err error
+	const waitTimeout = time.Minute * 5
 
-	if hcsshim.IsPending(err) {
-		err = ctr.hcsContainer.WaitTimeout(shutdownTimeout)
+	if !ctr.terminateInvoked {
+		err = ctr.hcsContainer.Shutdown()
+	}
+
+	if hcsshim.IsPending(err) || ctr.terminateInvoked {
+		err = ctr.hcsContainer.WaitTimeout(waitTimeout)
 	} else if hcsshim.IsAlreadyStopped(err) {
 		err = nil
 	}
@@ -1192,8 +1199,10 @@
 	return nil
 }
 
+// ctr mutex must be held when calling this function.
 func (c *client) terminateContainer(ctr *container) error {
 	const terminateTimeout = time.Minute * 5
+	ctr.terminateInvoked = true
 	err := ctr.hcsContainer.Terminate()
 
 	if hcsshim.IsPending(err) {
@@ -1259,7 +1268,6 @@
 		ctr.exitedAt = exitedAt
 		ctr.exitCode = uint32(exitCode)
 		close(ctr.waitCh)
-		ctr.Unlock()
 
 		if err := c.shutdownContainer(ctr); err != nil {
 			exitCode = -1
@@ -1273,6 +1281,7 @@
 		} else {
 			logger.Debug("completed container shutdown")
 		}
+		ctr.Unlock()
 
 		if err := ctr.hcsContainer.Close(); err != nil {
 			exitCode = -1