2020-08-19 21:51:45 -04:00
/**********************************************************************
scheduler . c
$ Author $
Copyright ( C ) 2020 Samuel Grant Dawson Williams
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2020-10-15 21:25:58 -04:00
# include "vm_core.h"
2021-02-09 01:39:56 -05:00
# include "ruby/fiber/scheduler.h"
2020-08-19 21:51:45 -04:00
# include "ruby/io.h"
2021-07-02 06:41:16 -04:00
# include "ruby/io/buffer.h"
2021-07-15 23:22:17 -04:00
# include "internal/thread.h"
2020-08-19 21:51:45 -04:00
2020-09-19 19:34:02 -04:00
static ID id_close ;
2021-07-18 18:14:51 -04:00
static ID id_scheduler_close ;
2020-09-19 19:34:02 -04:00
2020-09-17 08:30:40 -04:00
static ID id_block ;
static ID id_unblock ;
2020-09-19 19:34:02 -04:00
2020-12-26 04:09:49 -05:00
static ID id_timeout_after ;
2020-09-19 19:34:02 -04:00
static ID id_kernel_sleep ;
2020-12-07 15:29:09 -05:00
static ID id_process_wait ;
2020-09-19 19:34:02 -04:00
2020-08-19 21:51:45 -04:00
static ID id_io_read ;
static ID id_io_write ;
static ID id_io_wait ;
2021-07-02 06:41:16 -04:00
static ID id_io_close ;
2020-08-19 21:51:45 -04:00
2021-06-14 00:21:08 -04:00
static ID id_address_resolve ;
2020-08-19 21:51:45 -04:00
void
2021-02-09 01:39:56 -05:00
Init_Fiber_Scheduler ( void )
2020-08-19 21:51:45 -04:00
{
2020-09-19 19:34:02 -04:00
id_close = rb_intern_const ( " close " ) ;
2021-07-18 18:14:51 -04:00
id_scheduler_close = rb_intern_const ( " scheduler_close " ) ;
2020-09-19 19:34:02 -04:00
2020-09-17 08:30:40 -04:00
id_block = rb_intern_const ( " block " ) ;
id_unblock = rb_intern_const ( " unblock " ) ;
2020-09-19 19:34:02 -04:00
2020-12-26 04:09:49 -05:00
id_timeout_after = rb_intern_const ( " timeout_after " ) ;
2020-09-19 19:34:02 -04:00
id_kernel_sleep = rb_intern_const ( " kernel_sleep " ) ;
2020-12-07 15:29:09 -05:00
id_process_wait = rb_intern_const ( " process_wait " ) ;
2020-09-19 19:34:02 -04:00
2020-08-19 21:51:45 -04:00
id_io_read = rb_intern_const ( " io_read " ) ;
id_io_write = rb_intern_const ( " io_write " ) ;
id_io_wait = rb_intern_const ( " io_wait " ) ;
2021-07-02 06:41:16 -04:00
id_io_close = rb_intern_const ( " io_close " ) ;
2021-06-14 00:21:08 -04:00
id_address_resolve = rb_intern_const ( " address_resolve " ) ;
2020-08-19 21:51:45 -04:00
}
2020-10-15 21:25:58 -04:00
VALUE
2021-02-09 01:39:56 -05:00
rb_fiber_scheduler_get ( void )
2020-10-15 21:25:58 -04:00
{
2021-07-15 23:22:17 -04:00
VM_ASSERT ( ruby_thread_has_gvl_p ( ) ) ;
2020-10-15 21:25:58 -04:00
rb_thread_t * thread = GET_THREAD ( ) ;
VM_ASSERT ( thread ) ;
return thread - > scheduler ;
}
2021-06-14 01:56:53 -04:00
static void
verify_interface ( VALUE scheduler )
{
if ( ! rb_respond_to ( scheduler , id_block ) ) {
2021-06-14 08:15:12 -04:00
rb_raise ( rb_eArgError , " Scheduler must implement #block " ) ;
2021-06-14 01:56:53 -04:00
}
if ( ! rb_respond_to ( scheduler , id_unblock ) ) {
2021-06-14 08:15:12 -04:00
rb_raise ( rb_eArgError , " Scheduler must implement #unblock " ) ;
2021-06-14 01:56:53 -04:00
}
if ( ! rb_respond_to ( scheduler , id_kernel_sleep ) ) {
2021-06-14 08:15:12 -04:00
rb_raise ( rb_eArgError , " Scheduler must implement #kernel_sleep " ) ;
2021-06-14 01:56:53 -04:00
}
if ( ! rb_respond_to ( scheduler , id_io_wait ) ) {
2021-06-14 08:15:12 -04:00
rb_raise ( rb_eArgError , " Scheduler must implement #io_wait " ) ;
2021-06-14 01:56:53 -04:00
}
}
2020-10-15 21:25:58 -04:00
VALUE
2021-02-09 01:39:56 -05:00
rb_fiber_scheduler_set ( VALUE scheduler )
2020-10-15 21:25:58 -04:00
{
2021-07-15 23:22:17 -04:00
VM_ASSERT ( ruby_thread_has_gvl_p ( ) ) ;
2020-10-15 21:25:58 -04:00
rb_thread_t * thread = GET_THREAD ( ) ;
VM_ASSERT ( thread ) ;
2021-06-14 01:56:53 -04:00
if ( scheduler ! = Qnil ) {
verify_interface ( scheduler ) ;
}
2020-10-15 21:25:58 -04:00
// 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 ) {
2021-02-09 01:39:56 -05:00
rb_fiber_scheduler_close ( thread - > scheduler ) ;
2020-10-15 21:25:58 -04:00
}
thread - > scheduler = scheduler ;
return thread - > scheduler ;
}
static VALUE
2021-02-09 01:39:56 -05:00
rb_fiber_scheduler_current_for_threadptr ( rb_thread_t * thread )
2020-10-15 21:25:58 -04:00
{
VM_ASSERT ( thread ) ;
if ( thread - > blocking = = 0 ) {
return thread - > scheduler ;
2021-06-16 09:07:05 -04:00
}
else {
2020-10-15 21:25:58 -04:00
return Qnil ;
}
}
VALUE
2021-02-09 01:39:56 -05:00
rb_fiber_scheduler_current ( void )
2020-10-15 21:25:58 -04:00
{
2021-02-09 01:39:56 -05:00
return rb_fiber_scheduler_current_for_threadptr ( GET_THREAD ( ) ) ;
2020-10-15 21:25:58 -04:00
}
2021-02-09 01:39:56 -05:00
VALUE rb_fiber_scheduler_current_for_thread ( VALUE thread )
2020-10-15 21:25:58 -04:00
{
2021-02-09 01:39:56 -05:00
return rb_fiber_scheduler_current_for_threadptr ( rb_thread_ptr ( thread ) ) ;
2020-10-15 21:25:58 -04:00
}
2020-10-01 00:44:29 -04:00
VALUE
2021-02-09 01:39:56 -05:00
rb_fiber_scheduler_close ( VALUE scheduler )
2020-09-19 19:34:02 -04:00
{
2021-07-15 23:22:17 -04:00
VM_ASSERT ( ruby_thread_has_gvl_p ( ) ) ;
2021-07-18 18:14:51 -04:00
VALUE result ;
result = rb_check_funcall ( scheduler , id_scheduler_close , 0 , NULL ) ;
if ( result ! = Qundef ) return result ;
result = rb_check_funcall ( scheduler , id_close , 0 , NULL ) ;
if ( result ! = Qundef ) return result ;
2020-10-01 00:45:50 -04:00
return Qnil ;
2020-09-19 19:34:02 -04:00
}
2020-08-19 21:51:45 -04:00
VALUE
2021-02-09 01:39:56 -05:00
rb_fiber_scheduler_make_timeout ( struct timeval * timeout )
2020-10-01 00:44:29 -04:00
{
2020-08-19 21:51:45 -04:00
if ( timeout ) {
return rb_float_new ( ( double ) timeout - > tv_sec + ( 0.000001f * timeout - > tv_usec ) ) ;
}
return Qnil ;
}
2020-12-26 04:09:49 -05:00
VALUE
2021-03-30 00:33:15 -04:00
rb_fiber_scheduler_kernel_sleep ( VALUE scheduler , VALUE timeout )
2021-02-11 20:40:50 -05:00
{
2021-03-30 00:33:15 -04:00
return rb_funcall ( scheduler , id_kernel_sleep , 1 , timeout ) ;
2021-02-11 20:40:50 -05:00
}
2021-02-11 01:17:54 -05:00
VALUE
2021-03-30 00:33:15 -04:00
rb_fiber_scheduler_kernel_sleepv ( VALUE scheduler , int argc , VALUE * argv )
2021-02-11 01:17:54 -05:00
{
2021-03-30 00:33:15 -04:00
return rb_funcallv ( scheduler , id_kernel_sleep , argc , argv ) ;
2021-02-11 01:17:54 -05:00
}
2021-03-30 00:33:15 -04:00
#if 0
2020-10-01 00:44:29 -04:00
VALUE
2021-03-30 00:33:15 -04:00
rb_fiber_scheduler_timeout_after ( VALUE scheduler , VALUE timeout , VALUE exception , VALUE message )
2020-08-19 21:51:45 -04:00
{
2021-03-30 00:33:15 -04:00
VALUE arguments [ ] = {
timeout , exception , message
} ;
return rb_check_funcall ( scheduler , id_timeout_after , 3 , arguments ) ;
2020-08-19 21:51:45 -04:00
}
2020-10-01 00:44:29 -04:00
VALUE
2021-03-30 00:33:15 -04:00
rb_fiber_scheduler_timeout_afterv ( VALUE scheduler , int argc , VALUE * argv )
2020-08-19 21:51:45 -04:00
{
2021-03-30 00:33:15 -04:00
return rb_check_funcall ( scheduler , id_timeout_after , argc , argv ) ;
2020-08-19 21:51:45 -04:00
}
2021-03-30 00:33:15 -04:00
# endif
2020-08-19 21:51:45 -04:00
2020-12-07 15:29:09 -05:00
VALUE
2021-02-09 01:39:56 -05:00
rb_fiber_scheduler_process_wait ( VALUE scheduler , rb_pid_t pid , int flags )
2020-12-07 15:29:09 -05:00
{
2021-02-09 01:39:56 -05:00
VALUE arguments [ ] = {
PIDT2NUM ( pid ) , RB_INT2NUM ( flags )
} ;
2021-02-09 02:59:15 -05:00
2021-02-09 01:39:56 -05:00
return rb_check_funcall ( scheduler , id_process_wait , 2 , arguments ) ;
2020-12-07 15:29:09 -05:00
}
2020-10-01 00:44:29 -04:00
VALUE
2021-02-09 01:39:56 -05:00
rb_fiber_scheduler_block ( VALUE scheduler , VALUE blocker , VALUE timeout )
2020-09-05 00:26:24 -04:00
{
2020-09-20 17:54:08 -04:00
return rb_funcall ( scheduler , id_block , 2 , blocker , timeout ) ;
2020-09-05 00:26:24 -04:00
}
2020-10-01 00:44:29 -04:00
VALUE
2021-02-09 01:39:56 -05:00
rb_fiber_scheduler_unblock ( VALUE scheduler , VALUE blocker , VALUE fiber )
2020-09-05 00:26:24 -04:00
{
2021-07-15 23:22:17 -04:00
VM_ASSERT ( rb_obj_is_fiber ( fiber ) ) ;
2020-09-17 08:30:40 -04:00
return rb_funcall ( scheduler , id_unblock , 2 , blocker , fiber ) ;
2020-09-05 00:26:24 -04:00
}
2020-10-01 00:44:29 -04:00
VALUE
2021-02-09 01:39:56 -05:00
rb_fiber_scheduler_io_wait ( VALUE scheduler , VALUE io , VALUE events , VALUE timeout )
2020-08-19 21:51:45 -04:00
{
return rb_funcall ( scheduler , id_io_wait , 3 , io , events , timeout ) ;
}
2020-10-01 00:44:29 -04:00
VALUE
2021-02-09 01:39:56 -05:00
rb_fiber_scheduler_io_wait_readable ( VALUE scheduler , VALUE io )
2020-08-19 21:51:45 -04:00
{
2021-02-09 01:39:56 -05:00
return rb_fiber_scheduler_io_wait ( scheduler , io , RB_UINT2NUM ( RUBY_IO_READABLE ) , Qnil ) ;
2020-08-19 21:51:45 -04:00
}
2020-10-01 00:44:29 -04:00
VALUE
2021-02-09 01:39:56 -05:00
rb_fiber_scheduler_io_wait_writable ( VALUE scheduler , VALUE io )
2020-08-19 21:51:45 -04:00
{
2021-02-09 01:39:56 -05:00
return rb_fiber_scheduler_io_wait ( scheduler , io , RB_UINT2NUM ( RUBY_IO_WRITABLE ) , Qnil ) ;
2020-08-19 21:51:45 -04:00
}
2020-10-01 00:44:29 -04:00
VALUE
2021-07-02 06:41:16 -04:00
rb_fiber_scheduler_io_read ( VALUE scheduler , VALUE io , VALUE buffer , size_t length )
2020-08-20 08:53:08 -04:00
{
2021-02-09 01:39:56 -05:00
VALUE arguments [ ] = {
2021-07-02 06:41:16 -04:00
io , buffer , SIZET2NUM ( length )
2021-02-09 01:39:56 -05:00
} ;
2021-02-09 02:59:15 -05:00
2021-07-02 06:41:16 -04:00
return rb_check_funcall ( scheduler , id_io_read , 3 , arguments ) ;
2020-08-20 08:53:08 -04:00
}
2020-10-01 00:44:29 -04:00
VALUE
2021-07-02 06:41:16 -04:00
rb_fiber_scheduler_io_write ( VALUE scheduler , VALUE io , VALUE buffer , size_t length )
2020-08-19 21:51:45 -04:00
{
2021-02-09 01:39:56 -05:00
VALUE arguments [ ] = {
2021-07-02 06:41:16 -04:00
io , buffer , SIZET2NUM ( length )
2021-02-09 01:39:56 -05:00
} ;
2021-07-02 06:41:16 -04:00
return rb_check_funcall ( scheduler , id_io_write , 3 , arguments ) ;
}
VALUE
rb_fiber_scheduler_io_read_memory ( VALUE scheduler , VALUE io , void * base , size_t size , size_t length )
{
VALUE buffer = rb_io_buffer_new ( base , size , RB_IO_BUFFER_LOCKED ) ;
VALUE result = rb_fiber_scheduler_io_read ( scheduler , io , buffer , length ) ;
2021-12-18 23:05:57 -05:00
rb_io_buffer_unlock ( buffer ) ;
2021-07-02 06:41:16 -04:00
rb_io_buffer_free ( buffer ) ;
return result ;
}
VALUE
rb_fiber_scheduler_io_write_memory ( VALUE scheduler , VALUE io , const void * base , size_t size , size_t length )
{
2021-12-20 05:06:21 -05:00
VALUE buffer = rb_io_buffer_new ( ( void * ) base , size , RB_IO_BUFFER_LOCKED | RB_IO_BUFFER_READONLY ) ;
2021-07-02 06:41:16 -04:00
VALUE result = rb_fiber_scheduler_io_write ( scheduler , io , buffer , length ) ;
2021-12-18 23:05:57 -05:00
rb_io_buffer_unlock ( buffer ) ;
2021-07-02 06:41:16 -04:00
rb_io_buffer_free ( buffer ) ;
return result ;
}
VALUE
rb_fiber_scheduler_io_close ( VALUE scheduler , VALUE io )
{
VALUE arguments [ ] = { io } ;
return rb_check_funcall ( scheduler , id_io_close , 1 , arguments ) ;
2020-08-19 21:51:45 -04:00
}
2021-06-14 00:21:08 -04:00
VALUE
rb_fiber_scheduler_address_resolve ( VALUE scheduler , VALUE hostname )
{
VALUE arguments [ ] = {
hostname
} ;
return rb_check_funcall ( scheduler , id_address_resolve , 1 , arguments ) ;
}