mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
GVL Instrumentation API: add STARTED and EXITED events
[Feature #18339] After experimenting with the initial version of the API I figured there is a need for an exit event to cleanup instrumentation data. e.g. if you record data in a {thread_id -> data} table, you need to free associated data when a thread goes away.
This commit is contained in:
parent
20d4168250
commit
b6c1e1158d
Notes:
git
2022-06-17 16:08:50 +09:00
7 changed files with 57 additions and 31 deletions
|
@ -2,22 +2,30 @@
|
|||
#include "ruby/atomic.h"
|
||||
#include "ruby/thread.h"
|
||||
|
||||
static rb_atomic_t acquire_enter_count = 0;
|
||||
static rb_atomic_t acquire_exit_count = 0;
|
||||
static rb_atomic_t release_count = 0;
|
||||
static rb_atomic_t started_count = 0;
|
||||
static rb_atomic_t ready_count = 0;
|
||||
static rb_atomic_t resumed_count = 0;
|
||||
static rb_atomic_t suspended_count = 0;
|
||||
static rb_atomic_t exited_count = 0;
|
||||
|
||||
void
|
||||
ex_callback(rb_event_flag_t event, const rb_internal_thread_event_data_t *event_data, void *user_data)
|
||||
{
|
||||
switch(event) {
|
||||
case RUBY_INTERNAL_THREAD_EVENT_STARTED:
|
||||
RUBY_ATOMIC_INC(started_count);
|
||||
break;
|
||||
case RUBY_INTERNAL_THREAD_EVENT_READY:
|
||||
RUBY_ATOMIC_INC(acquire_enter_count);
|
||||
RUBY_ATOMIC_INC(ready_count);
|
||||
break;
|
||||
case RUBY_INTERNAL_THREAD_EVENT_RESUMED:
|
||||
RUBY_ATOMIC_INC(acquire_exit_count);
|
||||
RUBY_ATOMIC_INC(resumed_count);
|
||||
break;
|
||||
case RUBY_INTERNAL_THREAD_EVENT_SUSPENDED:
|
||||
RUBY_ATOMIC_INC(release_count);
|
||||
RUBY_ATOMIC_INC(suspended_count);
|
||||
break;
|
||||
case RUBY_INTERNAL_THREAD_EVENT_EXITED:
|
||||
RUBY_ATOMIC_INC(exited_count);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -27,19 +35,23 @@ static rb_internal_thread_event_hook_t * single_hook = NULL;
|
|||
static VALUE
|
||||
thread_counters(VALUE thread)
|
||||
{
|
||||
VALUE array = rb_ary_new2(3);
|
||||
rb_ary_push(array, UINT2NUM(acquire_enter_count));
|
||||
rb_ary_push(array, UINT2NUM(acquire_exit_count));
|
||||
rb_ary_push(array, UINT2NUM(release_count));
|
||||
VALUE array = rb_ary_new2(5);
|
||||
rb_ary_push(array, UINT2NUM(started_count));
|
||||
rb_ary_push(array, UINT2NUM(ready_count));
|
||||
rb_ary_push(array, UINT2NUM(resumed_count));
|
||||
rb_ary_push(array, UINT2NUM(suspended_count));
|
||||
rb_ary_push(array, UINT2NUM(exited_count));
|
||||
return array;
|
||||
}
|
||||
|
||||
static VALUE
|
||||
thread_reset_counters(VALUE thread)
|
||||
{
|
||||
RUBY_ATOMIC_SET(acquire_enter_count, 0);
|
||||
RUBY_ATOMIC_SET(acquire_exit_count, 0);
|
||||
RUBY_ATOMIC_SET(release_count, 0);
|
||||
RUBY_ATOMIC_SET(started_count, 0);
|
||||
RUBY_ATOMIC_SET(ready_count, 0);
|
||||
RUBY_ATOMIC_SET(resumed_count, 0);
|
||||
RUBY_ATOMIC_SET(suspended_count, 0);
|
||||
RUBY_ATOMIC_SET(exited_count, 0);
|
||||
return Qtrue;
|
||||
}
|
||||
|
||||
|
@ -48,7 +60,11 @@ thread_register_callback(VALUE thread)
|
|||
{
|
||||
single_hook = rb_internal_thread_add_event_hook(
|
||||
*ex_callback,
|
||||
RUBY_INTERNAL_THREAD_EVENT_READY | RUBY_INTERNAL_THREAD_EVENT_RESUMED | RUBY_INTERNAL_THREAD_EVENT_SUSPENDED,
|
||||
RUBY_INTERNAL_THREAD_EVENT_STARTED |
|
||||
RUBY_INTERNAL_THREAD_EVENT_READY |
|
||||
RUBY_INTERNAL_THREAD_EVENT_RESUMED |
|
||||
RUBY_INTERNAL_THREAD_EVENT_SUSPENDED |
|
||||
RUBY_INTERNAL_THREAD_EVENT_EXITED,
|
||||
NULL
|
||||
);
|
||||
|
||||
|
|
|
@ -190,10 +190,12 @@ void *rb_nogvl(void *(*func)(void *), void *data1,
|
|||
*/
|
||||
#define RUBY_CALL_WO_GVL_FLAG_SKIP_CHECK_INTS_
|
||||
|
||||
#define RUBY_INTERNAL_THREAD_EVENT_READY 0x01 /** acquiring GVL */
|
||||
#define RUBY_INTERNAL_THREAD_EVENT_RESUMED 0x02 /** acquired GVL */
|
||||
#define RUBY_INTERNAL_THREAD_EVENT_SUSPENDED 0x04 /** released GVL */
|
||||
#define RUBY_INTERNAL_THREAD_EVENT_MASK 0x07 /** All Thread events */
|
||||
#define RUBY_INTERNAL_THREAD_EVENT_STARTED 1 << 0 /** thread started */
|
||||
#define RUBY_INTERNAL_THREAD_EVENT_READY 1 << 1 /** acquiring GVL */
|
||||
#define RUBY_INTERNAL_THREAD_EVENT_RESUMED 1 << 2 /** acquired GVL */
|
||||
#define RUBY_INTERNAL_THREAD_EVENT_SUSPENDED 1 << 3 /** released GVL */
|
||||
#define RUBY_INTERNAL_THREAD_EVENT_EXITED 1 << 4 /** thread terminated */
|
||||
#define RUBY_INTERNAL_THREAD_EVENT_MASK 0xff /** All Thread events */
|
||||
|
||||
typedef void rb_internal_thread_event_data_t; // for future extension.
|
||||
|
||||
|
|
|
@ -44,8 +44,10 @@ class TestThreadInstrumentation < Test::Unit::TestCase
|
|||
counters = Marshal.load(read_pipe)
|
||||
read_pipe.close
|
||||
counters.each do |c|
|
||||
assert_predicate c,:nonzero?, "Call counters: #{counters.inspect}"
|
||||
assert_predicate c, :nonzero?, "Call counters: #{counters.inspect}"
|
||||
end
|
||||
|
||||
assert_equal counters.first, counters.last # exited as many times as we entered
|
||||
ensure
|
||||
Bug::ThreadInstrumentation::unregister_callback
|
||||
end
|
||||
|
|
1
thread.c
1
thread.c
|
@ -631,7 +631,6 @@ thread_do_start(rb_thread_t *th)
|
|||
}
|
||||
|
||||
void rb_ec_clear_current_thread_trace_func(const rb_execution_context_t *ec);
|
||||
#define thread_sched_to_dead thread_sched_to_waiting
|
||||
|
||||
static int
|
||||
thread_start_func_2(rb_thread_t *th, VALUE *stack_start)
|
||||
|
|
|
@ -30,6 +30,8 @@ thread_sched_to_waiting(struct rb_thread_sched *sched)
|
|||
{
|
||||
}
|
||||
|
||||
#define thread_sched_to_dead thread_sched_to_waiting
|
||||
|
||||
static void
|
||||
thread_sched_yield(struct rb_thread_sched *sched, rb_thread_t *th)
|
||||
{
|
||||
|
|
|
@ -109,6 +109,8 @@ struct rb_internal_thread_event_hook {
|
|||
static rb_internal_thread_event_hook_t *rb_internal_thread_event_hooks = NULL;
|
||||
static pthread_rwlock_t rb_internal_thread_event_hooks_rw_lock = PTHREAD_RWLOCK_INITIALIZER;
|
||||
|
||||
#define RB_INTERNAL_THREAD_HOOK(event) if (rb_internal_thread_event_hooks) { rb_thread_execute_hooks(event); }
|
||||
|
||||
rb_internal_thread_event_hook_t *
|
||||
rb_internal_thread_add_event_hook(rb_internal_thread_event_callback callback, rb_event_flag_t internal_event, void *user_data)
|
||||
{
|
||||
|
@ -377,10 +379,7 @@ thread_sched_to_ready_common(struct rb_thread_sched *sched, rb_thread_t *th)
|
|||
static void
|
||||
thread_sched_to_running_common(struct rb_thread_sched *sched, rb_thread_t *th)
|
||||
{
|
||||
if (rb_internal_thread_event_hooks) {
|
||||
rb_thread_execute_hooks(RUBY_INTERNAL_THREAD_EVENT_READY);
|
||||
}
|
||||
|
||||
RB_INTERNAL_THREAD_HOOK(RUBY_INTERNAL_THREAD_EVENT_READY);
|
||||
if (sched->running) {
|
||||
VM_ASSERT(th->unblock.func == 0 &&
|
||||
"we must not be in ubf_list and GVL readyq at the same time");
|
||||
|
@ -412,9 +411,7 @@ thread_sched_to_running_common(struct rb_thread_sched *sched, rb_thread_t *th)
|
|||
// ready -> running
|
||||
sched->running = th;
|
||||
|
||||
if (rb_internal_thread_event_hooks) {
|
||||
rb_thread_execute_hooks(RUBY_INTERNAL_THREAD_EVENT_RESUMED);
|
||||
}
|
||||
RB_INTERNAL_THREAD_HOOK(RUBY_INTERNAL_THREAD_EVENT_RESUMED);
|
||||
|
||||
if (!sched->timer) {
|
||||
if (!designate_timer_thread(sched) && !ubf_threads_empty()) {
|
||||
|
@ -434,10 +431,6 @@ thread_sched_to_running(struct rb_thread_sched *sched, rb_thread_t *th)
|
|||
static rb_thread_t *
|
||||
thread_sched_to_waiting_common(struct rb_thread_sched *sched)
|
||||
{
|
||||
if (rb_internal_thread_event_hooks) {
|
||||
rb_thread_execute_hooks(RUBY_INTERNAL_THREAD_EVENT_SUSPENDED);
|
||||
}
|
||||
|
||||
rb_thread_t *next;
|
||||
sched->running = NULL;
|
||||
next = ccan_list_top(&sched->readyq, rb_thread_t, sched.node.readyq);
|
||||
|
@ -449,11 +442,19 @@ thread_sched_to_waiting_common(struct rb_thread_sched *sched)
|
|||
static void
|
||||
thread_sched_to_waiting(struct rb_thread_sched *sched)
|
||||
{
|
||||
RB_INTERNAL_THREAD_HOOK(RUBY_INTERNAL_THREAD_EVENT_SUSPENDED);
|
||||
rb_native_mutex_lock(&sched->lock);
|
||||
thread_sched_to_waiting_common(sched);
|
||||
rb_native_mutex_unlock(&sched->lock);
|
||||
}
|
||||
|
||||
static void
|
||||
thread_sched_to_dead(struct rb_thread_sched *sched)
|
||||
{
|
||||
thread_sched_to_waiting(sched);
|
||||
RB_INTERNAL_THREAD_HOOK(RUBY_INTERNAL_THREAD_EVENT_EXITED);
|
||||
}
|
||||
|
||||
static void
|
||||
thread_sched_yield(struct rb_thread_sched *sched, rb_thread_t *th)
|
||||
{
|
||||
|
@ -1173,6 +1174,8 @@ thread_start_func_1(void *th_ptr)
|
|||
|
||||
native_thread_init(th->nt);
|
||||
|
||||
RB_INTERNAL_THREAD_HOOK(RUBY_INTERNAL_THREAD_EVENT_STARTED);
|
||||
|
||||
/* run */
|
||||
#if defined USE_NATIVE_THREAD_INIT
|
||||
thread_start_func_2(th, th->ec->machine.stack_start);
|
||||
|
|
|
@ -135,6 +135,8 @@ thread_sched_to_waiting(struct rb_thread_sched *sched)
|
|||
ReleaseMutex(sched->lock);
|
||||
}
|
||||
|
||||
#define thread_sched_to_dead thread_sched_to_waiting
|
||||
|
||||
static void
|
||||
thread_sched_yield(struct rb_thread_sched *sched, rb_thread_t *th)
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue