From e601e775905f356f897857759c479d5ab76b2286 Mon Sep 17 00:00:00 2001 From: nobu Date: Mon, 17 Apr 2017 02:31:35 +0000 Subject: eval.c: copy special exceptions before raise * eval.c (setup_exception): consider if the exception is frozen, but not one of special exception objects. * gc.c (rb_memerror): copy minimum objects. * thread.c (rb_threadptr_execute_interrupts): prepare special exception queued by another thread to be raised. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@58380 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- eval.c | 58 ++++++++++++++-------------------------------------------- gc.c | 14 +++++++++----- thread.c | 4 ++++ 3 files changed, 27 insertions(+), 49 deletions(-) diff --git a/eval.c b/eval.c index f3c151a130..7f500b8d64 100644 --- a/eval.c +++ b/eval.c @@ -464,23 +464,6 @@ exc_setup_cause(VALUE exc, VALUE cause) return exc; } -static inline int -sysstack_error_p(VALUE exc) -{ - return exc == sysstack_error || (!SPECIAL_CONST_P(exc) && RBASIC_CLASS(exc) == rb_eSysStackError); -} - -static inline int -special_exception_p(rb_thread_t *th, VALUE exc) -{ - enum ruby_special_exceptions i; - const VALUE *exceptions = th->vm->special_exceptions; - for (i = 0; i < ruby_special_error_count; ++i) { - if (exceptions[i] == exc) return TRUE; - } - return FALSE; -} - static void setup_exception(rb_thread_t *th, int tag, volatile VALUE mesg, VALUE cause) { @@ -498,9 +481,6 @@ setup_exception(rb_thread_t *th, int tag, volatile VALUE mesg, VALUE cause) mesg = rb_exc_new(rb_eRuntimeError, 0, 0); nocause = 0; } - else if (special_exception_p(th, mesg)) { - mesg = ruby_vm_special_exception_copy(mesg); - } if (cause != Qundef) { exc_setup_cause(mesg, cause); } @@ -514,33 +494,24 @@ setup_exception(rb_thread_t *th, int tag, volatile VALUE mesg, VALUE cause) file = rb_source_loc(&line); if (file && !NIL_P(mesg)) { VALUE at; - if (sysstack_error_p(mesg)) { - if (NIL_P(rb_attr_get(mesg, idBt))) { + int status; + + TH_PUSH_TAG(th); + if ((status = EXEC_TAG()) == 0) { + VALUE bt; + if (rb_threadptr_set_raised(th)) goto fatal; + bt = rb_get_backtrace(mesg); + if (NIL_P(bt)) { at = rb_threadptr_backtrace_object(th); - rb_ivar_set(mesg, idBt, at); - rb_ivar_set(mesg, idBt_locations, at); - } - } - else { - int status; - - TH_PUSH_TAG(th); - if ((status = EXEC_TAG()) == 0) { - VALUE bt; - if (rb_threadptr_set_raised(th)) goto fatal; - bt = rb_get_backtrace(mesg); - if (NIL_P(bt)) { - at = rb_threadptr_backtrace_object(th); - if (OBJ_FROZEN(mesg)) { - mesg = rb_obj_dup(mesg); - } - rb_ivar_set(mesg, idBt_locations, at); - set_backtrace(mesg, at); + if (OBJ_FROZEN(mesg)) { + mesg = rb_obj_dup(mesg); } - rb_threadptr_reset_raised(th); + rb_ivar_set(mesg, idBt_locations, at); + set_backtrace(mesg, at); } - TH_POP_TAG(); + rb_threadptr_reset_raised(th); } + TH_POP_TAG(); } if (!NIL_P(mesg)) { @@ -738,7 +709,6 @@ make_exception(int argc, const VALUE *argv, int isstr) exc = argv[0]; n = 1; exception_call: - if (sysstack_error_p(exc)) return exc; mesg = rb_check_funcall(exc, idException, n, argv+1); if (mesg == Qundef) { rb_raise(rb_eTypeError, "exception class/object expected"); diff --git a/gc.c b/gc.c index 2c23abf0fa..6378c5d7ac 100644 --- a/gc.c +++ b/gc.c @@ -7624,21 +7624,25 @@ rb_memerror(void) { rb_thread_t *th = GET_THREAD(); rb_objspace_t *objspace = rb_objspace_of(th->vm); + VALUE exc; if (during_gc) gc_exit(objspace, "rb_memerror"); - if (!nomem_error || + exc = nomem_error; + if (!exc || rb_thread_raised_p(th, RAISED_NOMEMORY)) { fprintf(stderr, "[FATAL] failed to allocate memory\n"); exit(EXIT_FAILURE); } if (rb_thread_raised_p(th, RAISED_NOMEMORY)) { rb_thread_raised_clear(th); - GET_THREAD()->errinfo = nomem_error; - TH_JUMP_TAG(th, TAG_RAISE); } - rb_thread_raised_set(th, RAISED_NOMEMORY); - rb_exc_raise(nomem_error); + else { + rb_thread_raised_set(th, RAISED_NOMEMORY); + exc = ruby_vm_special_exception_copy(exc); + } + th->errinfo = exc; + TH_JUMP_TAG(th, TAG_RAISE); } static void * diff --git a/thread.c b/thread.c index a21ef7ba78..972e1b9c6e 100644 --- a/thread.c +++ b/thread.c @@ -2071,6 +2071,10 @@ rb_threadptr_execute_interrupts(rb_thread_t *th, int blocking_timing) rb_threadptr_to_kill(th); } else { + if (err == th->vm->special_exceptions[ruby_error_stream_closed]) { + /* the only special exception to be queued accross thread */ + err = ruby_vm_special_exception_copy(err); + } /* set runnable if th was slept. */ if (th->status == THREAD_STOPPED || th->status == THREAD_STOPPED_FOREVER) -- cgit v1.2.3