[Feature #18339] GVL Instrumentation API

Ref: https://bugs.ruby-lang.org/issues/18339

Design:

- This tries to minimize the overhead when no hook is registered.
  It should only incur an extra unsynchronized boolean check.
- The hook list is protected with a read-write lock as to cause
  contention when some hooks are registered.
- The hooks MUST be thread safe, and MUST NOT call into Ruby as they
  are executed outside the GVL.
- It's simply a noop on Windows.

API:

```
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);
bool rb_internal_thread_remove_event_hook(rb_internal_thread_event_hook_t * hook);
```

You can subscribe to 3 events:

  - READY: called right before attempting to acquire the GVL
  - RESUMED: called right after successfully acquiring the GVL
  - SUSPENDED: called right after releasing the GVL.

The hooks MUST be threadsafe, as they are executed outside of the GVL, they also MUST NOT call any Ruby API.
This commit is contained in:
Jean Boussier 2022-01-27 17:12:22 +01:00
parent d142eff658
commit 9125374726
Notes: git 2022-06-03 22:13:53 +09:00
7 changed files with 468 additions and 1 deletions

View File

@ -0,0 +1,164 @@
# AUTOGENERATED DEPENDENCIES START
instrumentation.o: $(RUBY_EXTCONF_H)
instrumentation.o: $(arch_hdrdir)/ruby/config.h
instrumentation.o: $(hdrdir)/ruby/assert.h
instrumentation.o: $(hdrdir)/ruby/atomic.h
instrumentation.o: $(hdrdir)/ruby/backward.h
instrumentation.o: $(hdrdir)/ruby/backward/2/assume.h
instrumentation.o: $(hdrdir)/ruby/backward/2/attributes.h
instrumentation.o: $(hdrdir)/ruby/backward/2/bool.h
instrumentation.o: $(hdrdir)/ruby/backward/2/inttypes.h
instrumentation.o: $(hdrdir)/ruby/backward/2/limits.h
instrumentation.o: $(hdrdir)/ruby/backward/2/long_long.h
instrumentation.o: $(hdrdir)/ruby/backward/2/stdalign.h
instrumentation.o: $(hdrdir)/ruby/backward/2/stdarg.h
instrumentation.o: $(hdrdir)/ruby/defines.h
instrumentation.o: $(hdrdir)/ruby/intern.h
instrumentation.o: $(hdrdir)/ruby/internal/abi.h
instrumentation.o: $(hdrdir)/ruby/internal/anyargs.h
instrumentation.o: $(hdrdir)/ruby/internal/arithmetic.h
instrumentation.o: $(hdrdir)/ruby/internal/arithmetic/char.h
instrumentation.o: $(hdrdir)/ruby/internal/arithmetic/double.h
instrumentation.o: $(hdrdir)/ruby/internal/arithmetic/fixnum.h
instrumentation.o: $(hdrdir)/ruby/internal/arithmetic/gid_t.h
instrumentation.o: $(hdrdir)/ruby/internal/arithmetic/int.h
instrumentation.o: $(hdrdir)/ruby/internal/arithmetic/intptr_t.h
instrumentation.o: $(hdrdir)/ruby/internal/arithmetic/long.h
instrumentation.o: $(hdrdir)/ruby/internal/arithmetic/long_long.h
instrumentation.o: $(hdrdir)/ruby/internal/arithmetic/mode_t.h
instrumentation.o: $(hdrdir)/ruby/internal/arithmetic/off_t.h
instrumentation.o: $(hdrdir)/ruby/internal/arithmetic/pid_t.h
instrumentation.o: $(hdrdir)/ruby/internal/arithmetic/short.h
instrumentation.o: $(hdrdir)/ruby/internal/arithmetic/size_t.h
instrumentation.o: $(hdrdir)/ruby/internal/arithmetic/st_data_t.h
instrumentation.o: $(hdrdir)/ruby/internal/arithmetic/uid_t.h
instrumentation.o: $(hdrdir)/ruby/internal/assume.h
instrumentation.o: $(hdrdir)/ruby/internal/attr/alloc_size.h
instrumentation.o: $(hdrdir)/ruby/internal/attr/artificial.h
instrumentation.o: $(hdrdir)/ruby/internal/attr/cold.h
instrumentation.o: $(hdrdir)/ruby/internal/attr/const.h
instrumentation.o: $(hdrdir)/ruby/internal/attr/constexpr.h
instrumentation.o: $(hdrdir)/ruby/internal/attr/deprecated.h
instrumentation.o: $(hdrdir)/ruby/internal/attr/diagnose_if.h
instrumentation.o: $(hdrdir)/ruby/internal/attr/enum_extensibility.h
instrumentation.o: $(hdrdir)/ruby/internal/attr/error.h
instrumentation.o: $(hdrdir)/ruby/internal/attr/flag_enum.h
instrumentation.o: $(hdrdir)/ruby/internal/attr/forceinline.h
instrumentation.o: $(hdrdir)/ruby/internal/attr/format.h
instrumentation.o: $(hdrdir)/ruby/internal/attr/maybe_unused.h
instrumentation.o: $(hdrdir)/ruby/internal/attr/noalias.h
instrumentation.o: $(hdrdir)/ruby/internal/attr/nodiscard.h
instrumentation.o: $(hdrdir)/ruby/internal/attr/noexcept.h
instrumentation.o: $(hdrdir)/ruby/internal/attr/noinline.h
instrumentation.o: $(hdrdir)/ruby/internal/attr/nonnull.h
instrumentation.o: $(hdrdir)/ruby/internal/attr/noreturn.h
instrumentation.o: $(hdrdir)/ruby/internal/attr/pure.h
instrumentation.o: $(hdrdir)/ruby/internal/attr/restrict.h
instrumentation.o: $(hdrdir)/ruby/internal/attr/returns_nonnull.h
instrumentation.o: $(hdrdir)/ruby/internal/attr/warning.h
instrumentation.o: $(hdrdir)/ruby/internal/attr/weakref.h
instrumentation.o: $(hdrdir)/ruby/internal/cast.h
instrumentation.o: $(hdrdir)/ruby/internal/compiler_is.h
instrumentation.o: $(hdrdir)/ruby/internal/compiler_is/apple.h
instrumentation.o: $(hdrdir)/ruby/internal/compiler_is/clang.h
instrumentation.o: $(hdrdir)/ruby/internal/compiler_is/gcc.h
instrumentation.o: $(hdrdir)/ruby/internal/compiler_is/intel.h
instrumentation.o: $(hdrdir)/ruby/internal/compiler_is/msvc.h
instrumentation.o: $(hdrdir)/ruby/internal/compiler_is/sunpro.h
instrumentation.o: $(hdrdir)/ruby/internal/compiler_since.h
instrumentation.o: $(hdrdir)/ruby/internal/config.h
instrumentation.o: $(hdrdir)/ruby/internal/constant_p.h
instrumentation.o: $(hdrdir)/ruby/internal/core.h
instrumentation.o: $(hdrdir)/ruby/internal/core/rarray.h
instrumentation.o: $(hdrdir)/ruby/internal/core/rbasic.h
instrumentation.o: $(hdrdir)/ruby/internal/core/rbignum.h
instrumentation.o: $(hdrdir)/ruby/internal/core/rclass.h
instrumentation.o: $(hdrdir)/ruby/internal/core/rdata.h
instrumentation.o: $(hdrdir)/ruby/internal/core/rfile.h
instrumentation.o: $(hdrdir)/ruby/internal/core/rhash.h
instrumentation.o: $(hdrdir)/ruby/internal/core/robject.h
instrumentation.o: $(hdrdir)/ruby/internal/core/rregexp.h
instrumentation.o: $(hdrdir)/ruby/internal/core/rstring.h
instrumentation.o: $(hdrdir)/ruby/internal/core/rstruct.h
instrumentation.o: $(hdrdir)/ruby/internal/core/rtypeddata.h
instrumentation.o: $(hdrdir)/ruby/internal/ctype.h
instrumentation.o: $(hdrdir)/ruby/internal/dllexport.h
instrumentation.o: $(hdrdir)/ruby/internal/dosish.h
instrumentation.o: $(hdrdir)/ruby/internal/error.h
instrumentation.o: $(hdrdir)/ruby/internal/eval.h
instrumentation.o: $(hdrdir)/ruby/internal/event.h
instrumentation.o: $(hdrdir)/ruby/internal/fl_type.h
instrumentation.o: $(hdrdir)/ruby/internal/gc.h
instrumentation.o: $(hdrdir)/ruby/internal/glob.h
instrumentation.o: $(hdrdir)/ruby/internal/globals.h
instrumentation.o: $(hdrdir)/ruby/internal/has/attribute.h
instrumentation.o: $(hdrdir)/ruby/internal/has/builtin.h
instrumentation.o: $(hdrdir)/ruby/internal/has/c_attribute.h
instrumentation.o: $(hdrdir)/ruby/internal/has/cpp_attribute.h
instrumentation.o: $(hdrdir)/ruby/internal/has/declspec_attribute.h
instrumentation.o: $(hdrdir)/ruby/internal/has/extension.h
instrumentation.o: $(hdrdir)/ruby/internal/has/feature.h
instrumentation.o: $(hdrdir)/ruby/internal/has/warning.h
instrumentation.o: $(hdrdir)/ruby/internal/intern/array.h
instrumentation.o: $(hdrdir)/ruby/internal/intern/bignum.h
instrumentation.o: $(hdrdir)/ruby/internal/intern/class.h
instrumentation.o: $(hdrdir)/ruby/internal/intern/compar.h
instrumentation.o: $(hdrdir)/ruby/internal/intern/complex.h
instrumentation.o: $(hdrdir)/ruby/internal/intern/cont.h
instrumentation.o: $(hdrdir)/ruby/internal/intern/dir.h
instrumentation.o: $(hdrdir)/ruby/internal/intern/enum.h
instrumentation.o: $(hdrdir)/ruby/internal/intern/enumerator.h
instrumentation.o: $(hdrdir)/ruby/internal/intern/error.h
instrumentation.o: $(hdrdir)/ruby/internal/intern/eval.h
instrumentation.o: $(hdrdir)/ruby/internal/intern/file.h
instrumentation.o: $(hdrdir)/ruby/internal/intern/gc.h
instrumentation.o: $(hdrdir)/ruby/internal/intern/hash.h
instrumentation.o: $(hdrdir)/ruby/internal/intern/io.h
instrumentation.o: $(hdrdir)/ruby/internal/intern/load.h
instrumentation.o: $(hdrdir)/ruby/internal/intern/marshal.h
instrumentation.o: $(hdrdir)/ruby/internal/intern/numeric.h
instrumentation.o: $(hdrdir)/ruby/internal/intern/object.h
instrumentation.o: $(hdrdir)/ruby/internal/intern/parse.h
instrumentation.o: $(hdrdir)/ruby/internal/intern/proc.h
instrumentation.o: $(hdrdir)/ruby/internal/intern/process.h
instrumentation.o: $(hdrdir)/ruby/internal/intern/random.h
instrumentation.o: $(hdrdir)/ruby/internal/intern/range.h
instrumentation.o: $(hdrdir)/ruby/internal/intern/rational.h
instrumentation.o: $(hdrdir)/ruby/internal/intern/re.h
instrumentation.o: $(hdrdir)/ruby/internal/intern/ruby.h
instrumentation.o: $(hdrdir)/ruby/internal/intern/select.h
instrumentation.o: $(hdrdir)/ruby/internal/intern/select/largesize.h
instrumentation.o: $(hdrdir)/ruby/internal/intern/signal.h
instrumentation.o: $(hdrdir)/ruby/internal/intern/sprintf.h
instrumentation.o: $(hdrdir)/ruby/internal/intern/string.h
instrumentation.o: $(hdrdir)/ruby/internal/intern/struct.h
instrumentation.o: $(hdrdir)/ruby/internal/intern/thread.h
instrumentation.o: $(hdrdir)/ruby/internal/intern/time.h
instrumentation.o: $(hdrdir)/ruby/internal/intern/variable.h
instrumentation.o: $(hdrdir)/ruby/internal/intern/vm.h
instrumentation.o: $(hdrdir)/ruby/internal/interpreter.h
instrumentation.o: $(hdrdir)/ruby/internal/iterator.h
instrumentation.o: $(hdrdir)/ruby/internal/memory.h
instrumentation.o: $(hdrdir)/ruby/internal/method.h
instrumentation.o: $(hdrdir)/ruby/internal/module.h
instrumentation.o: $(hdrdir)/ruby/internal/newobj.h
instrumentation.o: $(hdrdir)/ruby/internal/rgengc.h
instrumentation.o: $(hdrdir)/ruby/internal/scan_args.h
instrumentation.o: $(hdrdir)/ruby/internal/special_consts.h
instrumentation.o: $(hdrdir)/ruby/internal/static_assert.h
instrumentation.o: $(hdrdir)/ruby/internal/stdalign.h
instrumentation.o: $(hdrdir)/ruby/internal/stdbool.h
instrumentation.o: $(hdrdir)/ruby/internal/symbol.h
instrumentation.o: $(hdrdir)/ruby/internal/value.h
instrumentation.o: $(hdrdir)/ruby/internal/value_type.h
instrumentation.o: $(hdrdir)/ruby/internal/variable.h
instrumentation.o: $(hdrdir)/ruby/internal/warning_push.h
instrumentation.o: $(hdrdir)/ruby/internal/xmalloc.h
instrumentation.o: $(hdrdir)/ruby/missing.h
instrumentation.o: $(hdrdir)/ruby/ruby.h
instrumentation.o: $(hdrdir)/ruby/st.h
instrumentation.o: $(hdrdir)/ruby/subst.h
instrumentation.o: $(hdrdir)/ruby/thread.h
instrumentation.o: $(hdrdir)/ruby/thread_native.h
instrumentation.o: instrumentation.c
# AUTOGENERATED DEPENDENCIES END

View File

@ -0,0 +1,2 @@
# frozen_string_literal: false
create_makefile("-test-/thread/instrumentation")

View File

@ -0,0 +1,95 @@
#include "ruby/ruby.h"
#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;
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_READY:
RUBY_ATOMIC_INC(acquire_enter_count);
break;
case RUBY_INTERNAL_THREAD_EVENT_RESUMED:
RUBY_ATOMIC_INC(acquire_exit_count);
break;
case RUBY_INTERNAL_THREAD_EVENT_SUSPENDED:
RUBY_ATOMIC_INC(release_count);
break;
}
}
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));
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);
return Qtrue;
}
static VALUE
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,
NULL
);
return Qnil;
}
static VALUE
thread_unregister_callback(VALUE thread)
{
if (single_hook) {
rb_internal_thread_remove_event_hook(single_hook);
single_hook = NULL;
}
return Qnil;
}
static VALUE
thread_register_and_unregister_callback(VALUE thread)
{
rb_internal_thread_event_hook_t * hooks[5];
for (int i = 0; i < 5; i++) {
hooks[i] = rb_internal_thread_add_event_hook(*ex_callback, RUBY_INTERNAL_THREAD_EVENT_READY, NULL);
}
if (!rb_internal_thread_remove_event_hook(hooks[4])) return Qfalse;
if (!rb_internal_thread_remove_event_hook(hooks[0])) return Qfalse;
if (!rb_internal_thread_remove_event_hook(hooks[3])) return Qfalse;
if (!rb_internal_thread_remove_event_hook(hooks[2])) return Qfalse;
if (!rb_internal_thread_remove_event_hook(hooks[1])) return Qfalse;
return Qtrue;
}
void
Init_instrumentation(void)
{
VALUE mBug = rb_define_module("Bug");
VALUE klass = rb_define_module_under(mBug, "ThreadInstrumentation");
rb_define_singleton_method(klass, "counters", thread_counters, 0);
rb_define_singleton_method(klass, "reset_counters", thread_reset_counters, 0);
rb_define_singleton_method(klass, "register_callback", thread_register_callback, 0);
rb_define_singleton_method(klass, "unregister_callback", thread_unregister_callback, 0);
rb_define_singleton_method(klass, "register_and_unregister_callbacks", thread_register_and_unregister_callback, 0);
}

View File

@ -190,6 +190,44 @@ 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 */
typedef void rb_internal_thread_event_data_t; // for future extension.
typedef void (*rb_internal_thread_event_callback)(rb_event_flag_t event,
const rb_internal_thread_event_data_t *event_data,
void *user_data);
typedef struct rb_internal_thread_event_hook rb_internal_thread_event_hook_t;
/**
* Registers a thread event hook function.
*
* @param[in] func A callback.
* @param[in] events A set of events that `func` should run.
* @param[in] data Passed as-is to `func`.
* @return An opaque pointer to the hook, to unregister it later.
* @note This functionality is a noop on Windows.
* @warning This function MUST not be called from a thread event callback.
*/
rb_internal_thread_event_hook_t *rb_internal_thread_add_event_hook(
rb_internal_thread_event_callback func, rb_event_flag_t events,
void *data);
/**
* Unregister the passed hook.
*
* @param[in] hook. The hook to unregister.
* @return Wether the hook was found and unregistered.
* @note This functionality is a noop on Windows.
* @warning This function MUST not be called from a thread event callback.
*/
bool rb_internal_thread_remove_event_hook(
rb_internal_thread_event_hook_t * hook);
RBIMPL_SYMBOL_EXPORT_END()
#endif /* RUBY_THREAD_H */

View File

@ -0,0 +1,50 @@
# frozen_string_literal: false
class TestThreadInstrumentation < Test::Unit::TestCase
def setup
pend("TODO: No windows support yet") if /mswin|mingw|bccwin/ =~ RUBY_PLATFORM
end
def test_thread_instrumentation
require '-test-/thread/instrumentation'
Bug::ThreadInstrumentation.reset_counters
Bug::ThreadInstrumentation::register_callback
begin
threads = 5.times.map { Thread.new { sleep 0.05; 1 + 1; sleep 0.02 } }
threads.each(&:join)
Bug::ThreadInstrumentation.counters.each do |c|
assert_predicate c,:nonzero?
end
ensure
Bug::ThreadInstrumentation::unregister_callback
end
end
def test_thread_instrumentation_fork_safe
skip "No fork()" unless Process.respond_to?(:fork)
require '-test-/thread/instrumentation'
Bug::ThreadInstrumentation::register_callback
begin
pid = fork do
Bug::ThreadInstrumentation.reset_counters
threads = 5.times.map { Thread.new { sleep 0.05; 1 + 1; sleep 0.02 } }
threads.each(&:join)
Bug::ThreadInstrumentation.counters.each do |c|
assert_predicate c,:nonzero?
end
end
_, status = Process.wait2(pid)
assert_predicate status, :success?
ensure
Bug::ThreadInstrumentation::unregister_callback
end
end
def test_thread_instrumentation_unregister
require '-test-/thread/instrumentation'
assert Bug::ThreadInstrumentation::register_and_unregister_callbacks
end
end

View File

@ -98,6 +98,95 @@
# endif
#endif
struct rb_internal_thread_event_hook {
rb_internal_thread_event_callback callback;
rb_event_flag_t event;
void *user_data;
struct rb_internal_thread_event_hook *next;
};
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;
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)
{
rb_internal_thread_event_hook_t *hook = ALLOC_N(rb_internal_thread_event_hook_t, 1);
hook->callback = callback;
hook->user_data = user_data;
hook->event = internal_event;
int r;
if ((r = pthread_rwlock_wrlock(&rb_internal_thread_event_hooks_rw_lock))) {
rb_bug_errno("pthread_rwlock_wrlock", r);
}
hook->next = rb_internal_thread_event_hooks;
ATOMIC_PTR_EXCHANGE(rb_internal_thread_event_hooks, hook);
if ((r = pthread_rwlock_unlock(&rb_internal_thread_event_hooks_rw_lock))) {
rb_bug_errno("pthread_rwlock_unlock", r);
}
return hook;
}
bool
rb_internal_thread_remove_event_hook(rb_internal_thread_event_hook_t * hook)
{
int r;
if ((r = pthread_rwlock_wrlock(&rb_internal_thread_event_hooks_rw_lock))) {
rb_bug_errno("pthread_rwlock_wrlock", r);
}
bool success = FALSE;
if (rb_internal_thread_event_hooks == hook) {
ATOMIC_PTR_EXCHANGE(rb_internal_thread_event_hooks, hook->next);
success = TRUE;
} else {
rb_internal_thread_event_hook_t *h = rb_internal_thread_event_hooks;
do {
if (h->next == hook) {
h->next = hook->next;
success = TRUE;
break;
}
} while ((h = h->next));
}
if ((r = pthread_rwlock_unlock(&rb_internal_thread_event_hooks_rw_lock))) {
rb_bug_errno("pthread_rwlock_unlock", r);
}
if (success) {
ruby_xfree(hook);
}
return success;
}
static void
rb_thread_execute_hooks(rb_event_flag_t event)
{
int r;
if ((r = pthread_rwlock_rdlock(&rb_internal_thread_event_hooks_rw_lock))) {
rb_bug_errno("pthread_rwlock_rdlock", r);
}
if (rb_internal_thread_event_hooks) {
rb_internal_thread_event_hook_t *h = rb_internal_thread_event_hooks;
do {
if (h->event & event) {
(*h->callback)(event, NULL, h->user_data);
}
} while((h = h->next));
}
if ((r = pthread_rwlock_unlock(&rb_internal_thread_event_hooks_rw_lock))) {
rb_bug_errno("pthread_rwlock_unlock", r);
}
}
enum rtimer_state {
/* alive, after timer_create: */
RTIMER_DISARM,
@ -295,6 +384,10 @@ thread_sched_to_running_common(struct rb_thread_sched *sched, rb_thread_t *th)
// waiting -> ready
thread_sched_to_ready_common(sched, th);
if (rb_internal_thread_event_hooks) {
rb_thread_execute_hooks(RUBY_INTERNAL_THREAD_EVENT_READY);
}
// wait for running chance
do {
if (!sched->timer) {
@ -319,6 +412,10 @@ 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);
}
if (!sched->timer) {
if (!designate_timer_thread(sched) && !ubf_threads_empty()) {
rb_thread_wakeup_timer_thread(-1);
@ -337,6 +434,10 @@ 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);
@ -687,9 +788,14 @@ native_thread_init(struct rb_native_thread *nt)
void
Init_native_thread(rb_thread_t *main_th)
{
int r;
if ((r = pthread_rwlock_init(&rb_internal_thread_event_hooks_rw_lock, NULL))) {
rb_bug_errno("pthread_rwlock_init", r);
}
#if defined(HAVE_PTHREAD_CONDATTR_SETCLOCK)
if (condattr_monotonic) {
int r = pthread_condattr_init(condattr_monotonic);
r = pthread_condattr_init(condattr_monotonic);
if (r == 0) {
r = pthread_condattr_setclock(condattr_monotonic, CLOCK_MONOTONIC);
}

View File

@ -29,6 +29,18 @@ static volatile DWORD ruby_native_thread_key = TLS_OUT_OF_INDEXES;
static int w32_wait_events(HANDLE *events, int count, DWORD timeout, rb_thread_t *th);
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)
{
// not implemented
}
bool
rb_internal_thread_remove_event_hook(rb_internal_thread_event_hook_t * hook)
{
// not implemented
}
RBIMPL_ATTR_NORETURN()
static void
w32_error(const char *func)