You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucy.apache.org by ma...@apache.org on 2015/03/19 01:09:37 UTC

[17/19] lucy-clownfish git commit: Implement error handling in terms of Go panic.

Implement error handling in terms of Go panic.

*   Clownfish `THROW` calls Go `panic`.
*   Clownfish `trap` utilizes Go `recover`.


Project: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/commit/c320f1fd
Tree: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/tree/c320f1fd
Diff: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/diff/c320f1fd

Branch: refs/heads/master
Commit: c320f1fd5a65b971a496787b59309ef82d8f4845
Parents: 56d26bc
Author: Marvin Humphrey <ma...@rectangular.com>
Authored: Sat Nov 15 19:45:17 2014 -0800
Committer: Marvin Humphrey <ma...@rectangular.com>
Committed: Sun Mar 15 19:02:11 2015 -0700

----------------------------------------------------------------------
 runtime/go/clownfish/clownfish.go | 84 ++++++++++++++++++++++++++++++++++
 runtime/go/clownfish/err_test.go  | 42 +++++++++++++++++
 runtime/go/ext/clownfish.c        | 42 ++++++-----------
 3 files changed, 141 insertions(+), 27 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/c320f1fd/runtime/go/clownfish/clownfish.go
----------------------------------------------------------------------
diff --git a/runtime/go/clownfish/clownfish.go b/runtime/go/clownfish/clownfish.go
index 75a3314..5b9b515 100644
--- a/runtime/go/clownfish/clownfish.go
+++ b/runtime/go/clownfish/clownfish.go
@@ -31,12 +31,40 @@ package clownfish
 #include "Clownfish/LockFreeRegistry.h"
 #include "Clownfish/Method.h"
 
+extern void
+GoCfish_PanicErr_internal(cfish_Err *error);
+typedef void
+(*cfish_Err_do_throw_t)(cfish_Err *error);
+extern cfish_Err_do_throw_t GoCfish_PanicErr;
+
+extern cfish_Err*
+GoCfish_TrapErr_internal(CFISH_Err_Attempt_t routine, void *context);
+typedef cfish_Err*
+(*cfish_Err_trap_t)(CFISH_Err_Attempt_t routine, void *context);
+extern cfish_Err_trap_t GoCfish_TrapErr;
+
+// C symbols linked into a Go-built package archive are not visible to
+// external C code -- but internal code *can* see symbols from outside.
+// This allows us to fake up symbol export by assigning values only known
+// interally to external symbols during Go package initialization.
+static CHY_INLINE void
+GoCfish_glue_exported_symbols() {
+	GoCfish_PanicErr = GoCfish_PanicErr_internal;
+	GoCfish_TrapErr  = GoCfish_TrapErr_internal;
+}
+
+static CHY_INLINE void
+GoCfish_RunRoutine(CFISH_Err_Attempt_t routine, void *context) {
+	routine(context);
+}
+
 */
 import "C"
 import "runtime"
 import "unsafe"
 
 func init() {
+	C.GoCfish_glue_exported_symbols()
 	C.cfish_bootstrap_parcel()
 }
 
@@ -108,3 +136,59 @@ func CFStringToGo(ptr unsafe.Pointer) string {
 	size := C.int(C.CFISH_Str_Get_Size(cfString))
 	return C.GoStringN(data, size)
 }
+
+// TODO: Err should be an interface.
+func NewError(mess string) error {
+	str := C.CString(mess)
+	len := C.size_t(len(mess))
+	messC := C.cfish_Str_new_steal_utf8(str, len)
+	obj := &Err{C.cfish_Err_new(messC)}
+	runtime.SetFinalizer(obj, (*Err).callDecRef)
+	return obj
+}
+
+func (obj *Err) callDecRef() {
+	C.cfish_dec_refcount(unsafe.Pointer(obj.ref))
+	obj.ref = nil
+}
+
+func (obj *Err) Error() string {
+	return CFStringToGo(unsafe.Pointer(C.CFISH_Err_Get_Mess(obj.ref)))
+}
+
+//export GoCfish_PanicErr_internal
+func GoCfish_PanicErr_internal(cfErr *C.cfish_Err) {
+	goErr := &Err{cfErr}
+	C.cfish_inc_refcount(unsafe.Pointer(cfErr))
+	runtime.SetFinalizer(goErr, (*Err).callDecRef)
+	panic(goErr)
+}
+
+//export GoCfish_TrapErr_internal
+func GoCfish_TrapErr_internal(routine C.CFISH_Err_Attempt_t,
+	context unsafe.Pointer) *C.cfish_Err {
+	err := TrapErr(func() { C.GoCfish_RunRoutine(routine, context) })
+	if err != nil {
+		return (err.(*Err)).ref
+	}
+	return nil
+}
+
+// Run the supplied routine, and if it panics with a *clownfish.Err, trap and
+// return it.
+func TrapErr(routine func()) (trapped error) {
+	defer func() {
+		if r := recover(); r != nil {
+			// TODO: pass whitelist of Err types to trap.
+			myErr, ok := r.(*Err)
+			if ok {
+				trapped = myErr
+			} else {
+				// re-panic
+				panic(r)
+			}
+		}
+	}()
+	routine()
+	return trapped
+}

http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/c320f1fd/runtime/go/clownfish/err_test.go
----------------------------------------------------------------------
diff --git a/runtime/go/clownfish/err_test.go b/runtime/go/clownfish/err_test.go
new file mode 100644
index 0000000..060cf1e
--- /dev/null
+++ b/runtime/go/clownfish/err_test.go
@@ -0,0 +1,42 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package clownfish_test
+
+import "git-wip-us.apache.org/repos/asf/lucy-clownfish.git/runtime/go/clownfish"
+import "testing"
+import "errors"
+
+func TestTrapErr(t *testing.T) {
+	err := clownfish.TrapErr(
+		func() { panic(clownfish.NewError("mistakes were made")) },
+	)
+	if err == nil {
+		t.Error("Failed to trap *clownfish.Err")
+	}
+}
+
+func TestTrapErr_no_trap_string(t *testing.T) {
+	defer func() { recover() }()
+	clownfish.TrapErr(func() { panic("foo") })
+	t.Error("Trapped plain string") // shouldn't reach here
+}
+
+func TestTrapErr_no_trap_error(t *testing.T) {
+	defer func() { recover() }()
+	clownfish.TrapErr(func() { panic(errors.New("foo")) })
+	t.Error("Trapped non-clownfish.Error error type") // shouldn't reach here
+}

http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/c320f1fd/runtime/go/ext/clownfish.c
----------------------------------------------------------------------
diff --git a/runtime/go/ext/clownfish.c b/runtime/go/ext/clownfish.c
index 196f90c..851012b 100644
--- a/runtime/go/ext/clownfish.c
+++ b/runtime/go/ext/clownfish.c
@@ -21,7 +21,6 @@
 #define C_CFISH_ERR
 #define C_CFISH_LOCKFREEREGISTRY
 
-#include <setjmp.h>
 #include <stdio.h>
 #include <stdlib.h>
 
@@ -36,6 +35,11 @@
 #include "Clownfish/VArray.h"
 #include "Clownfish/LockFreeRegistry.h"
 
+/* These symbols must be assigned real values during Go initialization,
+ * which we'll confirm in Err_init().  */
+void (*GoCfish_PanicErr)(Err *error);
+Err* (*GoCfish_TrapErr)(Err_Attempt_t routine, void *context);
+
 /******************************** Obj **************************************/
 
 static CFISH_INLINE bool
@@ -199,11 +203,17 @@ Method_Host_Name_IMP(Method *self) {
 
 /* TODO: Thread safety */
 static Err *current_error;
-static Err *thrown_error;
-static jmp_buf  *current_env;
 
 void
 Err_init_class(void) {
+    if (GoCfish_PanicErr == NULL
+        || GoCfish_TrapErr == NULL
+       ) {
+        fprintf(stderr, "Error at file %s line %d: Unexpected internal "
+            "failure to initialize functions during bootstrapping\n",
+            __FILE__, __LINE__);
+        exit(1);
+    }
 }
 
 Err*
@@ -221,17 +231,7 @@ Err_set_error(Err *error) {
 
 void
 Err_do_throw(Err *error) {
-    if (current_env) {
-        thrown_error = error;
-        longjmp(*current_env, 1);
-    }
-    else {
-        String *message = Err_Get_Mess(error);
-        char *utf8 = Str_To_Utf8(message);
-        fprintf(stderr, "%s", utf8);
-        FREEMEM(utf8);
-        exit(EXIT_FAILURE);
-    }
+    GoCfish_PanicErr(error);
 }
 
 void*
@@ -258,19 +258,7 @@ Err_warn_mess(String *message) {
 
 Err*
 Err_trap(Err_Attempt_t routine, void *context) {
-    jmp_buf  env;
-    jmp_buf *prev_env = current_env;
-    current_env = &env;
-
-    if (!setjmp(env)) {
-        routine(context);
-    }
-
-    current_env = prev_env;
-
-    Err *error = thrown_error;
-    thrown_error = NULL;
-    return error;
+    return GoCfish_TrapErr(routine, context);
 }
 
 /************************** LockFreeRegistry *******************************/