* 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
This commit is contained in:
parent
9528358120
commit
4a4a702e61
44
ChangeLog
44
ChangeLog
@ -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>
|
Thu Aug 16 19:15:23 2012 Nobuyoshi Nakada <nobu@ruby-lang.org>
|
||||||
|
|
||||||
* object.c (rb_class_initialize): forbid inheriting uninitialized
|
* object.c (rb_class_initialize): forbid inheriting uninitialized
|
||||||
|
7
cont.c
7
cont.c
@ -929,6 +929,9 @@ rb_cont_call(int argc, VALUE *argv, VALUE contval)
|
|||||||
cont->argc = argc;
|
cont->argc = argc;
|
||||||
cont->value = make_passing_arg(argc, argv);
|
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);
|
cont_restore_0(cont, &contval);
|
||||||
return Qnil; /* unreachable */
|
return Qnil; /* unreachable */
|
||||||
}
|
}
|
||||||
@ -1317,6 +1320,10 @@ fiber_switch(VALUE fibval, int argc, VALUE *argv, int is_resume)
|
|||||||
if (is_resume) {
|
if (is_resume) {
|
||||||
fib->prev = rb_fiber_current();
|
fib->prev = rb_fiber_current();
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
/* restore `tracing' context. see [Feature #4347] */
|
||||||
|
th->trace_running = cont->saved_thread.trace_running;
|
||||||
|
}
|
||||||
|
|
||||||
cont->argc = argc;
|
cont->argc = argc;
|
||||||
cont->value = make_passing_arg(argc, argv);
|
cont->value = make_passing_arg(argc, argv);
|
||||||
|
@ -1408,24 +1408,15 @@ int ruby_native_thread_p(void);
|
|||||||
#define RUBY_EVENT_C_CALL 0x0020
|
#define RUBY_EVENT_C_CALL 0x0020
|
||||||
#define RUBY_EVENT_C_RETURN 0x0040
|
#define RUBY_EVENT_C_RETURN 0x0040
|
||||||
#define RUBY_EVENT_RAISE 0x0080
|
#define RUBY_EVENT_RAISE 0x0080
|
||||||
#define RUBY_EVENT_ALL 0xffff
|
#define RUBY_EVENT_ALL 0x00ff
|
||||||
#define RUBY_EVENT_VM 0x10000
|
|
||||||
#define RUBY_EVENT_SWITCH 0x20000
|
#define RUBY_EVENT_SWITCH 0x20000
|
||||||
#define RUBY_EVENT_COVERAGE 0x40000
|
#define RUBY_EVENT_COVERAGE 0x40000
|
||||||
|
|
||||||
typedef unsigned int rb_event_flag_t;
|
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 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
|
#define RB_EVENT_HOOKS_HAVE_CALLBACK_DATA 1
|
||||||
void rb_add_event_hook(rb_event_hook_func_t func, rb_event_flag_t events,
|
void rb_add_event_hook(rb_event_hook_func_t func, rb_event_flag_t events, VALUE data);
|
||||||
VALUE data);
|
|
||||||
int rb_remove_event_hook(rb_event_hook_func_t func);
|
int rb_remove_event_hook(rb_event_hook_func_t func);
|
||||||
|
|
||||||
/* locale insensitive functions */
|
/* locale insensitive functions */
|
||||||
|
@ -247,7 +247,7 @@ struct timeval rb_time_timeval(VALUE);
|
|||||||
|
|
||||||
/* thread.c */
|
/* thread.c */
|
||||||
VALUE rb_obj_is_mutex(VALUE obj);
|
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_thread_execute_interrupts(VALUE th);
|
||||||
void rb_clear_trace_func(void);
|
void rb_clear_trace_func(void);
|
||||||
VALUE rb_get_coverages(void);
|
VALUE rb_get_coverages(void);
|
||||||
|
4
parse.y
4
parse.y
@ -5418,7 +5418,7 @@ e_option_supplied(struct parser_params *parser)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static VALUE
|
static VALUE
|
||||||
yycompile0(VALUE arg, int tracing)
|
yycompile0(VALUE arg)
|
||||||
{
|
{
|
||||||
int n;
|
int n;
|
||||||
NODE *tree;
|
NODE *tree;
|
||||||
@ -5470,7 +5470,7 @@ yycompile(struct parser_params *parser, const char *f, int line)
|
|||||||
{
|
{
|
||||||
ruby_sourcefile = ruby_strdup(f);
|
ruby_sourcefile = ruby_strdup(f);
|
||||||
ruby_sourceline = line - 1;
|
ruby_sourceline = line - 1;
|
||||||
return (NODE *)ruby_suppress_tracing(yycompile0, (VALUE)parser, TRUE);
|
return (NODE *)rb_suppress_tracing(yycompile0, (VALUE)parser);
|
||||||
}
|
}
|
||||||
#endif /* !RIPPER */
|
#endif /* !RIPPER */
|
||||||
|
|
||||||
|
@ -82,8 +82,12 @@ class TestContinuation < Test::Unit::TestCase
|
|||||||
cont = nil
|
cont = nil
|
||||||
func = lambda do |*args|
|
func = lambda do |*args|
|
||||||
if orig_thread == Thread.current
|
if orig_thread == Thread.current
|
||||||
|
if cont
|
||||||
@memo += 1
|
@memo += 1
|
||||||
cont.call(nil)
|
c = cont
|
||||||
|
cont = nil
|
||||||
|
c.call(nil)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
cont = callcc { |cc| cc }
|
cont = callcc { |cc| cc }
|
||||||
@ -105,14 +109,18 @@ class TestContinuation < Test::Unit::TestCase
|
|||||||
def tracing_with_thread_set_trace_func
|
def tracing_with_thread_set_trace_func
|
||||||
cont = nil
|
cont = nil
|
||||||
func = lambda do |*args|
|
func = lambda do |*args|
|
||||||
|
if cont
|
||||||
@memo += 1
|
@memo += 1
|
||||||
cont.call(nil)
|
c = cont
|
||||||
|
cont = nil
|
||||||
|
c.call(nil)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
cont = callcc { |cc| cc }
|
cont = callcc { |cc| cc }
|
||||||
if cont
|
if cont
|
||||||
Thread.current.set_trace_func(func)
|
Thread.current.set_trace_func(func)
|
||||||
else
|
else
|
||||||
Thread.current.set_trace_func(nil)
|
set_trace_func(nil)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
2
thread.c
2
thread.c
@ -568,8 +568,6 @@ thread_create_core(VALUE thval, VALUE args, VALUE (*fn)(ANYARGS))
|
|||||||
RBASIC(th->async_errinfo_mask_stack)->klass = 0;
|
RBASIC(th->async_errinfo_mask_stack)->klass = 0;
|
||||||
|
|
||||||
native_mutex_initialize(&th->interrupt_lock);
|
native_mutex_initialize(&th->interrupt_lock);
|
||||||
if (GET_VM()->event_hooks != NULL)
|
|
||||||
th->event_flags |= RUBY_EVENT_VM;
|
|
||||||
|
|
||||||
/* kick thread */
|
/* kick thread */
|
||||||
st_insert(th->vm->living_threads, thval, (st_data_t) th->thread_id);
|
st_insert(th->vm->living_threads, thval, (st_data_t) th->thread_id);
|
||||||
|
14
vm.c
14
vm.c
@ -83,6 +83,7 @@ VALUE ruby_vm_const_missing_count = 0;
|
|||||||
char ruby_vm_redefined_flag[BOP_LAST_];
|
char ruby_vm_redefined_flag[BOP_LAST_];
|
||||||
rb_thread_t *ruby_current_thread = 0;
|
rb_thread_t *ruby_current_thread = 0;
|
||||||
rb_vm_t *ruby_current_vm = 0;
|
rb_vm_t *ruby_current_vm = 0;
|
||||||
|
rb_event_flag_t ruby_vm_event_flags;
|
||||||
|
|
||||||
static void thread_free(void *ptr);
|
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;
|
return ST_CONTINUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
void vm_trace_mark_event_hooks(rb_hook_list_t *hooks);
|
||||||
mark_event_hooks(rb_event_hook_t *hook)
|
|
||||||
{
|
|
||||||
while (hook) {
|
|
||||||
rb_gc_mark(hook->data);
|
|
||||||
hook = hook->next;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
rb_vm_mark(void *ptr)
|
rb_vm_mark(void *ptr)
|
||||||
@ -1495,7 +1489,7 @@ rb_vm_mark(void *ptr)
|
|||||||
rb_mark_tbl(vm->loading_table);
|
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++) {
|
for (i = 0; i < RUBY_NSIG; i++) {
|
||||||
if (vm->trap_list[i].cmd)
|
if (vm->trap_list[i].cmd)
|
||||||
@ -1671,7 +1665,7 @@ rb_thread_mark(void *ptr)
|
|||||||
sizeof(th->machine_regs) / sizeof(VALUE));
|
sizeof(th->machine_regs) / sizeof(VALUE));
|
||||||
}
|
}
|
||||||
|
|
||||||
mark_event_hooks(th->event_hooks);
|
vm_trace_mark_event_hooks(&th->event_hooks);
|
||||||
}
|
}
|
||||||
|
|
||||||
RUBY_MARK_LEAVE("thread");
|
RUBY_MARK_LEAVE("thread");
|
||||||
|
19
vm_core.h
19
vm_core.h
@ -290,6 +290,12 @@ struct rb_objspace;
|
|||||||
void rb_objspace_free(struct rb_objspace *);
|
void rb_objspace_free(struct rb_objspace *);
|
||||||
#endif
|
#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 {
|
typedef struct rb_vm_struct {
|
||||||
VALUE self;
|
VALUE self;
|
||||||
|
|
||||||
@ -325,7 +331,7 @@ typedef struct rb_vm_struct {
|
|||||||
} trap_list[RUBY_NSIG];
|
} trap_list[RUBY_NSIG];
|
||||||
|
|
||||||
/* hook */
|
/* hook */
|
||||||
rb_event_hook_t *event_hooks;
|
rb_hook_list_t event_hooks;
|
||||||
|
|
||||||
int src_encoding_index;
|
int src_encoding_index;
|
||||||
|
|
||||||
@ -513,9 +519,8 @@ typedef struct rb_thread_struct {
|
|||||||
VALUE stat_insn_usage;
|
VALUE stat_insn_usage;
|
||||||
|
|
||||||
/* tracer */
|
/* tracer */
|
||||||
rb_event_hook_t *event_hooks;
|
rb_hook_list_t event_hooks;
|
||||||
rb_event_flag_t event_flags;
|
int trace_running;
|
||||||
int tracing;
|
|
||||||
|
|
||||||
/* fiber */
|
/* fiber */
|
||||||
VALUE fiber;
|
VALUE fiber;
|
||||||
@ -764,6 +769,7 @@ int rb_autoloading_value(VALUE mod, ID id, VALUE* value);
|
|||||||
#if RUBY_VM_THREAD_MODEL == 2
|
#if RUBY_VM_THREAD_MODEL == 2
|
||||||
extern rb_thread_t *ruby_current_thread;
|
extern rb_thread_t *ruby_current_thread;
|
||||||
extern rb_vm_t *ruby_current_vm;
|
extern rb_vm_t *ruby_current_vm;
|
||||||
|
extern rb_event_flag_t ruby_vm_event_flags;
|
||||||
|
|
||||||
#define GET_VM() ruby_current_vm
|
#define GET_VM() ruby_current_vm
|
||||||
#define GET_THREAD() ruby_current_thread
|
#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);
|
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 { \
|
#define EXEC_EVENT_HOOK(th, flag, self, id, klass) do { \
|
||||||
rb_event_flag_t wait_event__ = (th)->event_flags; \
|
if (UNLIKELY(ruby_vm_event_flags & (flag))) { \
|
||||||
if (UNLIKELY(wait_event__)) { \
|
if (((th)->event_hooks.events | (th)->vm->event_hooks.events) & (flag)) { \
|
||||||
if (wait_event__ & ((flag) | RUBY_EVENT_VM)) { \
|
|
||||||
rb_threadptr_exec_event_hooks((th), (flag), (self), (id), (klass)); \
|
rb_threadptr_exec_event_hooks((th), (flag), (self), (id), (klass)); \
|
||||||
} \
|
} \
|
||||||
} \
|
} \
|
||||||
|
563
vm_trace.c
563
vm_trace.c
@ -30,60 +30,79 @@
|
|||||||
|
|
||||||
/* (1) trace mechanisms */
|
/* (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 {
|
typedef struct rb_event_hook_struct {
|
||||||
EVENT_RUNNING_NOTHING,
|
rb_hook_flag_t hook_flags;
|
||||||
EVENT_RUNNING_TRACE = 1,
|
rb_event_flag_t events;
|
||||||
EVENT_RUNNING_THREAD = 2,
|
rb_event_hook_func_t func;
|
||||||
EVENT_RUNNING_VM = 4,
|
VALUE data;
|
||||||
EVENT_RUNNING_EVENT_MASK = EVENT_RUNNING_VM|EVENT_RUNNING_THREAD
|
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 {
|
static int ruby_event_flag_count[MAX_EVENT_NUM] = {0};
|
||||||
rb_thread_t *th;
|
|
||||||
VALUE klass;
|
|
||||||
VALUE self;
|
|
||||||
VALUE proc;
|
|
||||||
ID id;
|
|
||||||
rb_event_flag_t event;
|
|
||||||
};
|
|
||||||
|
|
||||||
static rb_event_hook_t *
|
/* Safe API. Callback will be called under PUSH_TAG() */
|
||||||
alloc_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);
|
||||||
|
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);
|
rb_event_hook_t *hook = hooks->hooks;
|
||||||
hook->func = func;
|
|
||||||
hook->flag = events;
|
|
||||||
hook->data = data;
|
|
||||||
return hook;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
thread_reset_event_flags(rb_thread_t *th)
|
|
||||||
{
|
|
||||||
rb_event_hook_t *hook = th->event_hooks;
|
|
||||||
rb_event_flag_t flag = th->event_flags & RUBY_EVENT_VM;
|
|
||||||
|
|
||||||
while (hook) {
|
while (hook) {
|
||||||
if (!(flag & RUBY_EVENT_REMOVED))
|
rb_gc_mark(hook->data);
|
||||||
flag |= hook->flag;
|
|
||||||
hook = hook->next;
|
hook = hook->next;
|
||||||
}
|
}
|
||||||
th->event_flags = flag;
|
}
|
||||||
|
|
||||||
|
/* ruby_vm_event_flags management */
|
||||||
|
|
||||||
|
static void
|
||||||
|
recalc_add_ruby_vm_event_flags(rb_event_flag_t events)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
rb_threadptr_add_event_hook(rb_thread_t *th,
|
recalc_remove_ruby_vm_event_flags(rb_event_flag_t events)
|
||||||
rb_event_hook_func_t func, rb_event_flag_t events, VALUE data)
|
|
||||||
{
|
{
|
||||||
rb_event_hook_t *hook = alloc_event_hook(func, events, data);
|
int i;
|
||||||
hook->next = th->event_hooks;
|
ruby_vm_event_flags = 0;
|
||||||
th->event_hooks = hook;
|
|
||||||
thread_reset_event_flags(th);
|
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 *
|
static rb_thread_t *
|
||||||
thval2thread_t(VALUE thval)
|
thval2thread_t(VALUE thval)
|
||||||
@ -93,180 +112,82 @@ thval2thread_t(VALUE thval)
|
|||||||
return th;
|
return th;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
static rb_event_hook_t *
|
||||||
rb_thread_add_event_hook(VALUE thval,
|
alloc_event_hook(rb_event_hook_func_t func, rb_event_flag_t events, VALUE data, rb_hook_flag_t hook_flags)
|
||||||
rb_event_hook_func_t func, rb_event_flag_t events, VALUE data)
|
|
||||||
{
|
{
|
||||||
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;
|
||||||
static int
|
hook->func = func;
|
||||||
set_threads_event_flags_i(st_data_t key, st_data_t val, st_data_t flag)
|
hook->data = data;
|
||||||
{
|
return 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
set_threads_event_flags(int flag)
|
connect_event_hook(rb_hook_list_t *list, rb_event_hook_t *hook)
|
||||||
{
|
{
|
||||||
st_foreach(GET_VM()->living_threads, set_threads_event_flags_i, (st_data_t) flag);
|
hook->next = list->hooks;
|
||||||
|
list->hooks = hook;
|
||||||
|
recalc_add_ruby_vm_event_flags(hook->events);
|
||||||
|
list->events |= hook->events;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int
|
static void
|
||||||
exec_event_hooks(const rb_event_hook_t *hook, rb_event_flag_t flag, VALUE self, ID id, VALUE klass)
|
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)
|
||||||
{
|
{
|
||||||
int removed = 0;
|
rb_event_hook_t *hook = alloc_event_hook(func, events, data, hook_flags);
|
||||||
for (; hook; hook = hook->next) {
|
connect_event_hook(&th->event_hooks, hook);
|
||||||
if (hook->flag & RUBY_EVENT_REMOVED) {
|
|
||||||
removed++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (flag & hook->flag) {
|
|
||||||
(*hook->func)(flag, hook->data, self, id, klass);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return removed;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int remove_defered_event_hook(rb_event_hook_t **root);
|
|
||||||
|
|
||||||
static VALUE
|
|
||||||
thread_exec_event_hooks(VALUE args, int running)
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
rb_threadptr_exec_event_hooks(rb_thread_t *th, rb_event_flag_t flag, VALUE self, ID id, VALUE klass)
|
rb_thread_add_event_hook(VALUE thval, rb_event_hook_func_t func, rb_event_flag_t events, VALUE data)
|
||||||
{
|
{
|
||||||
const VALUE errinfo = th->errinfo;
|
rb_threadptr_add_event_hook(thval2thread_t(thval), func, events, data, RUBY_HOOK_FLAG_SAFE);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
rb_add_event_hook(rb_event_hook_func_t func, rb_event_flag_t events, VALUE data)
|
rb_add_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_event_hook_t *hook = alloc_event_hook(func, events, data, RUBY_HOOK_FLAG_SAFE);
|
||||||
rb_vm_t *vm = GET_VM();
|
connect_event_hook(&GET_VM()->event_hooks, hook);
|
||||||
|
|
||||||
hook->next = vm->event_hooks;
|
|
||||||
vm->event_hooks = hook;
|
|
||||||
|
|
||||||
set_threads_event_flags(1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
void
|
||||||
defer_remove_event_hook(rb_event_hook_t *hook, rb_event_hook_func_t func)
|
rb_thread_add_raw_event_hook(VALUE thval, rb_event_hook_func_t func, rb_event_flag_t events, VALUE data)
|
||||||
{
|
{
|
||||||
|
rb_threadptr_add_event_hook(thval2thread_t(thval), func, events, data, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
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, 0);
|
||||||
|
connect_event_hook(&GET_VM()->event_hooks, hook);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* if func is 0, then clear all funcs */
|
||||||
|
static int
|
||||||
|
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) {
|
while (hook) {
|
||||||
if (func == 0 || hook->func == func) {
|
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;
|
hook = hook->next;
|
||||||
}
|
}
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
return ret;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
rb_threadptr_remove_event_hook(rb_thread_t *th, rb_event_hook_func_t func)
|
rb_threadptr_remove_event_hook(rb_thread_t *th, rb_event_hook_func_t func)
|
||||||
{
|
{
|
||||||
int ret;
|
return remove_event_hook_by_func(&th->event_hooks, func);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
@ -275,53 +196,10 @@ rb_thread_remove_event_hook(VALUE thval, rb_event_hook_func_t func)
|
|||||||
return rb_threadptr_remove_event_hook(thval2thread_t(thval), func);
|
return rb_threadptr_remove_event_hook(thval2thread_t(thval), func);
|
||||||
}
|
}
|
||||||
|
|
||||||
static rb_event_hook_t *
|
|
||||||
search_live_hook(rb_event_hook_t *hook)
|
|
||||||
{
|
|
||||||
while (hook) {
|
|
||||||
if (!(hook->flag & RUBY_EVENT_REMOVED))
|
|
||||||
return hook;
|
|
||||||
hook = hook->next;
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
running_vm_event_hooks(st_data_t key, st_data_t val, st_data_t data)
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
static rb_thread_t *
|
|
||||||
vm_event_hooks_running_thread(rb_vm_t *vm)
|
|
||||||
{
|
|
||||||
rb_thread_t *found = NULL;
|
|
||||||
st_foreach(vm->living_threads, running_vm_event_hooks, (st_data_t)&found);
|
|
||||||
return found;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
int
|
||||||
rb_remove_event_hook(rb_event_hook_func_t func)
|
rb_remove_event_hook(rb_event_hook_func_t func)
|
||||||
{
|
{
|
||||||
rb_vm_t *vm = GET_VM();
|
return remove_event_hook_by_func(&GET_VM()->event_hooks, func);
|
||||||
rb_event_hook_t *hook = search_live_hook(vm->event_hooks);
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
if (vm_event_hooks_running_thread(vm)) {
|
|
||||||
ret = defer_remove_event_hook(vm->event_hooks, func);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
ret = remove_event_hook(&vm->event_hooks, func);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hook && !search_live_hook(vm->event_hooks)) {
|
|
||||||
set_threads_event_flags(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
@ -340,6 +218,147 @@ rb_clear_trace_func(void)
|
|||||||
rb_remove_event_hook(0);
|
rb_remove_event_hook(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* invoke hooks */
|
||||||
|
|
||||||
|
static void
|
||||||
|
clean_hooks(rb_hook_list_t *list)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
|
||||||
|
if (raised) {
|
||||||
|
rb_threadptr_set_raised(th);
|
||||||
|
}
|
||||||
|
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
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);
|
static void call_trace_func(rb_event_flag_t, VALUE data, VALUE self, ID id, VALUE klass);
|
||||||
|
|
||||||
/* (2-1) set_trace_func (old API) */
|
/* (2-1) set_trace_func (old API) */
|
||||||
@ -393,7 +412,6 @@ set_trace_func(VALUE obj, VALUE trace)
|
|||||||
rb_remove_event_hook(call_trace_func);
|
rb_remove_event_hook(call_trace_func);
|
||||||
|
|
||||||
if (NIL_P(trace)) {
|
if (NIL_P(trace)) {
|
||||||
GET_THREAD()->tracing = EVENT_RUNNING_NOTHING;
|
|
||||||
return Qnil;
|
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_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);
|
rb_threadptr_remove_event_hook(th, call_trace_func);
|
||||||
|
|
||||||
if (NIL_P(trace)) {
|
if (NIL_P(trace)) {
|
||||||
th->tracing = EVENT_RUNNING_NOTHING;
|
|
||||||
return Qnil;
|
return Qnil;
|
||||||
}
|
}
|
||||||
|
|
||||||
thread_add_trace_func(th, trace);
|
thread_add_trace_func(th, trace);
|
||||||
return trace;
|
return trace;
|
||||||
}
|
}
|
||||||
@ -482,27 +500,27 @@ get_event_name(rb_event_flag_t event)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static VALUE
|
static void
|
||||||
call_trace_proc(VALUE args, int tracing)
|
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();
|
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 filename = srcfile ? rb_str_new2(srcfile) : Qnil;
|
||||||
VALUE argv[6];
|
VALUE argv[6];
|
||||||
int line = rb_sourceline();
|
int line = rb_sourceline();
|
||||||
ID id = 0;
|
rb_thread_t *th = GET_THREAD();
|
||||||
VALUE klass = 0;
|
|
||||||
|
|
||||||
if (p->klass != 0) {
|
if (klass != 0) {
|
||||||
id = p->id;
|
id = id;
|
||||||
klass = p->klass;
|
klass = klass;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
rb_thread_method_id_and_class(p->th, &id, &klass);
|
rb_thread_method_id_and_class(th, &id, &klass);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (id == ID_ALLOCATOR)
|
if (id == ID_ALLOCATOR)
|
||||||
return Qnil;
|
return;
|
||||||
|
|
||||||
if (klass) {
|
if (klass) {
|
||||||
if (RB_TYPE_P(klass, T_ICLASS)) {
|
if (RB_TYPE_P(klass, T_ICLASS)) {
|
||||||
klass = RBASIC(klass)->klass;
|
klass = RBASIC(klass)->klass;
|
||||||
@ -516,69 +534,10 @@ call_trace_proc(VALUE args, int tracing)
|
|||||||
argv[1] = filename;
|
argv[1] = filename;
|
||||||
argv[2] = INT2FIX(line);
|
argv[2] = INT2FIX(line);
|
||||||
argv[3] = id ? ID2SYM(id) : Qnil;
|
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;
|
argv[5] = klass ? klass : Qnil;
|
||||||
|
|
||||||
return rb_proc_call_with_block(p->proc, 6, argv, Qnil);
|
rb_proc_call_with_block(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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* (2-2) TracePoint API (not yet) */
|
/* (2-2) TracePoint API (not yet) */
|
||||||
|
Loading…
x
Reference in New Issue
Block a user