os: implement new Process api

Fixes #1004.
Fixes #1460.

R=mattn, r, niemeyer, rog, rsc
CC=golang-dev
https://golang.org/cl/4029053
diff --git a/src/pkg/os/Makefile b/src/pkg/os/Makefile
index f6caf08..3a81afe 100644
--- a/src/pkg/os/Makefile
+++ b/src/pkg/os/Makefile
@@ -22,21 +22,25 @@
 	env_unix.go\
 	file_unix.go\
 	sys_bsd.go\
+	exec_unix.go\
 
 GOFILES_darwin=\
 	env_unix.go\
 	file_unix.go\
 	sys_bsd.go\
+	exec_unix.go\
 
 GOFILES_linux=\
 	env_unix.go\
 	file_unix.go\
 	sys_linux.go\
+	exec_unix.go\
 
 GOFILES_windows=\
 	env_windows.go\
 	file_windows.go\
 	sys_windows.go\
+	exec_windows.go\
 
 GOFILES+=$(GOFILES_$(GOOS))
 
diff --git a/src/pkg/os/exec.go b/src/pkg/os/exec.go
index 100d984..dbdfacc 100644
--- a/src/pkg/os/exec.go
+++ b/src/pkg/os/exec.go
@@ -5,17 +5,29 @@
 package os
 
 import (
+	"runtime"
 	"syscall"
 )
 
-// ForkExec forks the current process and invokes Exec with the program, arguments,
-// and environment specified by name, argv, and envv.  It returns the process
-// id of the forked process and an Error, if any.  The fd array specifies the
+// Process stores the information about a process created by StartProcess.
+type Process struct {
+	Pid    int
+	handle int
+}
+
+func newProcess(pid, handle int) *Process {
+	p := &Process{pid, handle}
+	runtime.SetFinalizer(p, (*Process).Release)
+	return p
+}
+
+// StartProcess starts a new process with the program, arguments,
+// and environment specified by name, argv, and envv. The fd array specifies the
 // file descriptors to be set up in the new process: fd[0] will be Unix file
 // descriptor 0 (standard input), fd[1] descriptor 1, and so on.  A nil entry
 // will cause the child to have no open file descriptor with that index.
 // If dir is not empty, the child chdirs into the directory before execing the program.
-func ForkExec(name string, argv []string, envv []string, dir string, fd []*File) (pid int, err Error) {
+func StartProcess(name string, argv []string, envv []string, dir string, fd []*File) (p *Process, err Error) {
 	if envv == nil {
 		envv = Environ()
 	}
@@ -29,17 +41,17 @@
 		}
 	}
 
-	p, e := syscall.ForkExec(name, argv, envv, dir, intfd)
+	pid, h, e := syscall.StartProcess(name, argv, envv, dir, intfd)
 	if e != 0 {
-		return 0, &PathError{"fork/exec", name, Errno(e)}
+		return nil, &PathError{"fork/exec", name, Errno(e)}
 	}
-	return p, nil
+	return newProcess(pid, h), nil
 }
 
 // Exec replaces the current process with an execution of the
 // named binary, with arguments argv and environment envv.
 // If successful, Exec never returns.  If it fails, it returns an Error.
-// ForkExec is almost always a better way to execute a program.
+// StartProcess is almost always a better way to execute a program.
 func Exec(name string, argv []string, envv []string) Error {
 	if envv == nil {
 		envv = Environ()
@@ -65,37 +77,18 @@
 	Rusage             *syscall.Rusage // System-dependent resource usage info.
 }
 
-// Options for Wait.
-const (
-	WNOHANG   = syscall.WNOHANG   // Don't wait if no process has exited.
-	WSTOPPED  = syscall.WSTOPPED  // If set, status of stopped subprocesses is also reported.
-	WUNTRACED = syscall.WUNTRACED // Usually an alias for WSTOPPED.
-	WRUSAGE   = 1 << 20           // Record resource usage.
-)
-
-// WRUSAGE must not be too high a bit, to avoid clashing with Linux's
-// WCLONE, WALL, and WNOTHREAD flags, which sit in the top few bits of
-// the options
-
 // Wait waits for process pid to exit or stop, and then returns a
 // Waitmsg describing its status and an Error, if any. The options
 // (WNOHANG etc.) affect the behavior of the Wait call.
+// Wait is equivalent to calling FindProcess and then Wait
+// and Release on the result.
 func Wait(pid int, options int) (w *Waitmsg, err Error) {
-	var status syscall.WaitStatus
-	var rusage *syscall.Rusage
-	if options&WRUSAGE != 0 {
-		rusage = new(syscall.Rusage)
-		options ^= WRUSAGE
+	p, e := FindProcess(pid)
+	if e != nil {
+		return nil, e
 	}
-	pid1, e := syscall.Wait4(pid, &status, options, rusage)
-	if e != 0 {
-		return nil, NewSyscallError("wait", e)
-	}
-	w = new(Waitmsg)
-	w.Pid = pid1
-	w.WaitStatus = status
-	w.Rusage = rusage
-	return w, nil
+	defer p.Release()
+	return p.Wait(options)
 }
 
 // Convert i to decimal string.
diff --git a/src/pkg/os/exec_unix.go b/src/pkg/os/exec_unix.go
new file mode 100644
index 0000000..8990d6a
--- /dev/null
+++ b/src/pkg/os/exec_unix.go
@@ -0,0 +1,63 @@
+// Copyright 2009 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 os
+
+import (
+	"runtime"
+	"syscall"
+)
+
+// Options for Wait.
+const (
+	WNOHANG   = syscall.WNOHANG   // Don't wait if no process has exited.
+	WSTOPPED  = syscall.WSTOPPED  // If set, status of stopped subprocesses is also reported.
+	WUNTRACED = syscall.WUNTRACED // Usually an alias for WSTOPPED.
+	WRUSAGE   = 1 << 20           // Record resource usage.
+)
+
+// WRUSAGE must not be too high a bit, to avoid clashing with Linux's
+// WCLONE, WALL, and WNOTHREAD flags, which sit in the top few bits of
+// the options
+
+// Wait waits for the Process to exit or stop, and then returns a
+// Waitmsg describing its status and an Error, if any. The options
+// (WNOHANG etc.) affect the behavior of the Wait call.
+func (p *Process) Wait(options int) (w *Waitmsg, err Error) {
+	if p.Pid == -1 {
+		return nil, EINVAL
+	}
+	var status syscall.WaitStatus
+	var rusage *syscall.Rusage
+	if options&WRUSAGE != 0 {
+		rusage = new(syscall.Rusage)
+		options ^= WRUSAGE
+	}
+	pid1, e := syscall.Wait4(p.Pid, &status, options, rusage)
+	if e != 0 {
+		return nil, NewSyscallError("wait", e)
+	}
+	w = new(Waitmsg)
+	w.Pid = pid1
+	w.WaitStatus = status
+	w.Rusage = rusage
+	return w, nil
+}
+
+// Release releases any resources associated with the Process.
+func (p *Process) Release() Error {
+	// NOOP for unix.
+	p.Pid = -1
+	// no need for a finalizer anymore
+	runtime.SetFinalizer(p, nil)
+	return nil
+}
+
+// FindProcess looks for a running process by its pid.
+// The Process it returns can be used to obtain information
+// about the underlying operating system process.
+func FindProcess(pid int) (p *Process, err Error) {
+	// NOOP for unix.
+	return newProcess(pid, 0), nil
+}
diff --git a/src/pkg/os/exec_windows.go b/src/pkg/os/exec_windows.go
new file mode 100644
index 0000000..73c0104
--- /dev/null
+++ b/src/pkg/os/exec_windows.go
@@ -0,0 +1,50 @@
+// Copyright 2009 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 os
+
+import (
+	"runtime"
+	"syscall"
+)
+
+func (p *Process) Wait(options int) (w *Waitmsg, err Error) {
+	s, e := syscall.WaitForSingleObject(int32(p.handle), syscall.INFINITE)
+	switch s {
+	case syscall.WAIT_OBJECT_0:
+		break
+	case syscall.WAIT_FAILED:
+		return nil, NewSyscallError("WaitForSingleObject", e)
+	default:
+		return nil, ErrorString("os: unexpected result from WaitForSingleObject")
+	}
+	var ec uint32
+	if ok, e := syscall.GetExitCodeProcess(uint32(p.handle), &ec); !ok {
+		return nil, NewSyscallError("GetExitCodeProcess", e)
+	}
+	return &Waitmsg{p.Pid, syscall.WaitStatus{s, ec}, new(syscall.Rusage)}, nil
+}
+
+func (p *Process) Release() Error {
+	if p.handle == -1 {
+		return EINVAL
+	}
+	if ok, e := syscall.CloseHandle(int32(p.handle)); !ok {
+		return NewSyscallError("CloseHandle", e)
+	}
+	p.handle = -1
+	// no need for a finalizer anymore
+	runtime.SetFinalizer(p, nil)
+	return nil
+}
+
+func FindProcess(pid int) (p *Process, err Error) {
+	const da = syscall.STANDARD_RIGHTS_READ |
+		syscall.PROCESS_QUERY_INFORMATION | syscall.SYNCHRONIZE
+	h, e := syscall.OpenProcess(da, false, uint32(pid))
+	if e != 0 {
+		return nil, NewSyscallError("OpenProcess", e)
+	}
+	return newProcess(pid, int(h)), nil
+}
diff --git a/src/pkg/os/os_test.go b/src/pkg/os/os_test.go
index 49b58c8..2ea8acd 100644
--- a/src/pkg/os/os_test.go
+++ b/src/pkg/os/os_test.go
@@ -427,10 +427,11 @@
 		adir = "/"
 		expect = "/\n"
 	}
-	pid, err := ForkExec(cmd, args, nil, adir, []*File{nil, w, Stderr})
+	p, err := StartProcess(cmd, args, nil, adir, []*File{nil, w, Stderr})
 	if err != nil {
-		t.Fatalf("ForkExec: %v", err)
+		t.Fatalf("StartProcess: %v", err)
 	}
+	defer p.Release()
 	w.Close()
 
 	var b bytes.Buffer
@@ -440,7 +441,7 @@
 		args[0] = cmd
 		t.Errorf("exec %q returned %q wanted %q", strings.Join(args, " "), output, expect)
 	}
-	Wait(pid, 0)
+	p.Wait(0)
 }
 
 func checkMode(t *testing.T, path string, mode uint32) {
@@ -750,15 +751,16 @@
 	if err != nil {
 		t.Fatal(err)
 	}
-	pid, err := ForkExec("/bin/hostname", []string{"hostname"}, nil, "/", []*File{nil, w, Stderr})
+	p, err := StartProcess("/bin/hostname", []string{"hostname"}, nil, "/", []*File{nil, w, Stderr})
 	if err != nil {
 		t.Fatal(err)
 	}
+	defer p.Release()
 	w.Close()
 
 	var b bytes.Buffer
 	io.Copy(&b, r)
-	Wait(pid, 0)
+	p.Wait(0)
 	output := b.String()
 	if n := len(output); n > 0 && output[n-1] == '\n' {
 		output = output[0 : n-1]