summaryrefslogtreecommitdiff
path: root/libgo/runtime/go-deferred-recover.c
diff options
context:
space:
mode:
Diffstat (limited to 'libgo/runtime/go-deferred-recover.c')
-rw-r--r--libgo/runtime/go-deferred-recover.c92
1 files changed, 92 insertions, 0 deletions
diff --git a/libgo/runtime/go-deferred-recover.c b/libgo/runtime/go-deferred-recover.c
new file mode 100644
index 000000000..2d9ca1442
--- /dev/null
+++ b/libgo/runtime/go-deferred-recover.c
@@ -0,0 +1,92 @@
+/* go-deferred-recover.c -- support for a deferred recover function.
+
+ Copyright 2010 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. */
+
+#include <stddef.h>
+
+#include "go-panic.h"
+#include "go-defer.h"
+
+/* This is called when a call to recover is deferred. That is,
+ something like
+ defer recover()
+
+ We need to handle this specially. In 6g/8g, the recover function
+ looks up the stack frame. In particular, that means that a
+ deferred recover will not recover a panic thrown in the same
+ function that defers the recover. It will only recover a panic
+ thrown in a function that defers the deferred call to recover.
+
+ In other words:
+
+ func f1() {
+ defer recover() // does not stop panic
+ panic(0)
+ }
+
+ func f2() {
+ defer func() {
+ defer recover() // stops panic(0)
+ }()
+ panic(0)
+ }
+
+ func f3() {
+ defer func() {
+ defer recover() // does not stop panic
+ panic(0)
+ }()
+ panic(1)
+ }
+
+ func f4() {
+ defer func() {
+ defer func() {
+ defer recover() // stops panic(0)
+ }()
+ panic(0)
+ }()
+ panic(1)
+ }
+
+ The interesting case here is f3. As can be seen from f2, the
+ deferred recover could pick up panic(1). However, this does not
+ happen because it is blocked by the panic(0).
+
+ When a function calls recover, then when we invoke it we pass a
+ hidden parameter indicating whether it should recover something.
+ This parameter is set based on whether the function is being
+ invoked directly from defer. The parameter winds up determining
+ whether __go_recover or __go_deferred_recover is called at all.
+
+ In the case of a deferred recover, the hidden parameter which
+ controls the call is actually the one set up for the function which
+ runs the defer recover() statement. That is the right thing in all
+ the cases above except for f3. In f3 the function is permitted to
+ call recover, but the deferred recover call is not. We address
+ that here by checking for that specific case before calling
+ recover. If this function was deferred when there is already a
+ panic on the panic stack, then we can only recover that panic, not
+ any other.
+
+ Note that we can get away with using a special function here
+ because you are not permitted to take the address of a predeclared
+ function like recover. */
+
+struct __go_empty_interface
+__go_deferred_recover ()
+{
+ if (__go_panic_defer == NULL
+ || __go_panic_defer->__defer == NULL
+ || __go_panic_defer->__defer->__panic != __go_panic_defer->__panic)
+ {
+ struct __go_empty_interface ret;
+
+ ret.__type_descriptor = NULL;
+ ret.__object = NULL;
+ return ret;
+ }
+ return __go_recover();
+}