diff options
| author | Austin Clements <aclements@csail.mit.edu> | 2009-10-15 12:59:59 -0700 |
|---|---|---|
| committer | Austin Clements <aclements@csail.mit.edu> | 2009-10-15 12:59:59 -0700 |
| commit | 049501ce4c10093ba817f48aff4fa07c8984d796 (patch) | |
| tree | 8120023948d1a2fec9247faa39cf8cdc9313300a /src/pkg/debug | |
| parent | 974b23f569a67499eb727dda0219759dd5466e27 (diff) | |
| download | go-049501ce4c10093ba817f48aff4fa07c8984d796.tar.xz | |
Implement error handling on process monitor exit. Now, before
sending any message to the monitor, the sender must check a
"ready" channel. Before exiting, the monitor records its exit
error and closes this channel, ensuring that all later reads
from the ready channel will immediately return false.
Inspired by
http://chplib.wordpress.com/2009/09/30/poison-concurrent-termination/
R=rsc
APPROVED=rsc
DELTA=47 (27 added, 11 deleted, 9 changed)
OCL=35782
CL=35784
Diffstat (limited to 'src/pkg/debug')
| -rw-r--r-- | src/pkg/debug/proc/proc_linux.go | 56 |
1 files changed, 36 insertions, 20 deletions
diff --git a/src/pkg/debug/proc/proc_linux.go b/src/pkg/debug/proc/proc_linux.go index d2c8b8af2a..f278ec7277 100644 --- a/src/pkg/debug/proc/proc_linux.go +++ b/src/pkg/debug/proc/proc_linux.go @@ -145,14 +145,22 @@ type transitionHandler struct { // Each running process has one monitor thread, which processes // messages from the debugEvents, debugReqs, and stopReq channels and // calls transition handlers. +// +// To send a message to the monitor thread, first receive from the +// ready channel. If the ready channel returns true, the monitor is +// still running and will accept a message. If the ready channel +// returns false, the monitor is not running (the ready channel has +// been closed), and the reason it is not running will be stored in err. type process struct { pid int; threads map[int]*thread; breakpoints map[uintptr]*breakpoint; + ready chan bool; debugEvents chan *debugEvent; debugReqs chan *debugReq; stopReq chan os.Error; transitionHandlers *vector.Vector; + err os.Error; } // A thread represents a Linux thread in another process that is being @@ -212,6 +220,12 @@ func (e *newThreadError) String() string { return fmt.Sprintf("newThread wait wanted pid %v and signal %v, got %v and %v", e.Pid, e.StopSignal(), e.wantPid, e.wantSig); } +type ProcessExited struct {} + +func (p ProcessExited) String() string { + return "process exited"; +} + /* * Ptrace wrappers */ @@ -439,6 +453,10 @@ func (t *thread) wait() { // the stop go through so we can // update the thread's state. } + if !<-t.proc.ready { + // The monitor exited + break; + } t.proc.debugEvents <- &ev; break; } @@ -695,10 +713,6 @@ func (t *thread) onStop(handle func(), onErr func(os.Error)) { // monitor handles debug events and debug requests for p, exiting when // there are no threads left in p. -// -// TODO(austin) When an unrecoverable error occurs, abort the monitor -// and record this error so all future calls to do will return it -// immediately. func (p *process) monitor() { var err os.Error; @@ -709,13 +723,11 @@ func (p *process) monitor() { defer runtime.UnlockOSThread(); hadThreads := false; - for { + for err == nil { + p.ready <- true; select { case event := <-p.debugEvents: err = event.process(); - if err != nil { - break; - } case req := <-p.debugReqs: req.res <- req.f(); @@ -725,12 +737,9 @@ func (p *process) monitor() { } if len(p.threads) == 0 { - if hadThreads { + if err == nil && hadThreads { p.logTrace("no more threads; monitor exiting"); - // TODO(austin) Use a real error do - // future operations will fail - err = nil; - break; + err = ProcessExited{}; } } else { hadThreads = true; @@ -738,15 +747,15 @@ func (p *process) monitor() { } // Abort waiting handlers + // TODO(austin) How do I stop the wait threads? for _, h := range p.transitionHandlers.Data() { h := h.(*transitionHandler); h.onErr(err); } - // TODO(austin) How do I stop the wait threads? - if err != nil { - panic(err.String()); - } + // Indicate that the monitor cannot receive any more messages + p.err = err; + close(p.ready); } // do executes f in the monitor thread (and, thus, atomically with @@ -754,7 +763,9 @@ func (p *process) monitor() { // // Must NOT be called from the monitor thread. func (p *process) do(f func() os.Error) os.Error { - // TODO(austin) If monitor is stopped, return error. + if !<-p.ready { + return p.err; + } req := &debugReq{f, make(chan os.Error)}; p.debugReqs <- req; return <-req.res; @@ -763,8 +774,12 @@ func (p *process) do(f func() os.Error) os.Error { // stopMonitor stops the monitor with the given error. If the monitor // is already stopped, does nothing. func (p *process) stopMonitor(err os.Error) { - _ = p.stopReq <- err; // do not block -// TODO(austin) Wait until monitor has exited? + if err == nil { + panic("cannot stop the monitor with no error"); + } + if <-p.ready { + p.stopReq <- err; + } } /* @@ -1255,6 +1270,7 @@ func newProcess(pid int) *process { pid: pid, threads: make(map[int]*thread), breakpoints: make(map[uintptr]*breakpoint), + ready: make(chan bool, 1), debugEvents: make(chan *debugEvent), debugReqs: make(chan *debugReq), stopReq: make(chan os.Error), |
