diff --git a/ChangeLog b/ChangeLog index 036e770ba3..638ec8575e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,17 @@ +Thu Jul 31 00:44:34 2014 Koichi Sasada + + * vm_trace.c: clear and restore recursive checking thread local data + to avoid unexpected throw from TracePoint. + [Bug #9940] + + * test/ruby/test_settracefunc.rb: add a test. + + * thread.c: added + * rb_threadptr_reset_recursive_data(rb_thread_t *th); + * rb_threadptr_restore_recursive_data(rb_thread_t *th, VALUE old); + + * vm_core.h: ditto. + Wed Jul 23 23:49:59 2014 Hiroshi Shirosaki * lib/test/unit/parallel.rb: fix test-all parallel failure if a test diff --git a/test/ruby/test_settracefunc.rb b/test/ruby/test_settracefunc.rb index 79f315edd5..818cf0f0b5 100644 --- a/test/ruby/test_settracefunc.rb +++ b/test/ruby/test_settracefunc.rb @@ -1182,6 +1182,18 @@ class TestSetTraceFunc < Test::Unit::TestCase assert_equal([['call', :foo], ['return', :foo]], events, 'Bug #9759') ensure end + end + def test_recursive + assert_ruby_status [], %q{ + stack = [] + TracePoint.new(:c_call){|tp| + p 2 + stack << tp.method_id + }.enable{ + p 1 + } + raise if stack != [:p, :hash, :inspect] + }, '[Bug #9940]' end end diff --git a/thread.c b/thread.c index f3e60fce41..b51698fc24 100644 --- a/thread.c +++ b/thread.c @@ -2746,20 +2746,23 @@ rb_thread_inspect(VALUE thread) return str; } +static VALUE +threadptr_local_aref(rb_thread_t *th, ID id) +{ + st_data_t val; + + if (th->local_storage && st_lookup(th->local_storage, id, &val)) { + return (VALUE)val; + } + return Qnil; +} + VALUE rb_thread_local_aref(VALUE thread, ID id) { rb_thread_t *th; - st_data_t val; - GetThreadPtr(thread, th); - if (!th->local_storage) { - return Qnil; - } - if (st_lookup(th->local_storage, id, &val)) { - return (VALUE)val; - } - return Qnil; + return threadptr_local_aref(th, id); } /* @@ -2830,6 +2833,23 @@ rb_thread_aref(VALUE thread, VALUE key) return rb_thread_local_aref(thread, id); } +static VALUE +threadptr_local_aset(rb_thread_t *th, ID id, VALUE val) +{ + if (NIL_P(val)) { + if (!th->local_storage) return Qnil; + st_delete_wrap(th->local_storage, id); + return Qnil; + } + else { + if (!th->local_storage) { + th->local_storage = st_init_numtable(); + } + st_insert(th->local_storage, id, val); + return val; +} +} + VALUE rb_thread_local_aset(VALUE thread, ID id, VALUE val) { @@ -2839,16 +2859,8 @@ rb_thread_local_aset(VALUE thread, ID id, VALUE val) if (OBJ_FROZEN(thread)) { rb_error_frozen("thread locals"); } - if (NIL_P(val)) { - if (!th->local_storage) return Qnil; - st_delete_wrap(th->local_storage, id); - return Qnil; - } - if (!th->local_storage) { - th->local_storage = st_init_numtable(); - } - st_insert(th->local_storage, id, val); - return val; + + return threadptr_local_aset(th, id, val); } /* @@ -4778,6 +4790,20 @@ recursive_list_access(void) return list; } +VALUE +rb_threadptr_reset_recursive_data(rb_thread_t *th) +{ + VALUE old = threadptr_local_aref(th, recursive_key); + threadptr_local_aset(th, recursive_key, Qnil); + return old; +} + +void +rb_threadptr_restore_recursive_data(rb_thread_t *th, VALUE old) +{ + threadptr_local_aset(th, recursive_key, old); +} + /* * Returns Qtrue iff obj_id (or the pair ) is already * in the recursion list. diff --git a/version.h b/version.h index bb82cc0620..025a19fc3d 100644 --- a/version.h +++ b/version.h @@ -1,10 +1,10 @@ #define RUBY_VERSION "2.1.2" -#define RUBY_RELEASE_DATE "2014-07-23" -#define RUBY_PATCHLEVEL 182 +#define RUBY_RELEASE_DATE "2014-07-31" +#define RUBY_PATCHLEVEL 183 #define RUBY_RELEASE_YEAR 2014 #define RUBY_RELEASE_MONTH 7 -#define RUBY_RELEASE_DAY 23 +#define RUBY_RELEASE_DAY 31 #include "ruby/version.h" diff --git a/vm_core.h b/vm_core.h index fbdd57e8fa..ed6b6a5dc9 100644 --- a/vm_core.h +++ b/vm_core.h @@ -1032,6 +1032,9 @@ void rb_threadptr_exec_event_hooks_and_pop_frame(struct rb_trace_arg_struct *tra #define EXEC_EVENT_HOOK_AND_POP_FRAME(th_, flag_, self_, id_, klass_, data_) \ EXEC_EVENT_HOOK_ORIG(th_, flag_, self_, id_, klass_, data_, 1) +VALUE rb_threadptr_reset_recursive_data(rb_thread_t *th); +void rb_threadptr_restore_recursive_data(rb_thread_t *th, VALUE old); + RUBY_SYMBOL_EXPORT_BEGIN int rb_thread_check_trap_pending(void); diff --git a/vm_trace.c b/vm_trace.c index 81452997e9..2047d5aec8 100644 --- a/vm_trace.c +++ b/vm_trace.c @@ -335,6 +335,7 @@ rb_threadptr_exec_event_hooks_orig(rb_trace_arg_t *trace_arg, int pop_p) trace_arg->self != rb_mRubyVMFrozenCore /* skip special methods. TODO: remove it. */) { const VALUE errinfo = th->errinfo; const int outer_state = th->state; + const VALUE old_recursive = rb_threadptr_reset_recursive_data(th); int state = 0; th->state = 0; th->errinfo = Qnil; @@ -355,6 +356,7 @@ rb_threadptr_exec_event_hooks_orig(rb_trace_arg_t *trace_arg, int pop_p) terminate: th->trace_arg = 0; th->vm->trace_running--; + rb_threadptr_restore_recursive_data(th, old_recursive); if (state) { if (pop_p) {