Ian Lance Taylor submitted this change.
os: add handling of os.Interrupt for windows
Add GenerateConsoleCtrlEvent call to internal syscall package.
Define ErrProcessDone while reviewing handling of os.Signal().
Update test to run for windows using the added call.
Fixes #42311
Fixes #46354
Change-Id: I460955efc76c4febe04b612ac9a0670e62ba5ff3
Reviewed-on: https://go-review.googlesource.com/c/go/+/367495
Trust: Patrik Nyblom <pn...@google.com>
Run-TryBot: Ian Lance Taylor <ia...@golang.org>
TryBot-Result: Gopher Robot <go...@golang.org>
Reviewed-by: Ian Lance Taylor <ia...@golang.org>
---
M src/internal/syscall/windows/syscall_windows.go
M src/internal/syscall/windows/zsyscall_windows.go
M src/os/exec/exec_windows_test.go
M src/os/exec_posix.go
M src/os/exec_windows.go
M src/os/signal/signal_windows_test.go
M src/runtime/signal_windows_test.go
7 files changed, 66 insertions(+), 43 deletions(-)
diff --git a/src/internal/syscall/windows/syscall_windows.go b/src/internal/syscall/windows/syscall_windows.go
index f8965d0..07d0cc7 100644
--- a/src/internal/syscall/windows/syscall_windows.go
+++ b/src/internal/syscall/windows/syscall_windows.go
@@ -344,3 +344,4 @@
//sys DestroyEnvironmentBlock(block *uint16) (err error) = userenv.DestroyEnvironmentBlock
//sys RtlGenRandom(buf []byte) (err error) = advapi32.SystemFunction036
+//sys GenerateConsoleCtrlEvent(ctrlEvent uint32, processGroupID uint32) (err error) = kernel32.GenerateConsoleCtrlEvent
diff --git a/src/internal/syscall/windows/zsyscall_windows.go b/src/internal/syscall/windows/zsyscall_windows.go
index aaad4a5..4de662c 100644
--- a/src/internal/syscall/windows/zsyscall_windows.go
+++ b/src/internal/syscall/windows/zsyscall_windows.go
@@ -54,6 +54,7 @@
procSetTokenInformation = modadvapi32.NewProc("SetTokenInformation")
procSystemFunction036 = modadvapi32.NewProc("SystemFunction036")
procGetAdaptersAddresses = modiphlpapi.NewProc("GetAdaptersAddresses")
+ procGenerateConsoleCtrlEvent = modkernel32.NewProc("GenerateConsoleCtrlEvent")
procGetACP = modkernel32.NewProc("GetACP")
procGetComputerNameExW = modkernel32.NewProc("GetComputerNameExW")
procGetConsoleCP = modkernel32.NewProc("GetConsoleCP")
@@ -161,6 +162,14 @@
return
}
+func GenerateConsoleCtrlEvent(ctrlEvent uint32, processGroupID uint32) (err error) {
+ r1, _, e1 := syscall.Syscall(procGenerateConsoleCtrlEvent.Addr(), 2, uintptr(ctrlEvent), uintptr(processGroupID), 0)
+ if r1 == 0 {
+ err = errnoErr(e1)
+ }
+ return
+}
+
func GetACP() (acp uint32) {
r0, _, _ := syscall.Syscall(procGetACP.Addr(), 0, 0, 0, 0)
acp = uint32(r0)
diff --git a/src/os/exec/exec_windows_test.go b/src/os/exec/exec_windows_test.go
index 8e31e47..503867f 100644
--- a/src/os/exec/exec_windows_test.go
+++ b/src/os/exec/exec_windows_test.go
@@ -7,6 +7,7 @@
package exec_test
import (
+ "internal/testenv"
"io"
"os"
"os/exec"
@@ -54,3 +55,20 @@
t.Fatalf("got exit code %d; want 88", exitError.ExitCode())
}
}
+
+func TestErrProcessDone(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+ // On Windows, ProcAttr cannot be empty
+ p, err := os.StartProcess(testenv.GoToolPath(t), []string{""},
+ &os.ProcAttr{Dir: "", Env: nil, Files: []*os.File{os.Stdin, os.Stdout, os.Stderr}, Sys: nil})
+ if err != nil {
+ t.Errorf("starting test process: %v", err)
+ }
+ _, err = p.Wait()
+ if err != nil {
+ t.Errorf("Wait: %v", err)
+ }
+ if got := p.Signal(os.Kill); got != os.ErrProcessDone {
+ t.Fatalf("got %v want %v", got, os.ErrProcessDone)
+ }
+}
diff --git a/src/os/exec_posix.go b/src/os/exec_posix.go
index e1e7d53..3dc18a8 100644
--- a/src/os/exec_posix.go
+++ b/src/os/exec_posix.go
@@ -15,9 +15,7 @@
// The only signal values guaranteed to be present in the os package on all
// systems are os.Interrupt (send the process an interrupt) and os.Kill (force
-// the process to exit). On Windows, sending os.Interrupt to a process with
-// os.Process.Signal is not implemented; it will return an error instead of
-// sending a signal.
+// the process to exit).
var (
Interrupt Signal = syscall.SIGINT
Kill Signal = syscall.SIGKILL
diff --git a/src/os/exec_windows.go b/src/os/exec_windows.go
index 239bed1..bc232e0 100644
--- a/src/os/exec_windows.go
+++ b/src/os/exec_windows.go
@@ -47,13 +47,14 @@
func (p *Process) signal(sig Signal) error {
handle := atomic.LoadUintptr(&p.handle)
- if handle == uintptr(syscall.InvalidHandle) {
- return syscall.EINVAL
- }
if p.done() {
return ErrProcessDone
}
- if sig == Kill {
+ s, ok := sig.(syscall.Signal)
+ if !ok {
+ return syscall.EWINDOWS
+ }
+ if s == syscall.SIGKILL {
var terminationHandle syscall.Handle
e := syscall.DuplicateHandle(^syscall.Handle(0), syscall.Handle(handle), ^syscall.Handle(0), &terminationHandle, syscall.PROCESS_TERMINATE, false, 0)
if e != nil {
@@ -61,11 +62,17 @@
}
runtime.KeepAlive(p)
defer syscall.CloseHandle(terminationHandle)
- e = syscall.TerminateProcess(syscall.Handle(terminationHandle), 1)
+ e = syscall.TerminateProcess(terminationHandle, 1)
return NewSyscallError("TerminateProcess", e)
}
- // TODO(rsc): Handle Interrupt too?
- return syscall.Errno(syscall.EWINDOWS)
+ if s == syscall.SIGINT {
+ e := windows.GenerateConsoleCtrlEvent(syscall.CTRL_BREAK_EVENT, uint32(p.Pid))
+ if e != nil {
+ return NewSyscallError("GenerateConsoleCtrlEvent", e)
+ }
+ return nil
+ }
+ return syscall.EWINDOWS
}
func (p *Process) release() error {
diff --git a/src/os/signal/signal_windows_test.go b/src/os/signal/signal_windows_test.go
index 9b14551..89c072c 100644
--- a/src/os/signal/signal_windows_test.go
+++ b/src/os/signal/signal_windows_test.go
@@ -15,21 +15,6 @@
"time"
)
-func sendCtrlBreak(t *testing.T, pid int) {
- d, e := syscall.LoadDLL("kernel32.dll")
- if e != nil {
- t.Fatalf("LoadDLL: %v\n", e)
- }
- p, e := d.FindProc("GenerateConsoleCtrlEvent")
- if e != nil {
- t.Fatalf("FindProc: %v\n", e)
- }
- r, _, e := p.Call(syscall.CTRL_BREAK_EVENT, uintptr(pid))
- if r == 0 {
- t.Fatalf("GenerateConsoleCtrlEvent: %v\n", e)
- }
-}
-
func TestCtrlBreak(t *testing.T) {
// create source file
const source = `
@@ -90,7 +75,7 @@
}
go func() {
time.Sleep(1 * time.Second)
- sendCtrlBreak(t, cmd.Process.Pid)
+ cmd.Process.Signal(os.Interrupt)
}()
err = cmd.Wait()
if err != nil {
diff --git a/src/runtime/signal_windows_test.go b/src/runtime/signal_windows_test.go
index add23cd..1f329f4 100644
--- a/src/runtime/signal_windows_test.go
+++ b/src/runtime/signal_windows_test.go
@@ -59,22 +59,6 @@
}
}
-func sendCtrlBreak(pid int) error {
- kernel32, err := syscall.LoadDLL("kernel32.dll")
- if err != nil {
- return fmt.Errorf("LoadDLL: %v\n", err)
- }
- generateEvent, err := kernel32.FindProc("GenerateConsoleCtrlEvent")
- if err != nil {
- return fmt.Errorf("FindProc: %v\n", err)
- }
- result, _, err := generateEvent.Call(syscall.CTRL_BREAK_EVENT, uintptr(pid))
- if result == 0 {
- return fmt.Errorf("GenerateConsoleCtrlEvent: %v\n", err)
- }
- return nil
-}
-
// TestCtrlHandler tests that Go can gracefully handle closing the console window.
// See https://golang.org/issues/41884.
func TestCtrlHandler(t *testing.T) {
@@ -183,7 +167,7 @@
} else if strings.TrimSpace(line) != "ready" {
errCh <- fmt.Errorf("unexpected message: %v", line)
} else {
- errCh <- sendCtrlBreak(cmd.Process.Pid)
+ errCh <- cmd.Process.Signal(syscall.SIGINT)
}
}()
To view, visit change 367495. To unsubscribe, or for help writing mail filters, visit settings.