1313#include "ruby/io.h"
1414#include "ruby/io/buffer.h"
1515
16+ #include "ruby/thread.h"
17+
18+ // For `ruby_thread_has_gvl_p`.
1619#include "internal/thread.h"
1720
1821static ID id_close ;
@@ -33,6 +36,8 @@ static ID id_io_close;
3336
3437static ID id_address_resolve ;
3538
39+ static ID id_blocking_operation_wait ;
40+
3641static ID id_fiber_schedule ;
3742
3843/*
@@ -109,6 +114,8 @@ Init_Fiber_Scheduler(void)
109114
110115 id_address_resolve = rb_intern_const ("address_resolve" );
111116
117+ id_blocking_operation_wait = rb_intern_const ("blocking_operation_wait" );
118+
112119 id_fiber_schedule = rb_intern_const ("fiber" );
113120
114121#if 0 /* for RDoc */
@@ -693,6 +700,62 @@ rb_fiber_scheduler_address_resolve(VALUE scheduler, VALUE hostname)
693700 return rb_check_funcall (scheduler , id_address_resolve , 1 , arguments );
694701}
695702
703+ struct rb_blocking_operation_wait_arguments {
704+ void * (* function )(void * );
705+ void * data ;
706+ rb_unblock_function_t * unblock_function ;
707+ void * data2 ;
708+ int flags ;
709+
710+ struct rb_fiber_scheduler_blocking_operation_state * state ;
711+ };
712+
713+ static VALUE
714+ rb_fiber_scheduler_blocking_operation_wait_proc (RB_BLOCK_CALL_FUNC_ARGLIST (value , _arguments ))
715+ {
716+ struct rb_blocking_operation_wait_arguments * arguments = (struct rb_blocking_operation_wait_arguments * )_arguments ;
717+
718+ if (arguments -> state == NULL ) {
719+ rb_raise (rb_eRuntimeError , "Blocking function was already invoked!" );
720+ }
721+
722+ arguments -> state -> result = rb_nogvl (arguments -> function , arguments -> data , arguments -> unblock_function , arguments -> data2 , arguments -> flags );
723+ arguments -> state -> saved_errno = rb_errno ();
724+
725+ // Make sure it's only invoked once.
726+ arguments -> state = NULL ;
727+
728+ return Qnil ;
729+ }
730+
731+ /*
732+ * Document-method: Fiber::Scheduler#blocking_operation_wait
733+ * call-seq: blocking_operation_wait(work)
734+ *
735+ * Invoked by Ruby's core methods to run a blocking operation in a non-blocking way.
736+ *
737+ * Minimal suggested implementation is:
738+ *
739+ * def blocking_operation_wait(work)
740+ * Thread.new(&work).join
741+ * end
742+ */
743+ VALUE rb_fiber_scheduler_blocking_operation_wait (VALUE scheduler , void * (* function )(void * ), void * data , rb_unblock_function_t * unblock_function , void * data2 , int flags , struct rb_fiber_scheduler_blocking_operation_state * state )
744+ {
745+ struct rb_blocking_operation_wait_arguments arguments = {
746+ .function = function ,
747+ .data = data ,
748+ .unblock_function = unblock_function ,
749+ .data2 = data2 ,
750+ .flags = flags ,
751+ .state = state
752+ };
753+
754+ VALUE proc = rb_proc_new (rb_fiber_scheduler_blocking_operation_wait_proc , (VALUE )& arguments );
755+
756+ return rb_check_funcall (scheduler , id_blocking_operation_wait , 1 , & proc );
757+ }
758+
696759/*
697760 * Document-method: Fiber::Scheduler#fiber
698761 * call-seq: fiber(&block)
0 commit comments