diff options
author | Samuel Williams <[email protected]> | 2024-10-31 17:26:37 +1300 |
---|---|---|
committer | GitHub <[email protected]> | 2024-10-31 17:26:37 +1300 |
commit | 87fb44dff6409a19d12052cf0fc07ba80a4c45ac (patch) | |
tree | 1446cd9390356ae660c50ef7ae877bc57af8d567 /scheduler.c | |
parent | 550ac2f2edc07d1b63e3755233df0758a652b53f (diff) |
Introduce Fiber Scheduler `blocking_region` hook. (#11963)
Notes
Notes:
Merged-By: ioquatix <[email protected]>
Diffstat (limited to 'scheduler.c')
-rw-r--r-- | scheduler.c | 63 |
1 files changed, 63 insertions, 0 deletions
diff --git a/scheduler.c b/scheduler.c index 3159635dba..0d51a0d951 100644 --- a/scheduler.c +++ b/scheduler.c @@ -13,6 +13,9 @@ #include "ruby/io.h" #include "ruby/io/buffer.h" +#include "ruby/thread.h" + +// For `ruby_thread_has_gvl_p`. #include "internal/thread.h" static ID id_close; @@ -33,6 +36,8 @@ static ID id_io_close; static ID id_address_resolve; +static ID id_blocking_region; + static ID id_fiber_schedule; /* @@ -109,6 +114,8 @@ Init_Fiber_Scheduler(void) id_address_resolve = rb_intern_const("address_resolve"); + id_blocking_region = rb_intern_const("blocking_region"); + id_fiber_schedule = rb_intern_const("fiber"); #if 0 /* for RDoc */ @@ -693,6 +700,62 @@ rb_fiber_scheduler_address_resolve(VALUE scheduler, VALUE hostname) return rb_check_funcall(scheduler, id_address_resolve, 1, arguments); } +struct rb_blocking_region_arguments { + void *(*function)(void *); + void *data; + rb_unblock_function_t *unblock_function; + void *data2; + int flags; + + struct rb_fiber_scheduler_blocking_region_state *state; +}; + +static VALUE +rb_fiber_scheduler_blocking_region_proc(RB_BLOCK_CALL_FUNC_ARGLIST(value, _arguments)) +{ + struct rb_blocking_region_arguments *arguments = (struct rb_blocking_region_arguments*)_arguments; + + if (arguments->state == NULL) { + rb_raise(rb_eRuntimeError, "Blocking function was already invoked!"); + } + + arguments->state->result = rb_nogvl(arguments->function, arguments->data, arguments->unblock_function, arguments->data2, arguments->flags); + arguments->state->saved_errno = rb_errno(); + + // Make sure it's only invoked once. + arguments->state = NULL; + + return Qnil; +} + +/* + * Document-method: Fiber::Scheduler#blocking_region + * call-seq: blocking_region(work) + * + * Invoked by Ruby's core methods to run a blocking operation in a non-blocking way. + * + * Minimal suggested implementation is: + * + * def blocking_region(work) + * Thread.new(&work).join + * end + */ +VALUE rb_fiber_scheduler_blocking_region(VALUE scheduler, void* (*function)(void *), void *data, rb_unblock_function_t *unblock_function, void *data2, int flags, struct rb_fiber_scheduler_blocking_region_state *state) +{ + struct rb_blocking_region_arguments arguments = { + .function = function, + .data = data, + .unblock_function = unblock_function, + .data2 = data2, + .flags = flags, + .state = state + }; + + VALUE proc = rb_proc_new(rb_fiber_scheduler_blocking_region_proc, (VALUE)&arguments); + + return rb_check_funcall(scheduler, id_blocking_region, 1, &proc); +} + /* * Document-method: Fiber::Scheduler#fiber * call-seq: fiber(&block) |