1 /**********************************************************************
7 Copyright (C) 2020 Samuel Grant Dawson Williams
9 **********************************************************************/
12 #include "ruby/fiber/scheduler.h"
14 #include "ruby/io/buffer.h"
16 #include "internal/thread.h"
19 static ID id_scheduler_close
;
24 static ID id_timeout_after
;
25 static ID id_kernel_sleep
;
26 static ID id_process_wait
;
28 static ID id_io_read
, id_io_pread
;
29 static ID id_io_write
, id_io_pwrite
;
31 static ID id_io_close
;
33 static ID id_address_resolve
;
36 Init_Fiber_Scheduler(void)
38 id_close
= rb_intern_const("close");
39 id_scheduler_close
= rb_intern_const("scheduler_close");
41 id_block
= rb_intern_const("block");
42 id_unblock
= rb_intern_const("unblock");
44 id_timeout_after
= rb_intern_const("timeout_after");
45 id_kernel_sleep
= rb_intern_const("kernel_sleep");
46 id_process_wait
= rb_intern_const("process_wait");
48 id_io_read
= rb_intern_const("io_read");
49 id_io_pread
= rb_intern_const("io_pread");
50 id_io_write
= rb_intern_const("io_write");
51 id_io_pwrite
= rb_intern_const("io_pwrite");
53 id_io_wait
= rb_intern_const("io_wait");
54 id_io_close
= rb_intern_const("io_close");
56 id_address_resolve
= rb_intern_const("address_resolve");
60 rb_fiber_scheduler_get(void)
62 VM_ASSERT(ruby_thread_has_gvl_p());
64 rb_thread_t
*thread
= GET_THREAD();
67 return thread
->scheduler
;
71 verify_interface(VALUE scheduler
)
73 if (!rb_respond_to(scheduler
, id_block
)) {
74 rb_raise(rb_eArgError
, "Scheduler must implement #block");
77 if (!rb_respond_to(scheduler
, id_unblock
)) {
78 rb_raise(rb_eArgError
, "Scheduler must implement #unblock");
81 if (!rb_respond_to(scheduler
, id_kernel_sleep
)) {
82 rb_raise(rb_eArgError
, "Scheduler must implement #kernel_sleep");
85 if (!rb_respond_to(scheduler
, id_io_wait
)) {
86 rb_raise(rb_eArgError
, "Scheduler must implement #io_wait");
91 rb_fiber_scheduler_set(VALUE scheduler
)
93 VM_ASSERT(ruby_thread_has_gvl_p());
95 rb_thread_t
*thread
= GET_THREAD();
98 if (scheduler
!= Qnil
) {
99 verify_interface(scheduler
);
102 // 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.
103 if (thread
->scheduler
!= Qnil
) {
104 rb_fiber_scheduler_close(thread
->scheduler
);
107 thread
->scheduler
= scheduler
;
109 return thread
->scheduler
;
113 rb_fiber_scheduler_current_for_threadptr(rb_thread_t
*thread
)
117 if (thread
->blocking
== 0) {
118 return thread
->scheduler
;
126 rb_fiber_scheduler_current(void)
128 return rb_fiber_scheduler_current_for_threadptr(GET_THREAD());
131 VALUE
rb_fiber_scheduler_current_for_thread(VALUE thread
)
133 return rb_fiber_scheduler_current_for_threadptr(rb_thread_ptr(thread
));
137 rb_fiber_scheduler_close(VALUE scheduler
)
139 VM_ASSERT(ruby_thread_has_gvl_p());
143 result
= rb_check_funcall(scheduler
, id_scheduler_close
, 0, NULL
);
144 if (result
!= Qundef
) return result
;
146 result
= rb_check_funcall(scheduler
, id_close
, 0, NULL
);
147 if (result
!= Qundef
) return result
;
153 rb_fiber_scheduler_make_timeout(struct timeval
*timeout
)
156 return rb_float_new((double)timeout
->tv_sec
+ (0.000001f
* timeout
->tv_usec
));
163 rb_fiber_scheduler_kernel_sleep(VALUE scheduler
, VALUE timeout
)
165 return rb_funcall(scheduler
, id_kernel_sleep
, 1, timeout
);
169 rb_fiber_scheduler_kernel_sleepv(VALUE scheduler
, int argc
, VALUE
* argv
)
171 return rb_funcallv(scheduler
, id_kernel_sleep
, argc
, argv
);
176 rb_fiber_scheduler_timeout_after(VALUE scheduler
, VALUE timeout
, VALUE exception
, VALUE message
)
178 VALUE arguments
[] = {
179 timeout
, exception
, message
182 return rb_check_funcall(scheduler
, id_timeout_after
, 3, arguments
);
186 rb_fiber_scheduler_timeout_afterv(VALUE scheduler
, int argc
, VALUE
* argv
)
188 return rb_check_funcall(scheduler
, id_timeout_after
, argc
, argv
);
193 rb_fiber_scheduler_process_wait(VALUE scheduler
, rb_pid_t pid
, int flags
)
195 VALUE arguments
[] = {
196 PIDT2NUM(pid
), RB_INT2NUM(flags
)
199 return rb_check_funcall(scheduler
, id_process_wait
, 2, arguments
);
203 rb_fiber_scheduler_block(VALUE scheduler
, VALUE blocker
, VALUE timeout
)
205 return rb_funcall(scheduler
, id_block
, 2, blocker
, timeout
);
209 rb_fiber_scheduler_unblock(VALUE scheduler
, VALUE blocker
, VALUE fiber
)
211 VM_ASSERT(rb_obj_is_fiber(fiber
));
213 return rb_funcall(scheduler
, id_unblock
, 2, blocker
, fiber
);
217 rb_fiber_scheduler_io_wait(VALUE scheduler
, VALUE io
, VALUE events
, VALUE timeout
)
219 return rb_funcall(scheduler
, id_io_wait
, 3, io
, events
, timeout
);
223 rb_fiber_scheduler_io_wait_readable(VALUE scheduler
, VALUE io
)
225 return rb_fiber_scheduler_io_wait(scheduler
, io
, RB_UINT2NUM(RUBY_IO_READABLE
), Qnil
);
229 rb_fiber_scheduler_io_wait_writable(VALUE scheduler
, VALUE io
)
231 return rb_fiber_scheduler_io_wait(scheduler
, io
, RB_UINT2NUM(RUBY_IO_WRITABLE
), Qnil
);
235 rb_fiber_scheduler_io_read(VALUE scheduler
, VALUE io
, VALUE buffer
, size_t length
)
237 VALUE arguments
[] = {
238 io
, buffer
, SIZET2NUM(length
)
241 return rb_check_funcall(scheduler
, id_io_read
, 3, arguments
);
245 rb_fiber_scheduler_io_pread(VALUE scheduler
, VALUE io
, VALUE buffer
, size_t length
, off_t offset
)
247 VALUE arguments
[] = {
248 io
, buffer
, SIZET2NUM(length
), OFFT2NUM(offset
)
251 return rb_check_funcall(scheduler
, id_io_pread
, 4, arguments
);
255 rb_fiber_scheduler_io_write(VALUE scheduler
, VALUE io
, VALUE buffer
, size_t length
)
257 VALUE arguments
[] = {
258 io
, buffer
, SIZET2NUM(length
)
261 return rb_check_funcall(scheduler
, id_io_write
, 3, arguments
);
265 rb_fiber_scheduler_io_pwrite(VALUE scheduler
, VALUE io
, VALUE buffer
, size_t length
, off_t offset
)
267 VALUE arguments
[] = {
268 io
, buffer
, SIZET2NUM(length
), OFFT2NUM(offset
)
271 return rb_check_funcall(scheduler
, id_io_pwrite
, 4, arguments
);
275 rb_fiber_scheduler_io_read_memory(VALUE scheduler
, VALUE io
, void *base
, size_t size
, size_t length
)
277 VALUE buffer
= rb_io_buffer_new(base
, size
, RB_IO_BUFFER_LOCKED
);
279 VALUE result
= rb_fiber_scheduler_io_read(scheduler
, io
, buffer
, length
);
281 rb_io_buffer_unlock(buffer
);
282 rb_io_buffer_free(buffer
);
288 rb_fiber_scheduler_io_write_memory(VALUE scheduler
, VALUE io
, const void *base
, size_t size
, size_t length
)
290 VALUE buffer
= rb_io_buffer_new((void*)base
, size
, RB_IO_BUFFER_LOCKED
|RB_IO_BUFFER_READONLY
);
292 VALUE result
= rb_fiber_scheduler_io_write(scheduler
, io
, buffer
, length
);
294 rb_io_buffer_unlock(buffer
);
295 rb_io_buffer_free(buffer
);
301 rb_fiber_scheduler_io_close(VALUE scheduler
, VALUE io
)
303 VALUE arguments
[] = {io
};
305 return rb_check_funcall(scheduler
, id_io_close
, 1, arguments
);
309 rb_fiber_scheduler_address_resolve(VALUE scheduler
, VALUE hostname
)
311 VALUE arguments
[] = {
315 return rb_check_funcall(scheduler
, id_address_resolve
, 1, arguments
);