diff options
author | ko1 <ko1@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2012-08-16 11:41:24 +0000 |
---|---|---|
committer | ko1 <ko1@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2012-08-16 11:41:24 +0000 |
commit | 4a4a702e61d1c5585d522f1185a82a5685c554f6 (patch) | |
tree | bbbd9f9a92339aa20f6a113238965064a67487c7 | |
parent | 9528358120511f0587be39c853f5916cf8d05a9d (diff) | |
download | ruby-4a4a702e61d1c5585d522f1185a82a5685c554f6.tar.gz |
* vm_trace.c, vm_core.h: simplify tracing mechanism.
(1) add rb_hook_list_t data structure which includes
hooks, events (flag) and `need_clean' flag.
If the last flag is true, then clean the hooks list.
In other words, deleted hooks are contained by `hooks'.
Cleanup process should run before traversing the list.
(2) Change check mechanism
See EXEC_EVENT_HOOK() in vm_core.h.
(3) Add `raw' hooks APIs
Normal hooks are guarded from exception by rb_protect().
However, this protection is overhead for too simple
functions which never cause exceptions. `raw' hooks
are executed without protection and faster.
Now, we only provide registration APIs. All `raw'
hooks are kicked under protection (same as normal hooks).
* include/ruby/ruby.h: remove internal data definition and
macros.
* internal.h (ruby_suppress_tracing), vm_trace.c: rename
ruby_suppress_tracing() to rb_suppress_tracing()
and remove unused function parameter.
* parse.y: fix to use renamed rb_suppress_tracing().
* thread.c (thread_create_core): no need to set RUBY_VM_VM.
* vm.c (mark_event_hooks): move definition to vm_trace.c.
* vm.c (ruby_vm_event_flags): add a global variable.
This global variable represents all of Threads and VM's
event masks (T1#events | T2#events | ... | VM#events).
You can check the possibility kick trace func or not
with ruby_vm_event_flags.
ruby_vm_event_flags is maintained by vm_trace.c.
* cont.c (fiber_switch, rb_cont_call): restore tracing status.
[Feature #4347]
* test/ruby/test_continuation.rb: ditto.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@36715 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
-rw-r--r-- | ChangeLog | 44 | ||||
-rw-r--r-- | cont.c | 7 | ||||
-rw-r--r-- | include/ruby/ruby.h | 13 | ||||
-rw-r--r-- | internal.h | 2 | ||||
-rw-r--r-- | parse.y | 4 | ||||
-rw-r--r-- | test/ruby/test_continuation.rb | 18 | ||||
-rw-r--r-- | thread.c | 2 | ||||
-rw-r--r-- | vm.c | 14 | ||||
-rw-r--r-- | vm_core.h | 19 | ||||
-rw-r--r-- | vm_trace.c | 541 |
10 files changed, 335 insertions, 329 deletions
@@ -1,3 +1,47 @@ +Thu Aug 16 19:54:24 2012 Koichi Sasada <ko1@atdot.net> + + * vm_trace.c, vm_core.h: simplify tracing mechanism. + + (1) add rb_hook_list_t data structure which includes + hooks, events (flag) and `need_clean' flag. + If the last flag is true, then clean the hooks list. + In other words, deleted hooks are contained by `hooks'. + Cleanup process should run before traversing the list. + (2) Change check mechanism + See EXEC_EVENT_HOOK() in vm_core.h. + (3) Add `raw' hooks APIs + Normal hooks are guarded from exception by rb_protect(). + However, this protection is overhead for too simple + functions which never cause exceptions. `raw' hooks + are executed without protection and faster. + Now, we only provide registration APIs. All `raw' + hooks are kicked under protection (same as normal hooks). + + * include/ruby/ruby.h: remove internal data definition and + macros. + + * internal.h (ruby_suppress_tracing), vm_trace.c: rename + ruby_suppress_tracing() to rb_suppress_tracing() + and remove unused function parameter. + + * parse.y: fix to use renamed rb_suppress_tracing(). + + * thread.c (thread_create_core): no need to set RUBY_VM_VM. + + * vm.c (mark_event_hooks): move definition to vm_trace.c. + + * vm.c (ruby_vm_event_flags): add a global variable. + This global variable represents all of Threads and VM's + event masks (T1#events | T2#events | ... | VM#events). + You can check the possibility kick trace func or not + with ruby_vm_event_flags. + ruby_vm_event_flags is maintained by vm_trace.c. + + * cont.c (fiber_switch, rb_cont_call): restore tracing status. + [Feature #4347] + + * test/ruby/test_continuation.rb: ditto. + Thu Aug 16 19:15:23 2012 Nobuyoshi Nakada <nobu@ruby-lang.org> * object.c (rb_class_initialize): forbid inheriting uninitialized @@ -929,6 +929,9 @@ rb_cont_call(int argc, VALUE *argv, VALUE contval) cont->argc = argc; cont->value = make_passing_arg(argc, argv); + /* restore `tracing' context. see [Feature #4347] */ + th->trace_running = cont->saved_thread.trace_running; + cont_restore_0(cont, &contval); return Qnil; /* unreachable */ } @@ -1317,6 +1320,10 @@ fiber_switch(VALUE fibval, int argc, VALUE *argv, int is_resume) if (is_resume) { fib->prev = rb_fiber_current(); } + else { + /* restore `tracing' context. see [Feature #4347] */ + th->trace_running = cont->saved_thread.trace_running; + } cont->argc = argc; cont->value = make_passing_arg(argc, argv); diff --git a/include/ruby/ruby.h b/include/ruby/ruby.h index be93f204f1..6f8ae89658 100644 --- a/include/ruby/ruby.h +++ b/include/ruby/ruby.h @@ -1408,24 +1408,15 @@ int ruby_native_thread_p(void); #define RUBY_EVENT_C_CALL 0x0020 #define RUBY_EVENT_C_RETURN 0x0040 #define RUBY_EVENT_RAISE 0x0080 -#define RUBY_EVENT_ALL 0xffff -#define RUBY_EVENT_VM 0x10000 +#define RUBY_EVENT_ALL 0x00ff #define RUBY_EVENT_SWITCH 0x20000 #define RUBY_EVENT_COVERAGE 0x40000 typedef unsigned int rb_event_flag_t; typedef void (*rb_event_hook_func_t)(rb_event_flag_t evflag, VALUE data, VALUE self, ID mid, VALUE klass); -typedef struct rb_event_hook_struct { - rb_event_flag_t flag; - rb_event_hook_func_t func; - VALUE data; - struct rb_event_hook_struct *next; -} rb_event_hook_t; - #define RB_EVENT_HOOKS_HAVE_CALLBACK_DATA 1 -void rb_add_event_hook(rb_event_hook_func_t func, rb_event_flag_t events, - VALUE data); +void rb_add_event_hook(rb_event_hook_func_t func, rb_event_flag_t events, VALUE data); int rb_remove_event_hook(rb_event_hook_func_t func); /* locale insensitive functions */ diff --git a/internal.h b/internal.h index 57b485d74f..c091a1c49e 100644 --- a/internal.h +++ b/internal.h @@ -247,7 +247,7 @@ struct timeval rb_time_timeval(VALUE); /* thread.c */ VALUE rb_obj_is_mutex(VALUE obj); -VALUE ruby_suppress_tracing(VALUE (*func)(VALUE, int), VALUE arg, int always); +VALUE rb_suppress_tracing(VALUE (*func)(VALUE), VALUE arg); void rb_thread_execute_interrupts(VALUE th); void rb_clear_trace_func(void); VALUE rb_get_coverages(void); @@ -5418,7 +5418,7 @@ e_option_supplied(struct parser_params *parser) } static VALUE -yycompile0(VALUE arg, int tracing) +yycompile0(VALUE arg) { int n; NODE *tree; @@ -5470,7 +5470,7 @@ yycompile(struct parser_params *parser, const char *f, int line) { ruby_sourcefile = ruby_strdup(f); ruby_sourceline = line - 1; - return (NODE *)ruby_suppress_tracing(yycompile0, (VALUE)parser, TRUE); + return (NODE *)rb_suppress_tracing(yycompile0, (VALUE)parser); } #endif /* !RIPPER */ diff --git a/test/ruby/test_continuation.rb b/test/ruby/test_continuation.rb index 5e5b78cc82..18289c5418 100644 --- a/test/ruby/test_continuation.rb +++ b/test/ruby/test_continuation.rb @@ -82,8 +82,12 @@ class TestContinuation < Test::Unit::TestCase cont = nil func = lambda do |*args| if orig_thread == Thread.current - @memo += 1 - cont.call(nil) + if cont + @memo += 1 + c = cont + cont = nil + c.call(nil) + end end end cont = callcc { |cc| cc } @@ -105,14 +109,18 @@ class TestContinuation < Test::Unit::TestCase def tracing_with_thread_set_trace_func cont = nil func = lambda do |*args| - @memo += 1 - cont.call(nil) + if cont + @memo += 1 + c = cont + cont = nil + c.call(nil) + end end cont = callcc { |cc| cc } if cont Thread.current.set_trace_func(func) else - Thread.current.set_trace_func(nil) + set_trace_func(nil) end end @@ -568,8 +568,6 @@ thread_create_core(VALUE thval, VALUE args, VALUE (*fn)(ANYARGS)) RBASIC(th->async_errinfo_mask_stack)->klass = 0; native_mutex_initialize(&th->interrupt_lock); - if (GET_VM()->event_hooks != NULL) - th->event_flags |= RUBY_EVENT_VM; /* kick thread */ st_insert(th->vm->living_threads, thval, (st_data_t) th->thread_id); @@ -83,6 +83,7 @@ VALUE ruby_vm_const_missing_count = 0; char ruby_vm_redefined_flag[BOP_LAST_]; rb_thread_t *ruby_current_thread = 0; rb_vm_t *ruby_current_vm = 0; +rb_event_flag_t ruby_vm_event_flags; static void thread_free(void *ptr); @@ -1462,14 +1463,7 @@ vm_mark_each_thread_func(st_data_t key, st_data_t value, st_data_t dummy) return ST_CONTINUE; } -static void -mark_event_hooks(rb_event_hook_t *hook) -{ - while (hook) { - rb_gc_mark(hook->data); - hook = hook->next; - } -} +void vm_trace_mark_event_hooks(rb_hook_list_t *hooks); void rb_vm_mark(void *ptr) @@ -1495,7 +1489,7 @@ rb_vm_mark(void *ptr) rb_mark_tbl(vm->loading_table); } - mark_event_hooks(vm->event_hooks); + vm_trace_mark_event_hooks(&vm->event_hooks); for (i = 0; i < RUBY_NSIG; i++) { if (vm->trap_list[i].cmd) @@ -1671,7 +1665,7 @@ rb_thread_mark(void *ptr) sizeof(th->machine_regs) / sizeof(VALUE)); } - mark_event_hooks(th->event_hooks); + vm_trace_mark_event_hooks(&th->event_hooks); } RUBY_MARK_LEAVE("thread"); @@ -290,6 +290,12 @@ struct rb_objspace; void rb_objspace_free(struct rb_objspace *); #endif +typedef struct rb_hook_list_struct { + struct rb_event_hook_struct *hooks; + rb_event_flag_t events; + int need_clean; +} rb_hook_list_t; + typedef struct rb_vm_struct { VALUE self; @@ -325,7 +331,7 @@ typedef struct rb_vm_struct { } trap_list[RUBY_NSIG]; /* hook */ - rb_event_hook_t *event_hooks; + rb_hook_list_t event_hooks; int src_encoding_index; @@ -513,9 +519,8 @@ typedef struct rb_thread_struct { VALUE stat_insn_usage; /* tracer */ - rb_event_hook_t *event_hooks; - rb_event_flag_t event_flags; - int tracing; + rb_hook_list_t event_hooks; + int trace_running; /* fiber */ VALUE fiber; @@ -764,6 +769,7 @@ int rb_autoloading_value(VALUE mod, ID id, VALUE* value); #if RUBY_VM_THREAD_MODEL == 2 extern rb_thread_t *ruby_current_thread; extern rb_vm_t *ruby_current_vm; +extern rb_event_flag_t ruby_vm_event_flags; #define GET_VM() ruby_current_vm #define GET_THREAD() ruby_current_thread @@ -817,9 +823,8 @@ void rb_threadptr_exec_event_hooks(rb_thread_t *th, rb_event_flag_t flag, VALUE self, ID id, VALUE klass); #define EXEC_EVENT_HOOK(th, flag, self, id, klass) do { \ - rb_event_flag_t wait_event__ = (th)->event_flags; \ - if (UNLIKELY(wait_event__)) { \ - if (wait_event__ & ((flag) | RUBY_EVENT_VM)) { \ + if (UNLIKELY(ruby_vm_event_flags & (flag))) { \ + if (((th)->event_hooks.events | (th)->vm->event_hooks.events) & (flag)) { \ rb_threadptr_exec_event_hooks((th), (flag), (self), (id), (klass)); \ } \ } \ diff --git a/vm_trace.c b/vm_trace.c index f608e1b2c0..23ad2a8ea1 100644 --- a/vm_trace.c +++ b/vm_trace.c @@ -30,61 +30,80 @@ /* (1) trace mechanisms */ -#define RUBY_EVENT_REMOVED 0x1000000 +typedef enum { + RUBY_HOOK_FLAG_SAFE = 0x01, + RUBY_HOOK_FLAG_DELETED = 0x02, +} rb_hook_flag_t; -enum { - EVENT_RUNNING_NOTHING, - EVENT_RUNNING_TRACE = 1, - EVENT_RUNNING_THREAD = 2, - EVENT_RUNNING_VM = 4, - EVENT_RUNNING_EVENT_MASK = EVENT_RUNNING_VM|EVENT_RUNNING_THREAD -}; +typedef struct rb_event_hook_struct { + rb_hook_flag_t hook_flags; + rb_event_flag_t events; + rb_event_hook_func_t func; + VALUE data; + struct rb_event_hook_struct *next; +} rb_event_hook_t; -static VALUE thread_suppress_tracing(rb_thread_t *th, int ev, VALUE (*func)(VALUE, int), VALUE arg, int always); +#define MAX_EVENT_NUM 32 -struct event_call_args { - rb_thread_t *th; - VALUE klass; - VALUE self; - VALUE proc; - ID id; - rb_event_flag_t event; -}; +static int ruby_event_flag_count[MAX_EVENT_NUM] = {0}; -static rb_event_hook_t * -alloc_event_hook(rb_event_hook_func_t func, rb_event_flag_t events, VALUE data) +/* Safe API. Callback will be called under PUSH_TAG() */ +void rb_add_event_hook(rb_event_hook_func_t func, rb_event_flag_t events, VALUE data); +int rb_remove_event_hook(rb_event_hook_func_t func); +void rb_thread_add_event_hook(VALUE thval, rb_event_hook_func_t func, rb_event_flag_t events, VALUE data); +int rb_thread_remove_event_hook(VALUE thval, rb_event_hook_func_t func); + +/* Raw API. Callback will be called without PUSH_TAG() */ +void rb_add_raw_event_hook(rb_event_hook_func_t func, rb_event_flag_t events, VALUE data); +int rb_remove_raw_event_hook(rb_event_hook_func_t func); +void rb_thread_add_raw_event_hook(VALUE thval, rb_event_hook_func_t func, rb_event_flag_t events, VALUE data); +int rb_thread_remove_raw_event_hook(VALUE thval, rb_event_hook_func_t func); + +/* called from vm.c */ + +void +vm_trace_mark_event_hooks(rb_hook_list_t *hooks) { - rb_event_hook_t *hook = ALLOC(rb_event_hook_t); - hook->func = func; - hook->flag = events; - hook->data = data; - return hook; + rb_event_hook_t *hook = hooks->hooks; + + while (hook) { + rb_gc_mark(hook->data); + hook = hook->next; + } } +/* ruby_vm_event_flags management */ + static void -thread_reset_event_flags(rb_thread_t *th) +recalc_add_ruby_vm_event_flags(rb_event_flag_t events) { - rb_event_hook_t *hook = th->event_hooks; - rb_event_flag_t flag = th->event_flags & RUBY_EVENT_VM; + int i; + ruby_vm_event_flags = 0; - while (hook) { - if (!(flag & RUBY_EVENT_REMOVED)) - flag |= hook->flag; - hook = hook->next; + for (i=0; i<MAX_EVENT_NUM; i++) { + if (events & (1 << i)) { + ruby_event_flag_count[i]++; + } + ruby_vm_event_flags |= ruby_event_flag_count[i] ? (1<<i) : 0; } - th->event_flags = flag; } static void -rb_threadptr_add_event_hook(rb_thread_t *th, - rb_event_hook_func_t func, rb_event_flag_t events, VALUE data) +recalc_remove_ruby_vm_event_flags(rb_event_flag_t events) { - rb_event_hook_t *hook = alloc_event_hook(func, events, data); - hook->next = th->event_hooks; - th->event_hooks = hook; - thread_reset_event_flags(th); + int i; + ruby_vm_event_flags = 0; + + for (i=0; i<MAX_EVENT_NUM; i++) { + if (events & (1 << i)) { + ruby_event_flag_count[i]--; + } + ruby_vm_event_flags |= ruby_event_flag_count[i] ? (1<<i) : 0; + } } +/* add/remove hooks */ + static rb_thread_t * thval2thread_t(VALUE thval) { @@ -93,180 +112,82 @@ thval2thread_t(VALUE thval) return th; } -void -rb_thread_add_event_hook(VALUE thval, - rb_event_hook_func_t func, rb_event_flag_t events, VALUE data) +static rb_event_hook_t * +alloc_event_hook(rb_event_hook_func_t func, rb_event_flag_t events, VALUE data, rb_hook_flag_t hook_flags) { - rb_threadptr_add_event_hook(thval2thread_t(thval), func, events, data); + rb_event_hook_t *hook = ALLOC(rb_event_hook_t); + hook->hook_flags = hook_flags; + hook->events = events; + hook->func = func; + hook->data = data; + return hook; } -static int -set_threads_event_flags_i(st_data_t key, st_data_t val, st_data_t flag) +static void +connect_event_hook(rb_hook_list_t *list, rb_event_hook_t *hook) { - VALUE thval = key; - rb_thread_t *th; - GetThreadPtr(thval, th); - - if (flag) { - th->event_flags |= RUBY_EVENT_VM; - } - else { - th->event_flags &= (~RUBY_EVENT_VM); - } - return ST_CONTINUE; + hook->next = list->hooks; + list->hooks = hook; + recalc_add_ruby_vm_event_flags(hook->events); + list->events |= hook->events; } static void -set_threads_event_flags(int flag) +rb_threadptr_add_event_hook(rb_thread_t *th, rb_event_hook_func_t func, rb_event_flag_t events, VALUE data, rb_hook_flag_t hook_flags) { - st_foreach(GET_VM()->living_threads, set_threads_event_flags_i, (st_data_t) flag); + rb_event_hook_t *hook = alloc_event_hook(func, events, data, hook_flags); + connect_event_hook(&th->event_hooks, hook); } -static inline int -exec_event_hooks(const rb_event_hook_t *hook, rb_event_flag_t flag, VALUE self, ID id, VALUE klass) +void +rb_thread_add_event_hook(VALUE thval, rb_event_hook_func_t func, rb_event_flag_t events, VALUE data) { - int removed = 0; - for (; hook; hook = hook->next) { - if (hook->flag & RUBY_EVENT_REMOVED) { - removed++; - continue; - } - if (flag & hook->flag) { - (*hook->func)(flag, hook->data, self, id, klass); - } - } - return removed; + rb_threadptr_add_event_hook(thval2thread_t(thval), func, events, data, RUBY_HOOK_FLAG_SAFE); } -static int remove_defered_event_hook(rb_event_hook_t **root); - -static VALUE -thread_exec_event_hooks(VALUE args, int running) +void +rb_add_event_hook(rb_event_hook_func_t func, rb_event_flag_t events, VALUE data) { - struct event_call_args *argp = (struct event_call_args *)args; - rb_thread_t *th = argp->th; - rb_event_flag_t flag = argp->event; - VALUE self = argp->self; - ID id = argp->id; - VALUE klass = argp->klass; - const rb_event_flag_t wait_event = th->event_flags; - int removed; - - if (self == rb_mRubyVMFrozenCore) return 0; - - if ((wait_event & flag) && !(running & EVENT_RUNNING_THREAD)) { - th->tracing |= EVENT_RUNNING_THREAD; - removed = exec_event_hooks(th->event_hooks, flag, self, id, klass); - th->tracing &= ~EVENT_RUNNING_THREAD; - if (removed) { - remove_defered_event_hook(&th->event_hooks); - } - } - if (wait_event & RUBY_EVENT_VM) { - if (th->vm->event_hooks == NULL) { - th->event_flags &= (~RUBY_EVENT_VM); - } - else if (!(running & EVENT_RUNNING_VM)) { - th->tracing |= EVENT_RUNNING_VM; - removed = exec_event_hooks(th->vm->event_hooks, flag, self, id, klass); - th->tracing &= ~EVENT_RUNNING_VM; - if (removed) { - remove_defered_event_hook(&th->vm->event_hooks); - } - } - } - return 0; + rb_event_hook_t *hook = alloc_event_hook(func, events, data, RUBY_HOOK_FLAG_SAFE); + connect_event_hook(&GET_VM()->event_hooks, hook); } void -rb_threadptr_exec_event_hooks(rb_thread_t *th, rb_event_flag_t flag, VALUE self, ID id, VALUE klass) +rb_thread_add_raw_event_hook(VALUE thval, rb_event_hook_func_t func, rb_event_flag_t events, VALUE data) { - const VALUE errinfo = th->errinfo; - struct event_call_args args; - args.th = th; - args.event = flag; - args.self = self; - args.id = id; - args.klass = klass; - args.proc = 0; - thread_suppress_tracing(th, EVENT_RUNNING_EVENT_MASK, thread_exec_event_hooks, (VALUE)&args, FALSE); - th->errinfo = errinfo; + rb_threadptr_add_event_hook(thval2thread_t(thval), func, events, data, 0); } void -rb_add_event_hook(rb_event_hook_func_t func, rb_event_flag_t events, VALUE data) +rb_add_raw_event_hook(rb_event_hook_func_t func, rb_event_flag_t events, VALUE data) { - rb_event_hook_t *hook = alloc_event_hook(func, events, data); - rb_vm_t *vm = GET_VM(); - - hook->next = vm->event_hooks; - vm->event_hooks = hook; - - set_threads_event_flags(1); + rb_event_hook_t *hook = alloc_event_hook(func, events, data, 0); + connect_event_hook(&GET_VM()->event_hooks, hook); } +/* if func is 0, then clear all funcs */ static int -defer_remove_event_hook(rb_event_hook_t *hook, rb_event_hook_func_t func) +remove_event_hook_by_func(rb_hook_list_t *list, rb_event_hook_func_t func) { + int ret = 0; + rb_event_hook_t *hook = list->hooks; + while (hook) { if (func == 0 || hook->func == func) { - hook->flag |= RUBY_EVENT_REMOVED; + hook->hook_flags |= RUBY_HOOK_FLAG_DELETED; + ret+=1; + list->need_clean++; } hook = hook->next; } - return -1; -} - -static int -remove_event_hook(rb_event_hook_t **root, rb_event_hook_func_t func) -{ - rb_event_hook_t *hook = *root, *next; - - while (hook) { - next = hook->next; - if (func == 0 || hook->func == func || (hook->flag & RUBY_EVENT_REMOVED)) { - *root = next; - xfree(hook); - } - else { - root = &hook->next; - } - hook = next; - } - return -1; -} - -static int -remove_defered_event_hook(rb_event_hook_t **root) -{ - rb_event_hook_t *hook = *root, *next; - while (hook) { - next = hook->next; - if (hook->flag & RUBY_EVENT_REMOVED) { - *root = next; - xfree(hook); - } - else { - root = &hook->next; - } - hook = next; - } - return -1; + return ret; } static int rb_threadptr_remove_event_hook(rb_thread_t *th, rb_event_hook_func_t func) { - int ret; - if (th->tracing & EVENT_RUNNING_THREAD) { - ret = defer_remove_event_hook(th->event_hooks, func); - } - else { - ret = remove_event_hook(&th->event_hooks, func); - } - thread_reset_event_flags(th); - return ret; + return remove_event_hook_by_func(&th->event_hooks, func); } int @@ -275,69 +196,167 @@ rb_thread_remove_event_hook(VALUE thval, rb_event_hook_func_t func) return rb_threadptr_remove_event_hook(thval2thread_t(thval), func); } -static rb_event_hook_t * -search_live_hook(rb_event_hook_t *hook) +int +rb_remove_event_hook(rb_event_hook_func_t func) { - while (hook) { - if (!(hook->flag & RUBY_EVENT_REMOVED)) - return hook; - hook = hook->next; - } - return NULL; + return remove_event_hook_by_func(&GET_VM()->event_hooks, func); } static int -running_vm_event_hooks(st_data_t key, st_data_t val, st_data_t data) +clear_trace_func_i(st_data_t key, st_data_t val, st_data_t flag) { - rb_thread_t *th = thval2thread_t((VALUE)key); - if (!(th->tracing & EVENT_RUNNING_VM)) return ST_CONTINUE; - *(rb_thread_t **)data = th; - return ST_STOP; + rb_thread_t *th; + GetThreadPtr((VALUE)key, th); + rb_threadptr_remove_event_hook(th, 0); + return ST_CONTINUE; } -static rb_thread_t * -vm_event_hooks_running_thread(rb_vm_t *vm) +void +rb_clear_trace_func(void) { - rb_thread_t *found = NULL; - st_foreach(vm->living_threads, running_vm_event_hooks, (st_data_t)&found); - return found; + st_foreach(GET_VM()->living_threads, clear_trace_func_i, (st_data_t) 0); + rb_remove_event_hook(0); } -int -rb_remove_event_hook(rb_event_hook_func_t func) +/* invoke hooks */ + +static void +clean_hooks(rb_hook_list_t *list) { - rb_vm_t *vm = GET_VM(); - rb_event_hook_t *hook = search_live_hook(vm->event_hooks); - int ret; + rb_event_hook_t *hook = list->hooks, *prev = 0; + + list->events = 0; + list->need_clean = 0; + + while (hook) { + if (hook->hook_flags & RUBY_HOOK_FLAG_DELETED) { + if (prev == 0) { + /* start of list */ + list->hooks = hook->next; + } + else { + prev->next = hook->next; + } - if (vm_event_hooks_running_thread(vm)) { - ret = defer_remove_event_hook(vm->event_hooks, func); + recalc_remove_ruby_vm_event_flags(hook->events); + xfree(hook); + goto next_iter; + } + else { + list->events |= hook->events; /* update active events */ + } + prev = hook; + next_iter: + hook = hook->next; } - else { - ret = remove_event_hook(&vm->event_hooks, func); +} + +static inline int +exec_hooks(rb_thread_t *th, rb_hook_list_t *list, rb_event_flag_t event, VALUE self, ID id, VALUE klass) +{ + rb_event_hook_t *hook; + int state; + volatile int raised; + + if (UNLIKELY(list->need_clean > 0)) { + clean_hooks(list); } - if (hook && !search_live_hook(vm->event_hooks)) { - set_threads_event_flags(0); + raised = rb_threadptr_reset_raised(th); + + hook = list->hooks; + + /* TODO: Support !RUBY_HOOK_FLAG_SAFE hooks */ + + TH_PUSH_TAG(th); + if ((state = TH_EXEC_TAG()) == 0) { + while (hook) { + if (LIKELY(!(hook->hook_flags & RUBY_HOOK_FLAG_DELETED)) && (event & hook->events)) { + (*hook->func)(event, hook->data, self, id, klass); + } + hook = hook->next; + } } + TH_POP_TAG(); - return ret; -} + if (raised) { + rb_threadptr_set_raised(th); + } -static int -clear_trace_func_i(st_data_t key, st_data_t val, st_data_t flag) -{ - rb_thread_t *th; - GetThreadPtr((VALUE)key, th); - rb_threadptr_remove_event_hook(th, 0); - return ST_CONTINUE; + return state; } void -rb_clear_trace_func(void) +rb_threadptr_exec_event_hooks(rb_thread_t *th, rb_event_flag_t event, VALUE self, ID id, VALUE klass) +{ + if (th->trace_running == 0 && + self != rb_mRubyVMFrozenCore /* skip special methods. TODO: remove it. */) { + int state; + int outer_state = th->state; + th->state = 0; + + th->trace_running = 1; + { + const VALUE errinfo = th->errinfo; + rb_hook_list_t *list; + + /* thread local traces */ + list = &th->event_hooks; + if (list->events & event) { + state = exec_hooks(th, list, event, self, id, klass); + if (state) goto terminate; + } + + /* vm global traces */ + list = &th->vm->event_hooks; + if (list->events & event) { + state = exec_hooks(th, list, event, self, id, klass); + if (state) goto terminate; + } + th->errinfo = errinfo; + } + terminate: + th->trace_running = 0; + + if (state) { + TH_JUMP_TAG(th, state); + } + th->state = outer_state; + } +} + +VALUE +rb_suppress_tracing(VALUE (*func)(VALUE), VALUE arg) { - st_foreach(GET_VM()->living_threads, clear_trace_func_i, (st_data_t) 0); - rb_remove_event_hook(0); + volatile int raised; + volatile int outer_state; + VALUE result = Qnil; + rb_thread_t *th = GET_THREAD(); + int state; + int tracing = th->trace_running; + + th->trace_running = 1; + raised = rb_threadptr_reset_raised(th); + outer_state = th->state; + th->state = 0; + + PUSH_TAG(); + if ((state = EXEC_TAG()) == 0) { + result = (*func)(arg); + } + POP_TAG(); + + if (raised) { + rb_threadptr_set_raised(th); + } + th->trace_running = tracing; + + if (state) { + JUMP_TAG(state); + } + + th->state = outer_state; + return result; } static void call_trace_func(rb_event_flag_t, VALUE data, VALUE self, ID id, VALUE klass); @@ -393,7 +412,6 @@ set_trace_func(VALUE obj, VALUE trace) rb_remove_event_hook(call_trace_func); if (NIL_P(trace)) { - GET_THREAD()->tracing = EVENT_RUNNING_NOTHING; return Qnil; } @@ -412,7 +430,7 @@ thread_add_trace_func(rb_thread_t *th, VALUE trace) rb_raise(rb_eTypeError, "trace_func needs to be Proc"); } - rb_threadptr_add_event_hook(th, call_trace_func, RUBY_EVENT_ALL, trace); + rb_threadptr_add_event_hook(th, call_trace_func, RUBY_EVENT_ALL, trace, RUBY_HOOK_FLAG_SAFE); } /* @@ -450,9 +468,9 @@ thread_set_trace_func_m(VALUE obj, VALUE trace) rb_threadptr_remove_event_hook(th, call_trace_func); if (NIL_P(trace)) { - th->tracing = EVENT_RUNNING_NOTHING; return Qnil; } + thread_add_trace_func(th, trace); return trace; } @@ -482,27 +500,27 @@ get_event_name(rb_event_flag_t event) } } -static VALUE -call_trace_proc(VALUE args, int tracing) +static void +call_trace_func(rb_event_flag_t event, VALUE proc, VALUE self, ID id, VALUE klass) { - struct event_call_args *p = (struct event_call_args *)args; const char *srcfile = rb_sourcefile(); - VALUE eventname = rb_str_new2(get_event_name(p->event)); + VALUE eventname = rb_str_new2(get_event_name(event)); VALUE filename = srcfile ? rb_str_new2(srcfile) : Qnil; VALUE argv[6]; int line = rb_sourceline(); - ID id = 0; - VALUE klass = 0; + rb_thread_t *th = GET_THREAD(); - if (p->klass != 0) { - id = p->id; - klass = p->klass; + if (klass != 0) { + id = id; + klass = klass; } else { - rb_thread_method_id_and_class(p->th, &id, &klass); + rb_thread_method_id_and_class(th, &id, &klass); } + if (id == ID_ALLOCATOR) - return Qnil; + return; + if (klass) { if (RB_TYPE_P(klass, T_ICLASS)) { klass = RBASIC(klass)->klass; @@ -516,69 +534,10 @@ call_trace_proc(VALUE args, int tracing) argv[1] = filename; argv[2] = INT2FIX(line); argv[3] = id ? ID2SYM(id) : Qnil; - argv[4] = (p->self && srcfile) ? rb_binding_new() : Qnil; + argv[4] = (self && srcfile) ? rb_binding_new() : Qnil; argv[5] = klass ? klass : Qnil; - return rb_proc_call_with_block(p->proc, 6, argv, Qnil); -} - -static void -call_trace_func(rb_event_flag_t event, VALUE proc, VALUE self, ID id, VALUE klass) -{ - struct event_call_args args; - - args.th = GET_THREAD(); - args.event = event; - args.proc = proc; - args.self = self; - args.id = id; - args.klass = klass; - ruby_suppress_tracing(call_trace_proc, (VALUE)&args, FALSE); -} - -VALUE -ruby_suppress_tracing(VALUE (*func)(VALUE, int), VALUE arg, int always) -{ - rb_thread_t *th = GET_THREAD(); - return thread_suppress_tracing(th, EVENT_RUNNING_TRACE, func, arg, always); -} - -static VALUE -thread_suppress_tracing(rb_thread_t *th, int ev, VALUE (*func)(VALUE, int), VALUE arg, int always) -{ - int state, tracing = th->tracing, running = tracing & ev; - volatile int raised; - volatile int outer_state; - VALUE result = Qnil; - - if (running == ev && !always) { - return Qnil; - } - else { - th->tracing |= ev; - } - - raised = rb_threadptr_reset_raised(th); - outer_state = th->state; - th->state = 0; - - PUSH_TAG(); - if ((state = EXEC_TAG()) == 0) { - result = (*func)(arg, running); - } - - if (raised) { - rb_threadptr_set_raised(th); - } - POP_TAG(); - - th->tracing = tracing; - if (state) { - JUMP_TAG(state); - } - th->state = outer_state; - - return result; + rb_proc_call_with_block(proc, 6, argv, Qnil); } /* (2-2) TracePoint API (not yet) */ |