1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
|
#ifndef RUBY_FIBER_SCHEDULER_H /*-*-C++-*-vi:se ft=cpp:*/
#define RUBY_FIBER_SCHEDULER_H
/**
* @file
* @author Ruby developers <ruby-core@ruby-lang.org>
* @copyright This file is a part of the programming language Ruby.
* Permission is hereby granted, to either redistribute and/or
* modify this file, provided that the conditions mentioned in the
* file COPYING are met. Consult the file for details.
* @brief Scheduler APIs.
*/
#include "ruby/internal/config.h"
#include <errno.h>
#ifdef STDC_HEADERS
#include <stddef.h> /* size_t */
#endif
#include "ruby/ruby.h"
#include "ruby/internal/dllexport.h"
#include "ruby/internal/arithmetic.h"
RBIMPL_SYMBOL_EXPORT_BEGIN()
struct timeval;
/**
* Wrap a `ssize_t` and `int errno` into a single `VALUE`. This interface should
* be used to safely capture results from system calls like `read` and `write`.
*
* You should use `rb_fiber_scheduler_io_result_apply` to unpack the result of
* this value and update `int errno`.
*
* You should not directly try to interpret the result value as it is considered
* an opaque representation. However, the general representation is an integer
* in the range of `[-int errno, size_t size]`. Linux generally restricts the
* result of system calls like `read` and `write` to `<= 2^31` which means this
* will typically fit within a single FIXNUM.
*
* @param[in] result The result of the system call.
* @param[in] error The value of `errno`.
* @return A `VALUE` which contains the result and/or errno.
*/
static inline VALUE
rb_fiber_scheduler_io_result(ssize_t result, int error) {
if (result == -1) {
return RB_INT2NUM(-error);
} else {
return RB_SIZE2NUM(result);
}
}
/**
* Apply an io result to the local thread, returning the value of the orginal
* system call that created it and updating `int errno`.
*
* You should not directly try to interpret the result value as it is considered
* an opaque representation.
*
* @param[in] result The `VALUE` which contains an errno and/or result size.
* @post Updates `int errno` with the value if negative.
* @return The original result of the system call.
*/
static inline ssize_t
rb_fiber_scheduler_io_result_apply(VALUE result) {
if (RB_FIXNUM_P(result) && RB_NUM2INT(result) < 0) {
errno = -RB_NUM2INT(result);
return -1;
} else {
return RB_NUM2SIZE(result);
}
}
/**
* Queries the current scheduler of the current thread that is calling this
* function.
*
* @retval RUBY_Qnil No scheduler has been set so far to this thread (which
* is the default).
* @retval otherwise The scheduler that was last set for the current thread
* with rb_fiber_scheduler_set().
*/
VALUE rb_fiber_scheduler_get(void);
/**
* Destructively assigns the passed scheduler to that of the current thread
* that is calling this function. If the scheduler is set, non-blocking fibers
* (created by `Fiber.new` with `blocking: false`, or by `Fiber.schedule`) call
* that scheduler's hook methods on potentially blocking operations, and the
* current thread will call scheduler's `#close` method on finalisation
* (allowing the scheduler to properly manage all non-finished fibers).
* `scheduler` can be an object of any class corresponding to
* `Fiber::SchedulerInterface`. Its implementation is up to the user.
*
* @param[in] scheduler The scheduler to set.
* @exception rb_eArgError `scheduler` does not conform the interface.
* @post Current thread's scheduler is `scheduler`.
*/
VALUE rb_fiber_scheduler_set(VALUE scheduler);
/**
* Identical to rb_fiber_scheduler_get(), except it also returns ::RUBY_Qnil in
* case of a blocking fiber. As blocking fibers do not participate schedulers'
* scheduling this function can be handy.
*
* @retval RUBY_Qnil No scheduler is in effect.
* @retval otherwise The scheduler that is in effect, if any.
*/
VALUE rb_fiber_scheduler_current(void);
/**
* Identical to rb_fiber_scheduler_current(), except it queries for that of the
* passed thread instead of the implicit current one.
*
* @param[in] thread Target thread.
* @exception rb_eTypeError `thread` is not a thread.
* @retval RUBY_Qnil No scheduler is in effect in `thread`.
* @retval otherwise The scheduler that is in effect in `thread`.
*/
VALUE rb_fiber_scheduler_current_for_thread(VALUE thread);
/**
* Converts the passed timeout to an expression that rb_fiber_scheduler_block()
* etc. expects.
*
* @param[in] timeout A duration (can be `NULL`).
* @retval RUBY_Qnil No timeout (blocks indefinitely).
* @retval otherwise A timeout object.
*/
VALUE rb_fiber_scheduler_make_timeout(struct timeval *timeout);
/**
* Closes the passed scheduler object. This expects the scheduler to wait for
* all fibers. Thus the scheduler's main loop tends to start here.
*
* @param[in] scheduler Target scheduler.
* @return What `scheduler.close` returns.
*/
VALUE rb_fiber_scheduler_close(VALUE scheduler);
/**
* Nonblocking `sleep`. Depending on scheduler implementation, this for
* instance switches to another fiber etc.
*
* @param[in] scheduler Target scheduler.
* @param[in] duration Passed as-is to `scheduler.kernel_sleep`.
* @return What `scheduler.kernel_sleep` returns.
*/
VALUE rb_fiber_scheduler_kernel_sleep(VALUE scheduler, VALUE duration);
/**
* Identical to rb_fiber_scheduler_kernel_sleep(), except it can pass multiple
* arguments.
*
* @param[in] scheduler Target scheduler.
* @param[in] argc Number of objects of `argv`.
* @param[in] argv Passed as-is to `scheduler.kernel_sleep`
* @return What `scheduler.kernel_sleep` returns.
*/
VALUE rb_fiber_scheduler_kernel_sleepv(VALUE scheduler, int argc, VALUE * argv);
/* Description TBW */
#if 0
VALUE rb_fiber_scheduler_timeout_after(VALUE scheduler, VALUE timeout, VALUE exception, VALUE message);
VALUE rb_fiber_scheduler_timeout_afterv(VALUE scheduler, int argc, VALUE * argv);
int rb_fiber_scheduler_supports_process_wait(VALUE scheduler);
#endif
/**
* Nonblocking `waitpid`. Depending on scheduler implementation, this for
* instance switches to another fiber etc.
*
* @param[in] scheduler Target scheduler.
* @param[in] pid Process ID to wait.
* @param[in] flags Wait flags, e.g. `WUNTRACED`.
* @return What `scheduler.process_wait` returns.
*/
VALUE rb_fiber_scheduler_process_wait(VALUE scheduler, rb_pid_t pid, int flags);
/**
* Nonblocking wait for the passed "blocker", which is for instance
* `Thread.join` or `Mutex.lock`. Depending on scheduler implementation, this
* for instance switches to another fiber etc.
*
* @param[in] scheduler Target scheduler.
* @param[in] blocker What blocks the current fiber.
* @param[in] timeout Numeric timeout.
* @return What `scheduler.block` returns.
*/
VALUE rb_fiber_scheduler_block(VALUE scheduler, VALUE blocker, VALUE timeout);
/**
* Wakes up a fiber previously blocked using rb_fiber_scheduler_block().
*
* @param[in] scheduler Target scheduler.
* @param[in] blocker What was awaited for.
* @param[in] fiber What to unblock.
* @return What `scheduler.unblock` returns.
*/
VALUE rb_fiber_scheduler_unblock(VALUE scheduler, VALUE blocker, VALUE fiber);
/**
* Nonblocking version of rb_io_wait(). Depending on scheduler implementation,
* this for instance switches to another fiber etc.
*
* The "events" here is a Ruby level integer, which is an OR-ed value of
* `IO::READABLE`, `IO::WRITable`, and `IO::PRIORITY`.
*
* @param[in] scheduler Target scheduler.
* @param[in] io An io object to wait.
* @param[in] events An integer set of interests.
* @param[in] timeout Numeric timeout.
* @return What `scheduler.io_wait` returns.
*/
VALUE rb_fiber_scheduler_io_wait(VALUE scheduler, VALUE io, VALUE events, VALUE timeout);
/**
* Nonblocking wait until the passed IO is ready for reading. This is a
* special case of rb_fiber_scheduler_io_wait(), where the interest is
* `IO::READABLE` and timeout is never.
*
* @param[in] scheduler Target scheduler.
* @param[in] io An io object to wait.
* @return What `scheduler.io_wait` returns.
*/
VALUE rb_fiber_scheduler_io_wait_readable(VALUE scheduler, VALUE io);
/**
* Nonblocking wait until the passed IO is ready for writing. This is a
* special case of rb_fiber_scheduler_io_wait(), where the interest is
* `IO::WRITABLE` and timeout is never.
*
* @param[in] scheduler Target scheduler.
* @param[in] io An io object to wait.
* @return What `scheduler.io_wait` returns.
*/
VALUE rb_fiber_scheduler_io_wait_writable(VALUE scheduler, VALUE io);
/**
* Nonblocking read from the passed IO.
*
* @param[in] scheduler Target scheduler.
* @param[out] io An io object to read from.
* @param[out] buffer Return buffer.
* @param[in] length Requested number of bytes to read.
* @retval RUBY_Qundef `scheduler` doesn't have `#io_read`.
* @return otherwise What `scheduler.io_read` returns `[-errno, size]`.
*/
VALUE rb_fiber_scheduler_io_read(VALUE scheduler, VALUE io, VALUE buffer, size_t length);
/**
* Nonblocking write to the passed IO.
*
* @param[in] scheduler Target scheduler.
* @param[out] io An io object to write to.
* @param[in] buffer What to write.
* @param[in] length Number of bytes to write.
* @retval RUBY_Qundef `scheduler` doesn't have `#io_write`.
* @return otherwise What `scheduler.io_write` returns `[-errno, size]`.
*/
VALUE rb_fiber_scheduler_io_write(VALUE scheduler, VALUE io, VALUE buffer, size_t length);
/**
* Nonblocking read from the passed IO at the specified offset.
*
* @param[in] scheduler Target scheduler.
* @param[out] io An io object to read from.
* @param[out] buffer Return buffer.
* @param[in] length Requested number of bytes to read.
* @param[in] offset The offset in the given IO to read the data from.
* @retval RUBY_Qundef `scheduler` doesn't have `#io_read`.
* @return otherwise What `scheduler.io_read` returns.
*/
VALUE rb_fiber_scheduler_io_pread(VALUE scheduler, VALUE io, VALUE buffer, size_t length, off_t offset);
/**
* Nonblocking write to the passed IO at the specified offset.
*
* @param[in] scheduler Target scheduler.
* @param[out] io An io object to write to.
* @param[in] buffer What to write.
* @param[in] length Number of bytes to write.
* @param[in] offset The offset in the given IO to write the data to.
* @retval RUBY_Qundef `scheduler` doesn't have `#io_write`.
* @return otherwise What `scheduler.io_write` returns.
*/
VALUE rb_fiber_scheduler_io_pwrite(VALUE scheduler, VALUE io, VALUE buffer, size_t length, off_t offset);
/**
* Nonblocking read from the passed IO using a native buffer.
*
* @param[in] scheduler Target scheduler.
* @param[out] io An io object to read from.
* @param[out] buffer Return buffer.
* @param[in] length Requested number of bytes to read.
* @retval RUBY_Qundef `scheduler` doesn't have `#io_read`.
* @return otherwise What `scheduler.io_read` returns.
*/
VALUE rb_fiber_scheduler_io_read_memory(VALUE scheduler, VALUE io, void *buffer, size_t size, size_t length);
/**
* Nonblocking write to the passed IO using a native buffer.
*
* @param[in] scheduler Target scheduler.
* @param[out] io An io object to write to.
* @param[in] buffer What to write.
* @param[in] length Number of bytes to write.
* @retval RUBY_Qundef `scheduler` doesn't have `#io_write`.
* @return otherwise What `scheduler.io_write` returns.
*/
VALUE rb_fiber_scheduler_io_write_memory(VALUE scheduler, VALUE io, const void *buffer, size_t size, size_t length);
/**
* Nonblocking close the given IO.
*
* @param[in] scheduler Target scheduler.
* @param[in] io An io object to close.
* @retval RUBY_Qundef `scheduler` doesn't have `#io_close`.
* @return otherwise What `scheduler.io_close` returns.
*/
VALUE rb_fiber_scheduler_io_close(VALUE scheduler, VALUE io);
/**
* Nonblocking DNS lookup.
*
* @param[in] scheduler Target scheduler.
* @param[in] hostname A host name to query.
* @retval RUBY_Qundef `scheduler` doesn't have `#address_resolve`.
* @return otherwise What `scheduler.address_resolve` returns.
*/
VALUE rb_fiber_scheduler_address_resolve(VALUE scheduler, VALUE hostname);
RBIMPL_SYMBOL_EXPORT_END()
#endif /* RUBY_FIBER_SCHEDULER_H */
|