Skip to content

Commit

Permalink
fix local TP memory leak
Browse files Browse the repository at this point in the history
It free `rb_hook_list_t` itself if needed. To recognize the
need, this patch introduced `rb_hook_list_t::is_local` flag.

This patch is succession of #4652
  • Loading branch information
ko1 committed Dec 13, 2021
1 parent c81d7d7 commit 1d86099
Show file tree
Hide file tree
Showing 3 changed files with 24 additions and 17 deletions.
5 changes: 2 additions & 3 deletions iseq.c
Original file line number Diff line number Diff line change
Expand Up @@ -3359,6 +3359,7 @@ iseq_add_local_tracepoint(const rb_iseq_t *iseq, rb_event_flag_t turnon_events,
if (n > 0) {
if (iseq->aux.exec.local_hooks == NULL) {
((rb_iseq_t *)iseq)->aux.exec.local_hooks = RB_ZALLOC(rb_hook_list_t);
iseq->aux.exec.local_hooks->is_local = true;
}
rb_hook_list_connect_tracepoint((VALUE)iseq, iseq->aux.exec.local_hooks, tpval, target_line);
}
Expand Down Expand Up @@ -3413,9 +3414,7 @@ iseq_remove_local_tracepoint(const rb_iseq_t *iseq, VALUE tpval)
local_events = iseq->aux.exec.local_hooks->events;

if (local_events == 0) {
if (iseq->aux.exec.local_hooks->running == 0) {
rb_hook_list_free(iseq->aux.exec.local_hooks);
}
rb_hook_list_free(iseq->aux.exec.local_hooks);
((rb_iseq_t *)iseq)->aux.exec.local_hooks = NULL;
}

Expand Down
3 changes: 2 additions & 1 deletion vm_core.h
Original file line number Diff line number Diff line change
Expand Up @@ -609,8 +609,9 @@ void rb_objspace_call_finalizer(struct rb_objspace *);
typedef struct rb_hook_list_struct {
struct rb_event_hook_struct *hooks;
rb_event_flag_t events;
unsigned int need_clean;
unsigned int running;
bool need_clean;
bool is_local;
} rb_hook_list_t;


Expand Down
33 changes: 20 additions & 13 deletions vm_trace.c
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,11 @@ static void clean_hooks(const rb_execution_context_t *ec, rb_hook_list_t *list);
void
rb_hook_list_free(rb_hook_list_t *hooks)
{
hooks->need_clean = TRUE;
clean_hooks(GET_EC(), hooks);
hooks->need_clean = true;

if (hooks->running == 0) {
clean_hooks(GET_EC(), hooks);
}
}

/* ruby_vm_event_flags management */
Expand Down Expand Up @@ -196,9 +199,10 @@ clean_hooks(const rb_execution_context_t *ec, rb_hook_list_t *list)
{
rb_event_hook_t *hook, **nextp = &list->hooks;
VM_ASSERT(list->need_clean == TRUE);
VM_ASSERT(list->running == 0);

list->events = 0;
list->need_clean = FALSE;
list->need_clean = false;

while ((hook = *nextp) != 0) {
if (hook->hook_flags & RUBY_EVENT_HOOK_FLAG_DELETED) {
Expand All @@ -211,19 +215,21 @@ clean_hooks(const rb_execution_context_t *ec, rb_hook_list_t *list)
}
}

if (list == rb_ec_ractor_hooks(ec)) {
/* global events */
update_global_event_hook(list->events);
if (list->is_local) {
if (list->events == 0) {
/* local events */
ruby_xfree(list);
}
}
else {
/* local events */
update_global_event_hook(list->events);
}
}

static void
clean_hooks_check(const rb_execution_context_t *ec, rb_hook_list_t *list)
{
if (UNLIKELY(list->need_clean != FALSE)) {
if (UNLIKELY(list->need_clean)) {
if (list->running == 0) {
clean_hooks(ec, list);
}
Expand All @@ -246,7 +252,7 @@ remove_event_hook(const rb_execution_context_t *ec, const rb_thread_t *filter_th
if (data == Qundef || hook->data == data) {
hook->hook_flags |= RUBY_EVENT_HOOK_FLAG_DELETED;
ret+=1;
list->need_clean = TRUE;
list->need_clean = true;
}
}
}
Expand Down Expand Up @@ -1244,10 +1250,11 @@ disable_local_event_iseq_i(VALUE target, VALUE iseq_p, VALUE tpval)
rb_hook_list_t *hooks = def->body.bmethod.hooks;
VM_ASSERT(hooks != NULL);
rb_hook_list_remove_tracepoint(hooks, tpval);
if (hooks->running == 0) {

if (hooks->events == 0) {
rb_hook_list_free(def->body.bmethod.hooks);
def->body.bmethod.hooks = NULL;
}
def->body.bmethod.hooks = NULL;
}
return ST_CONTINUE;
}
Expand Down Expand Up @@ -1296,9 +1303,9 @@ rb_hook_list_remove_tracepoint(rb_hook_list_t *list, VALUE tpval)
while (hook) {
if (hook->data == tpval) {
hook->hook_flags |= RUBY_EVENT_HOOK_FLAG_DELETED;
list->need_clean = TRUE;
list->need_clean = true;
}
else {
else if ((hook->hook_flags & RUBY_EVENT_HOOK_FLAG_DELETED) == 0) {
events |= hook->events;
}
hook = hook->next;
Expand Down

0 comments on commit 1d86099

Please sign in to comment.