Skip to content

Commit 8730184

Browse files
committed
internal/lsp/fake: retry spurious file lock errors on windows
Cleaning the regtest sandbox sometimes fails on windows with errors that may be spurious. Copy logic from the testing package to retry these errors. For golang/go#53819 Change-Id: I059fbb5e023af1cd52a5d231cd11a7c2ae72bc92 Reviewed-on: https://go-review.googlesource.com/c/tools/+/417117 Run-TryBot: Robert Findley <[email protected]> Reviewed-by: Bryan Mills <[email protected]> TryBot-Result: Gopher Robot <[email protected]> gopls-CI: kokoro <[email protected]>
1 parent 459e2b8 commit 8730184

File tree

3 files changed

+57
-3
lines changed

3 files changed

+57
-3
lines changed

internal/lsp/fake/sandbox.go

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,11 @@ import (
99
"errors"
1010
"fmt"
1111
"io/ioutil"
12+
"math/rand"
1213
"os"
1314
"path/filepath"
1415
"strings"
16+
"time"
1517

1618
"golang.org/x/tools/internal/gocommand"
1719
"golang.org/x/tools/internal/testenv"
@@ -266,9 +268,36 @@ func (sb *Sandbox) Close() error {
266268
if sb.gopath != "" {
267269
goCleanErr = sb.RunGoCommand(context.Background(), "", "clean", []string{"-modcache"}, false)
268270
}
269-
err := os.RemoveAll(sb.rootdir)
271+
err := removeAll(sb.rootdir)
270272
if err != nil || goCleanErr != nil {
271273
return fmt.Errorf("error(s) cleaning sandbox: cleaning modcache: %v; removing files: %v", goCleanErr, err)
272274
}
273275
return nil
274276
}
277+
278+
// removeAll is copied from GOROOT/src/testing/testing.go
279+
//
280+
// removeAll is like os.RemoveAll, but retries Windows "Access is denied."
281+
// errors up to an arbitrary timeout.
282+
//
283+
// See https://go.dev/issue/50051 for additional context.
284+
func removeAll(path string) error {
285+
const arbitraryTimeout = 2 * time.Second
286+
var (
287+
start time.Time
288+
nextSleep = 1 * time.Millisecond
289+
)
290+
for {
291+
err := os.RemoveAll(path)
292+
if !isWindowsRetryable(err) {
293+
return err
294+
}
295+
if start.IsZero() {
296+
start = time.Now()
297+
} else if d := time.Since(start) + nextSleep; d >= arbitraryTimeout {
298+
return err
299+
}
300+
time.Sleep(nextSleep)
301+
nextSleep += time.Duration(rand.Int63n(int64(nextSleep)))
302+
}
303+
}

internal/lsp/fake/workdir.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,10 @@ func WriteFileData(path string, content []byte, rel RelativeTo) error {
7777
// on Windows.
7878
var isWindowsErrLockViolation = func(err error) bool { return false }
7979

80+
// isWindowsRetryable reports whether err is a Windows error code
81+
// that may be fixed by retrying a failed filesystem operation.
82+
var isWindowsRetryable = func(err error) bool { return false }
83+
8084
// Workdir is a temporary working directory for tests. It exposes file
8185
// operations in terms of relative paths, and fakes file watching by triggering
8286
// events on file operations.

internal/lsp/fake/workdir_windows.go

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,31 @@ import (
1010
)
1111

1212
func init() {
13-
// from https://docs.microsoft.com/en-us/windows/win32/debug/system-error-codes--0-499-
14-
const ERROR_LOCK_VIOLATION syscall.Errno = 33
13+
// constants copied from GOROOT/src/internal/syscall/windows/syscall_windows.go
14+
const (
15+
ERROR_SHARING_VIOLATION syscall.Errno = 32
16+
ERROR_LOCK_VIOLATION syscall.Errno = 33
17+
)
1518

1619
isWindowsErrLockViolation = func(err error) bool {
1720
return errors.Is(err, ERROR_LOCK_VIOLATION)
1821
}
22+
23+
// Copied from GOROOT/src/testing/testing_windows.go
24+
isWindowsRetryable = func(err error) bool {
25+
for {
26+
unwrapped := errors.Unwrap(err)
27+
if unwrapped == nil {
28+
break
29+
}
30+
err = unwrapped
31+
}
32+
if err == syscall.ERROR_ACCESS_DENIED {
33+
return true // Observed in https://go.dev/issue/50051.
34+
}
35+
if err == ERROR_SHARING_VIOLATION {
36+
return true // Observed in https://go.dev/issue/51442.
37+
}
38+
return false
39+
}
1940
}

0 commit comments

Comments
 (0)