]> Cypherpunks repositories - gostls13.git/commitdiff
os: replace non-portable Waitmsg with portable ProcessState
authorRob Pike <r@golang.org>
Tue, 21 Feb 2012 03:10:34 +0000 (14:10 +1100)
committerRob Pike <r@golang.org>
Tue, 21 Feb 2012 03:10:34 +0000 (14:10 +1100)
Use methods for key questions.
Provide access to non-portable pieces through portable methods.
Windows and Plan 9 updated.

R=golang-dev, bradfitz, bradfitz, r, dsymonds, rsc, iant, iant
CC=golang-dev
https://golang.org/cl/5673077

src/cmd/cgo/util.go
src/cmd/godoc/main.go
src/pkg/os/exec/exec.go
src/pkg/os/exec_plan9.go
src/pkg/os/exec_posix.go
src/pkg/os/exec_unix.go
src/pkg/os/exec_windows.go
src/pkg/os/os_test.go

index 155c65904f2d0fb0e1d28f81758d7b696d4f20d9..cd7cde2b6ef8a9085e574b3319147ab81f4047ce 100644 (file)
@@ -56,11 +56,11 @@ func run(stdin []byte, argv []string) (stdout, stderr []byte, ok bool) {
        <-c
        <-c
 
-       w, err := p.Wait()
+       state, err := p.Wait()
        if err != nil {
                fatalf("%s", err)
        }
-       ok = w.Exited() && w.ExitStatus() == 0
+       ok = state.Success()
        return
 }
 
index 80cf61877827391be5ca00d830d2028691db2f77..8d592200e9b010a5108ea4204b1e4be41ff757fc 100644 (file)
@@ -109,10 +109,10 @@ func exec(rw http.ResponseWriter, args []string) (status int) {
                log.Printf("os.Wait(%d, 0): %v", p.Pid, err)
                return 2
        }
-       status = wait.ExitStatus()
-       if !wait.Exited() || status > 1 {
+       if !wait.Success() {
                os.Stderr.Write(buf.Bytes())
-               log.Printf("executing %v failed (exit status = %d)", args, status)
+               log.Printf("executing %v failed", args)
+               status = 1 // See comment in default case in dosync.
                return
        }
 
@@ -143,6 +143,8 @@ func dosync(w http.ResponseWriter, r *http.Request) {
                // don't change the package tree
                syncDelay.set(time.Duration(*syncMin) * time.Minute) //  revert to regular sync schedule
        default:
+               // TODO(r): this cannot happen now, since Wait has a boolean exit condition,
+               // not an integer.
                // sync failed because of an error - back off exponentially, but try at least once a day
                syncDelay.backoff(24 * time.Hour)
        }
index 248d97d458890ffd27b9415d7e3b3f01fab9e0aa..ebe92a9fba39344c24c2c4350bc5870da6ed9413 100644 (file)
@@ -79,9 +79,9 @@ type Cmd struct {
        // Process is the underlying process, once started.
        Process *os.Process
 
-       // Waitmsg contains information about an exited process,
+       // ProcessState contains information about an exited process,
        // available after a call to Wait or Run.
-       Waitmsg *os.Waitmsg
+       ProcessState *os.ProcessState
 
        err             error // last error (from LookPath, stdin, stdout, stderr)
        finished        bool  // when Wait was called
@@ -266,11 +266,11 @@ func (c *Cmd) Start() error {
 
 // An ExitError reports an unsuccessful exit by a command.
 type ExitError struct {
-       *os.Waitmsg
+       *os.ProcessState
 }
 
 func (e *ExitError) Error() string {
-       return e.Waitmsg.String()
+       return e.ProcessState.String()
 }
 
 // Wait waits for the command to exit.
@@ -291,8 +291,8 @@ func (c *Cmd) Wait() error {
                return errors.New("exec: Wait was already called")
        }
        c.finished = true
-       msg, err := c.Process.Wait()
-       c.Waitmsg = msg
+       state, err := c.Process.Wait()
+       c.ProcessState = state
 
        var copyError error
        for _ = range c.goroutine {
@@ -307,8 +307,8 @@ func (c *Cmd) Wait() error {
 
        if err != nil {
                return err
-       } else if !msg.Exited() || msg.ExitStatus() != 0 {
-               return &ExitError{msg}
+       } else if !state.Success() {
+               return &ExitError{state}
        }
 
        return copyError
index 92126c1dd82f2b6cfbb00fc674860ef611ca08de..9da86e26397c1d36be215e49b39c8422ea440574 100644 (file)
@@ -8,6 +8,7 @@ import (
        "errors"
        "runtime"
        "syscall"
+       "time"
 )
 
 // StartProcess starts a new process with the program, arguments and attributes
@@ -64,14 +65,9 @@ func (p *Process) Kill() error {
        return e
 }
 
-// Waitmsg stores the information about an exited process as reported by Wait.
-type Waitmsg struct {
-       syscall.Waitmsg
-}
-
 // Wait waits for the Process to exit or stop, and then returns a
 // Waitmsg describing its status and an error, if any.
-func (p *Process) Wait() (w *Waitmsg, err error) {
+func (p *Process) Wait() (ps *ProcessState, err error) {
        var waitmsg syscall.Waitmsg
 
        if p.Pid == -1 {
@@ -91,7 +87,11 @@ func (p *Process) Wait() (w *Waitmsg, err error) {
                }
        }
 
-       return &Waitmsg{waitmsg}, nil
+       ps = &ProcessState{
+               pid:    waitmsg.Pid,
+               status: waitmsg,
+       }
+       return ps, nil
 }
 
 // Release releases any resources associated with the Process.
@@ -108,9 +108,57 @@ func findProcess(pid int) (p *Process, err error) {
        return newProcess(pid, 0), nil
 }
 
-func (w *Waitmsg) String() string {
-       if w == nil {
+// ProcessState stores information about process as reported by Wait.
+type ProcessState struct {
+       pid    int             // The process's id.
+       status syscall.Waitmsg // System-dependent status info.
+}
+
+// Pid returns the process id of the exited process.
+func (p *ProcessState) Pid() int {
+       return p.pid
+}
+
+// Exited returns whether the program has exited.
+func (p *ProcessState) Exited() bool {
+       return p.status.Exited()
+}
+
+// Success reports whether the program exited successfully,
+// such as with exit status 0 on Unix.
+func (p *ProcessState) Success() bool {
+       return p.status.ExitStatus() == 0
+}
+
+// Sys returns system-dependent exit information about
+// the process.  Convert it to the appropriate underlying
+// type, such as *syscall.Waitmsg on Plan 9, to access its contents.
+func (p *ProcessState) Sys() interface{} {
+       return &p.status
+}
+
+// SysUsage returns system-dependent resource usage information about
+// the exited process.  Convert it to the appropriate underlying
+// type, such as *syscall.Waitmsg on Unix, to access its contents.
+func (p *ProcessState) SysUsage() interface{} {
+       return &p.status
+}
+
+// UserTime returns the user CPU time of the exited process and its children.
+// It is always reported as 0 on Windows.
+func (p *ProcessState) UserTime() time.Duration {
+       return time.Duration(p.status.Time[0]) * time.Millisecond
+}
+
+// SystemTime returns the system CPU time of the exited process and its children.
+// It is always reported as 0 on Windows.
+func (p *ProcessState) SystemTime() time.Duration {
+       return time.Duration(p.status.Time[1]) * time.Millisecond
+}
+
+func (p *ProcessState) String() string {
+       if p == nil {
                return "<nil>"
        }
-       return "exit status: " + w.Msg
+       return "exit status: " + p.status.Msg
 }
index 03c7f0e82ff26039ac5340097f5d9f23e3b5723f..2b8d2b2b2b207eb711f077c7b1f2828342330241 100644 (file)
@@ -42,18 +42,41 @@ func (p *Process) Kill() error {
        return p.Signal(Kill)
 }
 
-// TODO(rsc): Should os implement its own syscall.WaitStatus
-// wrapper with the methods, or is exposing the underlying one enough?
-//
-// TODO(rsc): Certainly need to have Rusage struct,
-// since syscall one might have different field types across
-// different OS.
-
-// Waitmsg stores the information about an exited process as reported by Wait.
-type Waitmsg struct {
-       Pid                int             // The process's id.
-       syscall.WaitStatus                 // System-dependent status info.
-       Rusage             *syscall.Rusage // System-dependent resource usage info.
+// ProcessState stores information about process as reported by Wait.
+type ProcessState struct {
+       pid    int                 // The process's id.
+       status *syscall.WaitStatus // System-dependent status info.
+       rusage *syscall.Rusage
+}
+
+// Pid returns the process id of the exited process.
+func (p *ProcessState) Pid() int {
+       return p.pid
+}
+
+// Exited returns whether the program has exited.
+func (p *ProcessState) Exited() bool {
+       return p.status.Exited()
+}
+
+// Success reports whether the program exited successfully,
+// such as with exit status 0 on Unix.
+func (p *ProcessState) Success() bool {
+       return p.status.ExitStatus() == 0
+}
+
+// Sys returns system-dependent exit information about
+// the process.  Convert it to the appropriate underlying
+// type, such as *syscall.WaitStatus on Unix, to access its contents.
+func (p *ProcessState) Sys() interface{} {
+       return p.status
+}
+
+// SysUsage returns system-dependent resource usage information about
+// the exited process.  Convert it to the appropriate underlying
+// type, such as *syscall.Rusage on Unix, to access its contents.
+func (p *ProcessState) SysUsage() interface{} {
+       return p.rusage
 }
 
 // Convert i to decimal string.
@@ -83,26 +106,26 @@ func itod(i int) string {
        return string(b[bp:])
 }
 
-func (w *Waitmsg) String() string {
-       if w == nil {
+func (p *ProcessState) String() string {
+       if p == nil {
                return "<nil>"
        }
-       // TODO(austin) Use signal names when possible?
+       status := p.Sys().(*syscall.WaitStatus)
        res := ""
        switch {
-       case w.Exited():
-               res = "exit status " + itod(w.ExitStatus())
-       case w.Signaled():
-               res = "signal " + itod(int(w.Signal()))
-       case w.Stopped():
-               res = "stop signal " + itod(int(w.StopSignal()))
-               if w.StopSignal() == syscall.SIGTRAP && w.TrapCause() != 0 {
-                       res += " (trap " + itod(w.TrapCause()) + ")"
+       case status.Exited():
+               res = "exit status " + itod(status.ExitStatus())
+       case status.Signaled():
+               res = "signal " + itod(int(status.Signal()))
+       case status.Stopped():
+               res = "stop signal " + itod(int(status.StopSignal()))
+               if status.StopSignal() == syscall.SIGTRAP && status.TrapCause() != 0 {
+                       res += " (trap " + itod(status.TrapCause()) + ")"
                }
-       case w.Continued():
+       case status.Continued():
                res = "continued"
        }
-       if w.CoreDump() {
+       if status.CoreDump() {
                res += " (core dumped)"
        }
        return res
index b9880ff796db93b7510018f2b936fa3878835409..e5905f06af4ba8657c336c6dac3946edc9086d07 100644 (file)
@@ -10,26 +10,30 @@ import (
        "errors"
        "runtime"
        "syscall"
+       "time"
 )
 
 // Wait waits for the Process to exit or stop, and then returns a
-// Waitmsg describing its status and an error, if any.
-func (p *Process) Wait() (w *Waitmsg, err error) {
+// ProcessState describing its status and an error, if any.
+func (p *Process) Wait() (ps *ProcessState, err error) {
        if p.Pid == -1 {
                return nil, syscall.EINVAL
        }
        var status syscall.WaitStatus
-       pid1, e := syscall.Wait4(p.Pid, &status, 0, nil)
+       var rusage syscall.Rusage
+       pid1, e := syscall.Wait4(p.Pid, &status, 0, &rusage)
        if e != nil {
                return nil, NewSyscallError("wait", e)
        }
        if pid1 != 0 {
                p.done = true
        }
-       w = new(Waitmsg)
-       w.Pid = pid1
-       w.WaitStatus = status
-       return w, nil
+       ps = &ProcessState{
+               pid:    pid1,
+               status: &status,
+               rusage: &rusage,
+       }
+       return ps, nil
 }
 
 // Signal sends a signal to the Process.
@@ -60,3 +64,13 @@ func findProcess(pid int) (p *Process, err error) {
        // NOOP for unix.
        return newProcess(pid, 0), nil
 }
+
+// UserTime returns the user CPU time of the exited process and its children.
+func (p *ProcessState) UserTime() time.Duration {
+       return time.Duration(p.rusage.Utime.Nano()) * time.Nanosecond
+}
+
+// SystemTime returns the system CPU time of the exited process and its children.
+func (p *ProcessState) SystemTime() time.Duration {
+       return time.Duration(p.rusage.Stime.Nano()) * time.Nanosecond
+}
index 7d46c89d83c1f7194f2ee6df4177a0afa6c88947..8887ba4ee371335dc60b4f9babee7fb8fcd7d2b2 100644 (file)
@@ -8,12 +8,13 @@ import (
        "errors"
        "runtime"
        "syscall"
+       "time"
        "unsafe"
 )
 
 // Wait waits for the Process to exit or stop, and then returns a
-// Waitmsg describing its status and an error, if any.
-func (p *Process) Wait() (w *Waitmsg, err error) {
+// ProcessState describing its status and an error, if any.
+func (p *Process) Wait() (ps *ProcessState, err error) {
        s, e := syscall.WaitForSingleObject(syscall.Handle(p.handle), syscall.INFINITE)
        switch s {
        case syscall.WAIT_OBJECT_0:
@@ -29,7 +30,7 @@ func (p *Process) Wait() (w *Waitmsg, err error) {
                return nil, NewSyscallError("GetExitCodeProcess", e)
        }
        p.done = true
-       return &Waitmsg{p.Pid, syscall.WaitStatus{Status: s, ExitCode: ec}, new(syscall.Rusage)}, nil
+       return &ProcessState{p.Pid, &syscall.WaitStatus{Status: s, ExitCode: ec}, new(syscall.Rusage)}, nil
 }
 
 // Signal sends a signal to the Process.
@@ -83,3 +84,15 @@ func init() {
                Args[i] = string(syscall.UTF16ToString((*v)[:]))
        }
 }
+
+// UserTime returns the user CPU time of the exited process and its children.
+// For now, it is always reported as 0 on Windows.
+func (p *ProcessState) UserTime() time.Duration {
+       return 0
+}
+
+// SystemTime returns the system CPU time of the exited process and its children.
+// For now, it is always reported as 0 on Windows.
+func (p *ProcessState) SystemTime() time.Duration {
+       return 0
+}
index 976d64bdd6b336ba393bef7480383b9cd0f2047e..21e2f374ca22f2f36db8b255abfec4d917a5b46e 100644 (file)
@@ -1007,10 +1007,10 @@ func TestStatDirWithTrailingSlash(t *testing.T) {
        }
 }
 
-func TestNilWaitmsgString(t *testing.T) {
-       var w *Waitmsg
-       s := w.String()
+func TestNilProcessStateString(t *testing.T) {
+       var ps *ProcessState
+       s := ps.String()
        if s != "<nil>" {
-               t.Errorf("(*Waitmsg)(nil).String() = %q, want %q", s, "<nil>")
+               t.Errorf("(*ProcessState)(nil).String() = %q, want %q", s, "<nil>")
        }
 }