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 *******************************/