1
0
mirror of https://github.com/golang/go synced 2024-11-26 23:01:23 -07:00

os: remove 5ms sleep on Windows in (*Process).Wait

The 5ms sleep in (*Process).Wait was added to mitigate errors while
removing executable files using os.RemoveAll.

Windows 10 1903 implements POSIX semantics for DeleteFile, making the
implementation of os.RemoveAll on Windows much more robust. Older
Windows 10 versions also made internal improvements to avoid errors
when removing files, making it less likely that the 5ms sleep is
necessary.

Windows 10 is the oldest version that Go supports (see #57004), so it
makes sense to unconditionally remove the 5ms sleep now. We have all
the Go 1.22 development cycle to see if this causes any regression.

Fixes #25965

Change-Id: Ie0bbe6dc3e8389fd51a32484d5d20cf59b019451
Reviewed-on: https://go-review.googlesource.com/c/go/+/509335
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
Reviewed-by: Alex Brainman <alex.brainman@gmail.com>
Reviewed-by: Bryan Mills <bcmills@google.com>
Run-TryBot: Quim Muntal <quimmuntal@gmail.com>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
This commit is contained in:
qmuntal 2023-07-13 16:40:49 +02:00 committed by Quim Muntal
parent 4918490962
commit f0894a00f4
2 changed files with 83 additions and 6 deletions

View File

@ -35,12 +35,6 @@ func (p *Process) wait() (ps *ProcessState, err error) {
return nil, NewSyscallError("GetProcessTimes", e)
}
p.setDone()
// NOTE(brainman): It seems that sometimes process is not dead
// when WaitForSingleObject returns. But we do not know any
// other way to wait for it. Sleeping for a while seems to do
// the trick sometimes.
// See https://golang.org/issue/25965 for details.
defer time.Sleep(5 * time.Millisecond)
defer p.Release()
return &ProcessState{p.Pid, syscall.WaitStatus{ExitCode: ec}, &u}, nil
}

View File

@ -0,0 +1,83 @@
// Copyright 2023 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.
//go:build windows
package os_test
import (
"internal/testenv"
"io"
. "os"
"path/filepath"
"sync"
"testing"
)
func TestRemoveAllWithExecutedProcess(t *testing.T) {
// Regression test for golang.org/issue/25965.
if testing.Short() {
t.Skip("slow test; skipping")
}
testenv.MustHaveExec(t)
name, err := Executable()
if err != nil {
t.Fatal(err)
}
r, err := Open(name)
if err != nil {
t.Fatal(err)
}
defer r.Close()
const n = 100
var execs [n]string
// First create n executables.
for i := 0; i < n; i++ {
// Rewind r.
if _, err := r.Seek(0, io.SeekStart); err != nil {
t.Fatal(err)
}
name := filepath.Join(t.TempDir(), "test.exe")
execs[i] = name
w, err := Create(name)
if err != nil {
t.Fatal(err)
}
if _, err = io.Copy(w, r); err != nil {
w.Close()
t.Fatal(err)
}
if err := w.Sync(); err != nil {
w.Close()
t.Fatal(err)
}
if err = w.Close(); err != nil {
t.Fatal(err)
}
}
// Then run each executable and remove its directory.
// Run each executable in a separate goroutine to add some load
// and increase the chance of triggering the bug.
var wg sync.WaitGroup
wg.Add(n)
for i := 0; i < n; i++ {
go func(i int) {
defer wg.Done()
name := execs[i]
dir := filepath.Dir(name)
// Run test.exe without executing any test, just to make it do something.
cmd := testenv.Command(t, name, "-test.run=^$")
if err := cmd.Run(); err != nil {
t.Errorf("exec failed: %v", err)
}
// Remove dir and check that it doesn't return `ERROR_ACCESS_DENIED`.
err = RemoveAll(dir)
if err != nil {
t.Errorf("RemoveAll failed: %v", err)
}
}(i)
}
wg.Wait()
}