summaryrefslogtreecommitdiff
path: root/scheduler.c
diff options
context:
space:
mode:
authorSamuel Williams <[email protected]>2024-10-31 17:26:37 +1300
committerGitHub <[email protected]>2024-10-31 17:26:37 +1300
commit87fb44dff6409a19d12052cf0fc07ba80a4c45ac (patch)
tree1446cd9390356ae660c50ef7ae877bc57af8d567 /scheduler.c
parent550ac2f2edc07d1b63e3755233df0758a652b53f (diff)
Introduce Fiber Scheduler `blocking_region` hook. (#11963)
Notes
Notes: Merged-By: ioquatix <[email protected]>
Diffstat (limited to 'scheduler.c')
-rw-r--r--scheduler.c63
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)