ruby-changes:24664
From: ko1 <ko1@a...>
Date: Thu, 16 Aug 2012 20:41:38 +0900 (JST)
Subject: [ruby-changes:24664] ko1:r36715 (trunk): * vm_trace.c, vm_core.h: simplify tracing mechanism.
ko1 2012-08-16 20:41:24 +0900 (Thu, 16 Aug 2012) New Revision: 36715 http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=rev&revision=36715 Log: * 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. Modified files: trunk/ChangeLog trunk/cont.c trunk/include/ruby/ruby.h trunk/internal.h trunk/parse.y trunk/test/ruby/test_continuation.rb trunk/thread.c trunk/vm.c trunk/vm_core.h trunk/vm_trace.c Index: include/ruby/ruby.h =================================================================== --- include/ruby/ruby.h (revision 36714) +++ include/ruby/ruby.h (revision 36715) @@ -1408,24 +1408,15 @@ #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 */ Index: ChangeLog =================================================================== --- ChangeLog (revision 36714) +++ ChangeLog (revision 36715) @@ -1,3 +1,47 @@ +Thu Aug 16 19:54:24 2012 Koichi Sasada <ko1@a...> + + * 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@r...> * object.c (rb_class_initialize): forbid inheriting uninitialized Index: vm_core.h =================================================================== --- vm_core.h (revision 36714) +++ vm_core.h (revision 36715) @@ -290,6 +290,12 @@ 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 @@ } 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 @@ 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 @@ #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 @@ 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)); \ } \ } \ Index: thread.c =================================================================== --- thread.c (revision 36714) +++ thread.c (revision 36715) @@ -568,8 +568,6 @@ 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); Index: parse.y =================================================================== --- parse.y (revision 36714) +++ parse.y (revision 36715) @@ -5418,7 +5418,7 @@ } static VALUE -yycompile0(VALUE arg, int tracing) +yycompile0(VALUE arg) { int n; NODE *tree; @@ -5470,7 +5470,7 @@ { 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 */ Index: vm_trace.c =================================================================== --- vm_trace.c (revision 36714) +++ vm_trace.c (revision 36715) @@ -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 @@ 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; + return ret; } 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; -} - -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 @@ 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; - if (vm_event_hooks_running_thread(vm)) { - ret = defer_remove_event_hook(vm->event_hooks, func); + 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; + } + + 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); + } + + return state; } -static int -clear_trace_func_i(st_data_t key, st_data_t val, st_data_t flag) +void +rb_threadptr_exec_event_hooks(rb_thread_t *th, rb_event_flag_t event, VALUE self, ID id, VALUE klass) { - rb_thread_t *th; - GetThreadPtr((VALUE)key, th); - rb_threadptr_remove_event_hook(th, 0); - return ST_CONTINUE; + 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; + } } -void -rb_clear_trace_func(void) +VA (... truncated) -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/