Bug #20103
openrb_internal_thread_remove_event_hook() hangs when called from within a Thread hook
Description
Problem¶
The Ruby process hangs when rb_internal_thread_remove_event_hook()
is called from within a Thread hook registered by rb_internal_thread_add_event_hook()
.
The thread is waiting for the thread event execution lock (pthread_rwlock_wrlock(&rb_internal_thread_event_hooks_rw_lock))
), which is acquired by its caller, rb_thread_execute_hooks()
.
https://github.com/ruby/ruby/blob/e81a5453e3c76c4348da042d86debde7689254fe/thread_pthread.c#L3323
This situation would occur when one wants to register a oneshot hook that performs something on a Thread for only a single time. The hook would remove itself after its main procedure.
In my case, I'm doing a timer_create(3) on each pthread for profiling purposes.
Expected behavior¶
The process won't hang, or at least print some kind of warning.
I think there some options here:
- Discourage this kind of definition. Thread hooks should not add/remove other Thread hooks, or otherwise do so through other async methods, such as postponed jobs.
- Find a way to make this work. Maybe inner calls to add/remove_event_hook() can share locks with the outer call, but I'm not sure if that's possible as I'm not familiar with pthread_rwlocks.
Repro¶
#include "ruby.h"
#include "ruby/thread.h"
rb_internal_thread_event_hook_t *hook;
void
event_hook_callback(rb_event_flag_t flag, const rb_internal_thread_event_data_t *data, void *custom_data)
{
rb_internal_thread_remove_event_hook(hook); // hangs
}
RUBY_FUNC_EXPORTED void
Init_mycext(void)
{
hook = rb_internal_thread_add_event_hook(
event_hook_callback,
RUBY_INTERNAL_THREAD_EVENT_RESUMED,
NULL
);
}
ruby -rmycext -e 'Thread.new { loop { 1 + 1 } }'
No data to display