runtime: improve handling of panic during deferred function

When a panic occurs while processing a deferred function that
recovered an earlier panic, we shouldn't report the recovered panic
in the panic stack trace. Stop doing so by keeping track of the panic
that triggered a defer, marking it as aborted if we see the defer again,
and discarding aborted panics when a panic is recovered. This is what
the gc runtime does.

The test for this is TestRecursivePanic in runtime/crash_test.go.
We don't run that test yet, but we will soon.

Change-Id: Idbfd4cca731116cd620c7012499c3c9ec8065ff2
Reviewed-on: https://go-review.googlesource.com/46461
Reviewed-by: Than McIntosh <thanm@google.com>
diff --git a/libgo/go/runtime/panic.go b/libgo/go/runtime/panic.go
index de3c79f..e3d0358 100644
--- a/libgo/go/runtime/panic.go
+++ b/libgo/go/runtime/panic.go
@@ -91,6 +91,9 @@
 // arg is a value to pass to pfn.
 func deferproc(frame *bool, pfn uintptr, arg unsafe.Pointer) {
 	d := newdefer()
+	if d._panic != nil {
+		throw("deferproc: d.panic != nil after newdefer")
+	}
 	d.frame = frame
 	d.panicStack = getg()._panic
 	d.pfn = pfn
@@ -338,17 +341,28 @@
 		if d == nil {
 			break
 		}
-		gp._defer = d.link
 
 		pfn := d.pfn
+		if pfn == 0 {
+			if d._panic != nil {
+				d._panic.aborted = true
+				d._panic = nil
+			}
+			gp._defer = d.link
+			freedefer(d)
+			continue
+		}
 		d.pfn = 0
 
-		if pfn != 0 {
-			var fn func(unsafe.Pointer)
-			*(*uintptr)(unsafe.Pointer(&fn)) = uintptr(unsafe.Pointer(&pfn))
-			fn(d.arg)
-		}
+		var fn func(unsafe.Pointer)
+		*(*uintptr)(unsafe.Pointer(&fn)) = uintptr(unsafe.Pointer(&pfn))
+		fn(d.arg)
 
+		if gp._defer != d {
+			throw("bad defer entry in Goexit")
+		}
+		d._panic = nil
+		gp._defer = d.link
 		freedefer(d)
 		// Note: we ignore recovers here because Goexit isn't a panic
 	}
@@ -442,39 +456,71 @@
 		}
 
 		pfn := d.pfn
+
+		// If defer was started by earlier panic or Goexit (and, since we're back here, that triggered a new panic),
+		// take defer off list. The earlier panic or Goexit will not continue running.
+		if pfn == 0 {
+			if d._panic != nil {
+				d._panic.aborted = true
+			}
+			d._panic = nil
+			gp._defer = d.link
+			freedefer(d)
+			continue
+		}
 		d.pfn = 0
 
-		if pfn != 0 {
-			var fn func(unsafe.Pointer)
-			*(*uintptr)(unsafe.Pointer(&fn)) = uintptr(unsafe.Pointer(&pfn))
-			fn(d.arg)
+		// Record the panic that is running the defer.
+		// If there is a new panic during the deferred call, that panic
+		// will find d in the list and will mark d._panic (this panic) aborted.
+		d._panic = p
 
-			if p.recovered {
-				// Some deferred function called recover.
-				// Stop running this panic.
-				gp._panic = p.link
+		var fn func(unsafe.Pointer)
+		*(*uintptr)(unsafe.Pointer(&fn)) = uintptr(unsafe.Pointer(&pfn))
+		fn(d.arg)
 
-				// Unwind the stack by throwing an exception.
-				// The compiler has arranged to create
-				// exception handlers in each function
-				// that uses a defer statement.  These
-				// exception handlers will check whether
-				// the entry on the top of the defer stack
-				// is from the current function.  If it is,
-				// we have unwound the stack far enough.
-				unwindStack()
+		if gp._defer != d {
+			throw("bad defer entry in panic")
+		}
+		d._panic = nil
 
-				throw("unwindStack returned")
+		if p.recovered {
+			// Some deferred function called recover.
+			// Stop running this panic.
+			gp._panic = p.link
+
+			// Aborted panics are marked but remain on the g.panic list.
+			// Remove them from the list.
+			for gp._panic != nil && gp._panic.aborted {
+				gp._panic = gp._panic.link
+			}
+			if gp._panic == nil { // must be done with signal
+				gp.sig = 0
 			}
 
-			// Because we executed that defer function by a panic,
-			// and it did not call recover, we know that we are
-			// not returning from the calling function--we are
-			// panicking through it.
-			*d.frame = false
+			// Unwind the stack by throwing an exception.
+			// The compiler has arranged to create
+			// exception handlers in each function
+			// that uses a defer statement.  These
+			// exception handlers will check whether
+			// the entry on the top of the defer stack
+			// is from the current function.  If it is,
+			// we have unwound the stack far enough.
+			unwindStack()
+
+			throw("unwindStack returned")
 		}
 
+		// Because we executed that defer function by a panic,
+		// and it did not call recover, we know that we are
+		// not returning from the calling function--we are
+		// panicking through it.
+		*d.frame = false
+
+		// Deferred function did not panic. Remove d.
+		// In the p.recovered case, d will be removed by checkdefer.
 		gp._defer = d.link
+
 		freedefer(d)
 	}
 
diff --git a/libgo/go/runtime/runtime2.go b/libgo/go/runtime/runtime2.go
index 96a4edb..cdd3fcc 100644
--- a/libgo/go/runtime/runtime2.go
+++ b/libgo/go/runtime/runtime2.go
@@ -700,6 +700,10 @@
 	// has a defer statement itself.
 	panicStack *_panic
 
+	// The panic that caused the defer to run. This is used to
+	// discard panics that have already been handled.
+	_panic *_panic
+
 	// The function to call.
 	pfn uintptr
 
@@ -733,6 +737,10 @@
 	// Whether this panic was pushed on the stack because of an
 	// exception thrown in some other language.
 	isforeign bool
+
+	// Whether this panic was already seen by a deferred function
+	// which called panic again.
+	aborted bool
 }
 
 const (