From 5e9ec351044fb74f07f2a45a0dab1e226159b7e6 Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Mon, 14 Jun 2021 17:56:53 +1200 Subject: Wake up join list within thread EC context. (#4471) * Wake up join list within thread EC context. * Consume items from join list so that they are not re-executed. If `rb_fiber_scheduler_unblock` raises an exception, it can result in a segfault if `rb_threadptr_join_list_wakeup` is not within a valid EC. This change moves `rb_threadptr_join_list_wakeup` into the thread's top level EC which initially caused an infinite loop because on exception will retry. We explicitly remove items from the thread's join list to avoid this situation. * Verify the required scheduler interface. * Test several scheduler hooks methods with broken `unblock` implementation. --- scheduler.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) (limited to 'scheduler.c') diff --git a/scheduler.c b/scheduler.c index 88db433f1e..66cbfc6f10 100644 --- a/scheduler.c +++ b/scheduler.c @@ -49,12 +49,36 @@ rb_scheduler_get(void) return thread->scheduler; } +static void +verify_interface(VALUE scheduler) +{ + if (!rb_respond_to(scheduler, id_block)) { + rb_raise(rb_eArgError, "Scheduler must implement #block!"); + } + + if (!rb_respond_to(scheduler, id_unblock)) { + rb_raise(rb_eArgError, "Scheduler must implement #unblock!"); + } + + if (!rb_respond_to(scheduler, id_kernel_sleep)) { + rb_raise(rb_eArgError, "Scheduler must implement #kernel_sleep!"); + } + + if (!rb_respond_to(scheduler, id_io_wait)) { + rb_raise(rb_eArgError, "Scheduler must implement #io_wait!"); + } +} + VALUE rb_scheduler_set(VALUE scheduler) { rb_thread_t *thread = GET_THREAD(); VM_ASSERT(thread); + if (scheduler != Qnil) { + verify_interface(scheduler); + } + // We invoke Scheduler#close when setting it to something else, to ensure the previous scheduler runs to completion before changing the scheduler. That way, we do not need to consider interactions, e.g., of a Fiber from the previous scheduler with the new scheduler. if (thread->scheduler != Qnil) { rb_scheduler_close(thread->scheduler); -- cgit v1.2.3