Ruby 3.0.5p211 (2022-11-24 revision ba5cf0f7c52d4d35cc6a173c89eda98ceffa2dcf)
scheduler.c
Go to the documentation of this file.
1/**********************************************************************
2
3 scheduler.c
4
5 $Author$
6
7 Copyright (C) 2020 Samuel Grant Dawson Williams
8
9**********************************************************************/
10
11#include "vm_core.h"
12#include "internal/scheduler.h"
13#include "ruby/io.h"
14
15static ID id_close;
16
17static ID id_block;
18static ID id_unblock;
19
20static ID id_kernel_sleep;
21static ID id_process_wait;
22
23static ID id_io_read;
24static ID id_io_write;
25static ID id_io_wait;
26
27void
29{
30 id_close = rb_intern_const("close");
31
32 id_block = rb_intern_const("block");
33 id_unblock = rb_intern_const("unblock");
34
35 id_kernel_sleep = rb_intern_const("kernel_sleep");
36 id_process_wait = rb_intern_const("process_wait");
37
38 id_io_read = rb_intern_const("io_read");
39 id_io_write = rb_intern_const("io_write");
40 id_io_wait = rb_intern_const("io_wait");
41}
42
45{
46 rb_thread_t *thread = GET_THREAD();
47 VM_ASSERT(thread);
48
49 return thread->scheduler;
50}
51
52static void
53verify_interface(VALUE scheduler)
54{
55 if (!rb_respond_to(scheduler, id_block)) {
56 rb_raise(rb_eArgError, "Scheduler must implement #block!");
57 }
58
59 if (!rb_respond_to(scheduler, id_unblock)) {
60 rb_raise(rb_eArgError, "Scheduler must implement #unblock!");
61 }
62
63 if (!rb_respond_to(scheduler, id_kernel_sleep)) {
64 rb_raise(rb_eArgError, "Scheduler must implement #kernel_sleep!");
65 }
66
67 if (!rb_respond_to(scheduler, id_io_wait)) {
68 rb_raise(rb_eArgError, "Scheduler must implement #io_wait!");
69 }
70}
71
74{
75 rb_thread_t *thread = GET_THREAD();
76 VM_ASSERT(thread);
77
78 if (scheduler != Qnil) {
79 verify_interface(scheduler);
80 }
81
82 // 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.
83 if (thread->scheduler != Qnil) {
85 }
86
87 thread->scheduler = scheduler;
88
89 return thread->scheduler;
90}
91
92static VALUE
93rb_threadptr_scheduler_current(rb_thread_t *thread)
94{
95 VM_ASSERT(thread);
96
97 if (thread->blocking == 0) {
98 return thread->scheduler;
99 } else {
100 return Qnil;
101 }
102}
103
104VALUE
106{
107 return rb_threadptr_scheduler_current(GET_THREAD());
108}
109
111{
112 return rb_threadptr_scheduler_current(rb_thread_ptr(thread));
113}
114
115VALUE
117{
118 if (rb_respond_to(scheduler, id_close)) {
119 return rb_funcall(scheduler, id_close, 0);
120 }
121
122 return Qnil;
123}
124
125VALUE
127{
128 if (timeout) {
129 return rb_float_new((double)timeout->tv_sec + (0.000001f * timeout->tv_usec));
130 }
131
132 return Qnil;
133}
134
135VALUE
137{
138 return rb_funcall(scheduler, id_kernel_sleep, 1, timeout);
139}
140
141VALUE
143{
144 return rb_funcallv(scheduler, id_kernel_sleep, argc, argv);
145}
146
147int
149{
150 return rb_respond_to(scheduler, id_process_wait);
151}
152
153VALUE
154rb_scheduler_process_wait(VALUE scheduler, rb_pid_t pid, int flags)
155{
156 return rb_funcall(scheduler, id_process_wait, 2, PIDT2NUM(pid), RB_INT2NUM(flags));
157}
158
159VALUE
160rb_scheduler_block(VALUE scheduler, VALUE blocker, VALUE timeout)
161{
162 return rb_funcall(scheduler, id_block, 2, blocker, timeout);
163}
164
165VALUE
166rb_scheduler_unblock(VALUE scheduler, VALUE blocker, VALUE fiber)
167{
168 return rb_funcall(scheduler, id_unblock, 2, blocker, fiber);
169}
170
171VALUE
172rb_scheduler_io_wait(VALUE scheduler, VALUE io, VALUE events, VALUE timeout)
173{
174 return rb_funcall(scheduler, id_io_wait, 3, io, events, timeout);
175}
176
177VALUE
179{
180 return rb_scheduler_io_wait(scheduler, io, RB_UINT2NUM(RUBY_IO_READABLE), Qnil);
181}
182
183VALUE
185{
186 return rb_scheduler_io_wait(scheduler, io, RB_UINT2NUM(RUBY_IO_WRITABLE), Qnil);
187}
188
189int
191{
192 return rb_respond_to(scheduler, id_io_read);
193}
194
195VALUE
196rb_scheduler_io_read(VALUE scheduler, VALUE io, VALUE buffer, size_t offset, size_t length)
197{
198 return rb_funcall(scheduler, id_io_read, 4, io, buffer, SIZET2NUM(offset), SIZET2NUM(length));
199}
200
201int
203{
204 return rb_respond_to(scheduler, id_io_write);
205}
206
207VALUE
208rb_scheduler_io_write(VALUE scheduler, VALUE io, VALUE buffer, size_t offset, size_t length)
209{
210 // We should ensure string has capacity to receive data, and then resize it afterwards.
211 return rb_funcall(scheduler, id_io_write, 4, io, buffer, SIZET2NUM(offset), SIZET2NUM(length));
212}
void rb_raise(VALUE exc, const char *fmt,...)
Definition: error.c:2917
VALUE rb_eArgError
Definition: error.c:1058
VALUE rb_funcall(VALUE, ID, int,...)
Calls a method.
Definition: vm_eval.c:1077
int rb_respond_to(VALUE, ID)
Definition: vm_method.c:2561
@ RUBY_IO_READABLE
Definition: io.h:45
@ RUBY_IO_WRITABLE
Definition: io.h:46
#define RB_UINT2NUM
Definition: int.h:39
#define RB_INT2NUM
Definition: int.h:37
#define rb_float_new
Definition: numeric.h:95
#define rb_funcallv(...)
Definition: internal.h:77
voidpf uLong offset
Definition: ioapi.h:144
#define PIDT2NUM
Definition: pid_t.h:27
int argc
Definition: ruby.c:240
char ** argv
Definition: ruby.c:241
VALUE rb_scheduler_io_wait_writable(VALUE scheduler, VALUE io)
Definition: scheduler.c:184
VALUE rb_scheduler_kernel_sleep(VALUE scheduler, VALUE timeout)
Definition: scheduler.c:136
VALUE rb_scheduler_current(void)
Definition: scheduler.c:105
VALUE rb_scheduler_io_wait(VALUE scheduler, VALUE io, VALUE events, VALUE timeout)
Definition: scheduler.c:172
VALUE rb_scheduler_block(VALUE scheduler, VALUE blocker, VALUE timeout)
Definition: scheduler.c:160
VALUE rb_scheduler_kernel_sleepv(VALUE scheduler, int argc, VALUE *argv)
Definition: scheduler.c:142
VALUE rb_scheduler_process_wait(VALUE scheduler, rb_pid_t pid, int flags)
Definition: scheduler.c:154
void Init_Scheduler(void)
Definition: scheduler.c:28
VALUE rb_scheduler_timeout(struct timeval *timeout)
Definition: scheduler.c:126
VALUE rb_scheduler_get(void)
Definition: scheduler.c:44
int rb_scheduler_supports_process_wait(VALUE scheduler)
Definition: scheduler.c:148
int rb_scheduler_supports_io_read(VALUE scheduler)
Definition: scheduler.c:190
VALUE rb_scheduler_io_wait_readable(VALUE scheduler, VALUE io)
Definition: scheduler.c:178
VALUE rb_thread_scheduler_current(VALUE thread)
Definition: scheduler.c:110
VALUE rb_scheduler_unblock(VALUE scheduler, VALUE blocker, VALUE fiber)
Definition: scheduler.c:166
int rb_scheduler_supports_io_write(VALUE scheduler)
Definition: scheduler.c:202
VALUE rb_scheduler_close(VALUE scheduler)
Definition: scheduler.c:116
VALUE rb_scheduler_io_read(VALUE scheduler, VALUE io, VALUE buffer, size_t offset, size_t length)
Definition: scheduler.c:196
VALUE rb_scheduler_io_write(VALUE scheduler, VALUE io, VALUE buffer, size_t offset, size_t length)
Definition: scheduler.c:208
VALUE rb_scheduler_set(VALUE scheduler)
Definition: scheduler.c:73
Internal header for Scheduler.
#define SIZET2NUM
Definition: size_t.h:52
#define Qnil
unsigned blocking
Definition: vm_core.h:1016
long tv_usec
Definition: missing.h:53
time_t tv_sec
Definition: missing.h:52
unsigned long VALUE
Definition: value.h:38
unsigned long ID
Definition: value.h:39
#define VM_ASSERT(expr)
Definition: vm_core.h:61