os: avoid error result when dir is removed out from under RemoveAll

Fixes #7776.

LGTM=bradfitz
R=golang-codereviews, bradfitz
CC=golang-codereviews, r
https://golang.org/cl/145860043
diff --git a/src/os/os_test.go b/src/os/os_test.go
index 0224c9b..7a28497 100644
--- a/src/os/os_test.go
+++ b/src/os/os_test.go
@@ -18,6 +18,7 @@
 	"runtime"
 	"sort"
 	"strings"
+	"sync"
 	"syscall"
 	"testing"
 	"text/template"
@@ -1403,3 +1404,44 @@
 		}
 	}
 }
+
+func mkdirTree(t *testing.T, root string, level, max int) {
+	if level >= max {
+		return
+	}
+	level++
+	for i := 'a'; i < 'c'; i++ {
+		dir := filepath.Join(root, string(i))
+		if err := Mkdir(dir, 0700); err != nil {
+			t.Fatal(err)
+		}
+		mkdirTree(t, dir, level, max)
+	}
+}
+
+// Test that simultaneous RemoveAll do not report an error.
+// As long as it gets removed, we should be happy.
+func TestRemoveAllRace(t *testing.T) {
+	n := runtime.GOMAXPROCS(16)
+	defer runtime.GOMAXPROCS(n)
+	root, err := ioutil.TempDir("", "issue")
+	if err != nil {
+		t.Fatal(err)
+	}
+	mkdirTree(t, root, 1, 6)
+	hold := make(chan struct{})
+	var wg sync.WaitGroup
+	for i := 0; i < 4; i++ {
+		wg.Add(1)
+		go func() {
+			defer wg.Done()
+			<-hold
+			err := RemoveAll(root)
+			if err != nil {
+				t.Errorf("unexpected error: %T, %q", err, err)
+			}
+		}()
+	}
+	close(hold) // let workers race to remove root
+	wg.Wait()
+}
diff --git a/src/os/path.go b/src/os/path.go
index 02a77ec..6cc6940 100644
--- a/src/os/path.go
+++ b/src/os/path.go
@@ -66,7 +66,7 @@
 func RemoveAll(path string) error {
 	// Simple case: if Remove works, we're done.
 	err := Remove(path)
-	if err == nil {
+	if err == nil || IsNotExist(err) {
 		return nil
 	}
 
@@ -116,6 +116,9 @@
 
 	// Remove directory.
 	err1 := Remove(path)
+	if err1 == nil || IsNotExist(err1) {
+		return nil
+	}
 	if err == nil {
 		err = err1
 	}