mirror of https://github.com/golang/go.git
internal/lsp/fake: in WriteFileData, retry writes that fail with ERROR_LOCK_VIOLATION on Windows
The 'go' command reads the main module's 'go.mod' file with the file locked. If 'gopls' starts a 'go' command in the background that reads the 'go.mod' file, there is a narrow window in which that read can race with the test's ioutil.WriteFile. This change retries the write (as a user would probably do), fixing the test failure. However, I suspect that there may be a broader UX issue here that it will mask: in order to actually avoid the race without a locking error, something in the LSP protocol needs to be synchronizing gopls-initiated reads with editor-initiated writes. The test failure this fixes indicates that that synchronization seems not to be happening. Fixes golang/go#50971 Change-Id: Iba140277bf957bae4937cc23f62d3aac817a7d36 Reviewed-on: https://go-review.googlesource.com/c/tools/+/382414 Trust: Bryan Mills <bcmills@google.com> Run-TryBot: Bryan Mills <bcmills@google.com> TryBot-Result: Gopher Robot <gobot@golang.org> Reviewed-by: Robert Findley <rfindley@google.com>
This commit is contained in:
parent
f6067dc943
commit
82366c6960
|
|
@ -14,6 +14,7 @@ import (
|
|||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"golang.org/x/tools/internal/lsp/protocol"
|
||||
"golang.org/x/tools/internal/span"
|
||||
|
|
@ -67,12 +68,25 @@ func WriteFileData(path string, content []byte, rel RelativeTo) error {
|
|||
if err := os.MkdirAll(filepath.Dir(fp), 0755); err != nil {
|
||||
return errors.Errorf("creating nested directory: %w", err)
|
||||
}
|
||||
if err := ioutil.WriteFile(fp, []byte(content), 0644); err != nil {
|
||||
return errors.Errorf("writing %q: %w", path, err)
|
||||
backoff := 1 * time.Millisecond
|
||||
for {
|
||||
err := ioutil.WriteFile(fp, []byte(content), 0644)
|
||||
if err != nil {
|
||||
if isWindowsErrLockViolation(err) {
|
||||
time.Sleep(backoff)
|
||||
backoff *= 2
|
||||
continue
|
||||
}
|
||||
return errors.Errorf("writing %q: %w", path, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// isWindowsErrLockViolation reports whether err is ERROR_LOCK_VIOLATION
|
||||
// on Windows.
|
||||
var isWindowsErrLockViolation = func(err error) bool { return false }
|
||||
|
||||
// Workdir is a temporary working directory for tests. It exposes file
|
||||
// operations in terms of relative paths, and fakes file watching by triggering
|
||||
// events on file operations.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,20 @@
|
|||
// Copyright 2021 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package fake
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
|
||||
errors "golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
func init() {
|
||||
// from https://docs.microsoft.com/en-us/windows/win32/debug/system-error-codes--0-499-
|
||||
const ERROR_LOCK_VIOLATION syscall.Errno = 33
|
||||
|
||||
isWindowsErrLockViolation = func(err error) bool {
|
||||
return errors.Is(err, ERROR_LOCK_VIOLATION)
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue