polyphony 0.55.0 → 0.59.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2f0f6698a072e4ba2913477d20f5884d5d7579b99cca1db0f2ffa3aeca42ebcc
4
- data.tar.gz: 97fec9986f6693d685bfb0c1745995c8a85413a9b6cc1a1ff69393d55a5f3b74
3
+ metadata.gz: 3aba9d551e940893d6d3303d93d514b520b7b649a1db99b101bdbbcdec762501
4
+ data.tar.gz: 601d33709dc92bb0c029d9dba5d555441a2b5a6721b807185a36d65cb5ef8170
5
5
  SHA512:
6
- metadata.gz: 053beffc7c27658d129002b5efc29f64c7fcf1ff7fbf3f2eea1d588b2a9e40e89251640bf9bb82ce478837f38c32863987d8ac0af987d374d4ac242d012363dd
7
- data.tar.gz: e89df4044f09f4df64fc57622f22e70d932f2f09ea3b2a5c2090698784c286367df43d352231068b8ef96d6a7ba89e5492b4c237c9960aeb5fa79af9ee738768
6
+ metadata.gz: 2a7e355b5f8857093082d75759942f5d3a63ab40cf3a0f46045829e2c223ae92dbcc370ceb2dd3f26bd04e0bb036eb03ab40ef65a011cd172dd6c122698541fa
7
+ data.tar.gz: e8708853190d44c82f3088ba0baecf19f03110a11c2cf229f0015c800b23da01e1eb0d94af20cb2af7fd8dd7bdafde9bf218f484c57cda358c0da187dfeb4b6e
data/CHANGELOG.md CHANGED
@@ -1,3 +1,29 @@
1
+ ## 0.59.1 2021-06-28
2
+
3
+ - Accept fiber tag in `Polyphony::Timer.new`
4
+
5
+ ## 0.59 2021-06-28
6
+
7
+ - Redesign tracing mechanism and API - now completely separated from Ruby core
8
+ trace API
9
+ - Refactor C code - move run queue into backend
10
+
11
+ ## 0.58 2021-06-25
12
+
13
+ - Implement `Thread#idle_gc_period`, `#on_idle` (#56)
14
+ - Implement `Backend#idle_block=` (#56)
15
+
16
+ ## 0.57.0 2021-06-23
17
+
18
+ - Implement `Backend#splice_chunks` method for both libev and io_uring backends
19
+ - Improve waiting for readiness in libev `Backend#splice`, `#splice_to_eof`
20
+ - Enable splice op in libev `Backend#chain` for non-Linux OS
21
+
22
+ ## 0.56.0 2021-06-22
23
+
24
+ - Implement fake `Backend#splice`, `Backend#splice_to_eof` methods for non-Linux
25
+ OS
26
+
1
27
  ## 0.55.0 2021-06-17
2
28
 
3
29
  - Finish io_uring implementation of Backend#chain
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- polyphony (0.55.0)
4
+ polyphony (0.59.1)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+
5
+ require 'polyphony'
6
+
7
+ GC.disable
8
+
9
+ p count: GC.count
10
+ snooze
11
+ p count_after_snooze: GC.count
12
+ sleep 0.1
13
+ p count_after_sleep: GC.count
14
+
15
+ Thread.current.backend.idle_gc_period = 60
16
+
17
+ p count: GC.count
18
+ snooze
19
+ p count_after_snooze: GC.count
20
+ sleep 0.1
21
+ p count_after_sleep: GC.count
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+ require 'polyphony'
5
+
6
+ i, o = IO.pipe
7
+ f = spin { p i.read }
8
+
9
+ o << 'hello'
10
+ o.close
11
+ f.await
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+ require 'polyphony'
5
+
6
+ i, o = IO.pipe
7
+
8
+ f = spin do
9
+ i.read_loop { |data| STDOUT << data }
10
+ end
11
+
12
+ result = nil
13
+ # File.open(__FILE__, 'r') do |f|
14
+ File.open('../tipi/log', 'r') do |f|
15
+ result = Thread.current.backend.splice_chunks(
16
+ f,
17
+ o,
18
+ "Content-Type: ruby\n\n",
19
+ "0\r\n\r\n",
20
+ ->(len) { "#{len.to_s(16)}\r\n" },
21
+ "\r\n",
22
+ 16384
23
+ )
24
+ end
25
+
26
+
27
+ o.close
28
+ f.await
29
+ p result: result
@@ -0,0 +1,288 @@
1
+ #include <time.h>
2
+ #include <fcntl.h>
3
+ #include "ruby.h"
4
+ #include "ruby/io.h"
5
+ #include "polyphony.h"
6
+ #include "backend_common.h"
7
+
8
+ inline void backend_base_initialize(struct Backend_base *base) {
9
+ runqueue_initialize(&base->runqueue);
10
+ base->currently_polling = 0;
11
+ base->pending_count = 0;
12
+ base->idle_gc_period = 0;
13
+ base->idle_gc_last_time = 0;
14
+ base->idle_proc = Qnil;
15
+ base->trace_proc = Qnil;
16
+ }
17
+
18
+ inline void backend_base_finalize(struct Backend_base *base) {
19
+ runqueue_finalize(&base->runqueue);
20
+ }
21
+
22
+ inline void backend_base_mark(struct Backend_base *base) {
23
+ if (base->idle_proc != Qnil) rb_gc_mark(base->idle_proc);
24
+ if (base->trace_proc != Qnil) rb_gc_mark(base->trace_proc);
25
+ runqueue_mark(&base->runqueue);
26
+ }
27
+
28
+ VALUE backend_base_switch_fiber(VALUE backend, struct Backend_base *base) {
29
+ VALUE current_fiber = rb_fiber_current();
30
+ runqueue_entry next;
31
+ unsigned int pending_ops_count = base->pending_count;
32
+ unsigned int backend_was_polled = 0;
33
+ unsigned int idle_tasks_run_count = 0;
34
+
35
+ if (SHOULD_TRACE(base) && (rb_ivar_get(current_fiber, ID_ivar_running) != Qfalse))
36
+ TRACE(base, 2, SYM_fiber_switchpoint, current_fiber);
37
+
38
+ while (1) {
39
+ next = runqueue_shift(&base->runqueue);
40
+ if (next.fiber != Qnil) {
41
+ // Polling for I/O op completion is normally done when the run queue is
42
+ // empty, but if the runqueue never empties, we'll never get to process
43
+ // any event completions. In order to prevent this, an anti-starve
44
+ // mechanism is employed, under the following conditions:
45
+ // - a blocking poll was not yet performed
46
+ // - there are pending blocking operations
47
+ // - the runqueue has signalled that a non-blocking poll should be
48
+ // performed
49
+ // - the run queue length high watermark has reached its threshold (currently 128)
50
+ // - the run queue switch counter has reached its threshold (currently 64)
51
+ if (!backend_was_polled && pending_ops_count && runqueue_should_poll_nonblocking(&base->runqueue)) {
52
+ // this prevents event starvation in case the run queue never empties
53
+ Backend_poll(backend, Qnil);
54
+ }
55
+ break;
56
+ }
57
+
58
+ if (!idle_tasks_run_count) {
59
+ idle_tasks_run_count++;
60
+ backend_run_idle_tasks(base);
61
+ }
62
+ if (pending_ops_count == 0) break;
63
+ Backend_poll(backend, Qtrue);
64
+ backend_was_polled = 1;
65
+ }
66
+
67
+ if (next.fiber == Qnil) return Qnil;
68
+
69
+ // run next fiber
70
+ COND_TRACE(base, 3, SYM_fiber_run, next.fiber, next.value);
71
+
72
+ rb_ivar_set(next.fiber, ID_ivar_runnable, Qnil);
73
+ RB_GC_GUARD(next.fiber);
74
+ RB_GC_GUARD(next.value);
75
+ return (next.fiber == current_fiber) ?
76
+ next.value : FIBER_TRANSFER(next.fiber, next.value);
77
+ }
78
+
79
+ void backend_base_schedule_fiber(VALUE thread, VALUE backend, struct Backend_base *base, VALUE fiber, VALUE value, int prioritize) {
80
+ int already_runnable;
81
+
82
+ if (rb_fiber_alive_p(fiber) != Qtrue) return;
83
+ already_runnable = rb_ivar_get(fiber, ID_ivar_runnable) != Qnil;
84
+
85
+ COND_TRACE(base, 3, SYM_fiber_schedule, fiber, value);
86
+ (prioritize ? runqueue_unshift : runqueue_push)(&base->runqueue, fiber, value, already_runnable);
87
+ if (!already_runnable) {
88
+ rb_ivar_set(fiber, ID_ivar_runnable, Qtrue);
89
+ if (rb_thread_current() != thread) {
90
+ // If the fiber scheduling is done across threads, we need to make sure the
91
+ // target thread is woken up in case it is in the middle of running its
92
+ // event selector. Otherwise it's gonna be stuck waiting for an event to
93
+ // happen, not knowing that it there's already a fiber ready to run in its
94
+ // run queue.
95
+ Backend_wakeup(backend);
96
+ }
97
+ }
98
+ }
99
+
100
+
101
+ inline void backend_trace(struct Backend_base *base, int argc, VALUE *argv) {
102
+ if (base->trace_proc == Qnil) return;
103
+
104
+ rb_funcallv(base->trace_proc, ID_call, argc, argv);
105
+ }
106
+
107
+ #ifdef POLYPHONY_USE_PIDFD_OPEN
108
+ #ifndef __NR_pidfd_open
109
+ #define __NR_pidfd_open 434 /* System call # on most architectures */
110
+ #endif
111
+
112
+ inline int pidfd_open(pid_t pid, unsigned int flags) {
113
+ return syscall(__NR_pidfd_open, pid, flags);
114
+ }
115
+ #endif
116
+
117
+ //////////////////////////////////////////////////////////////////////
118
+ //////////////////////////////////////////////////////////////////////
119
+ // the following is copied verbatim from the Ruby source code (io.c)
120
+
121
+ inline int io_setstrbuf(VALUE *str, long len) {
122
+ #ifdef _WIN32
123
+ len = (len + 1) & ~1L; /* round up for wide char */
124
+ #endif
125
+ if (*str == Qnil) {
126
+ *str = rb_str_new(0, len);
127
+ return 1;
128
+ }
129
+ else {
130
+ VALUE s = StringValue(*str);
131
+ long clen = RSTRING_LEN(s);
132
+ if (clen >= len) {
133
+ rb_str_modify(s);
134
+ return 0;
135
+ }
136
+ len -= clen;
137
+ }
138
+ rb_str_modify_expand(*str, len);
139
+ return 0;
140
+ }
141
+
142
+ #define MAX_REALLOC_GAP 4096
143
+
144
+ inline void io_shrink_read_string(VALUE str, long n) {
145
+ if (rb_str_capacity(str) - n > MAX_REALLOC_GAP) {
146
+ rb_str_resize(str, n);
147
+ }
148
+ }
149
+
150
+ inline void io_set_read_length(VALUE str, long n, int shrinkable) {
151
+ if (RSTRING_LEN(str) != n) {
152
+ rb_str_modify(str);
153
+ rb_str_set_len(str, n);
154
+ if (shrinkable) io_shrink_read_string(str, n);
155
+ }
156
+ }
157
+
158
+ inline rb_encoding* io_read_encoding(rb_io_t *fptr) {
159
+ if (fptr->encs.enc) {
160
+ return fptr->encs.enc;
161
+ }
162
+ return rb_default_external_encoding();
163
+ }
164
+
165
+ inline VALUE io_enc_str(VALUE str, rb_io_t *fptr) {
166
+ OBJ_TAINT(str);
167
+ rb_enc_associate(str, io_read_encoding(fptr));
168
+ return str;
169
+ }
170
+
171
+ //////////////////////////////////////////////////////////////////////
172
+ //////////////////////////////////////////////////////////////////////
173
+
174
+ VALUE backend_await(struct Backend_base *backend) {
175
+ VALUE ret;
176
+ backend->pending_count++;
177
+ ret = Thread_switch_fiber(rb_thread_current());
178
+ backend->pending_count--;
179
+ RB_GC_GUARD(ret);
180
+ return ret;
181
+ }
182
+
183
+ VALUE backend_snooze() {
184
+ Fiber_make_runnable(rb_fiber_current(), Qnil);
185
+ VALUE ret = Thread_switch_fiber(rb_thread_current());
186
+ return ret;
187
+ }
188
+
189
+ inline void rectify_io_file_pos(rb_io_t *fptr) {
190
+ // Apparently after reopening a closed file, the file position is not reset,
191
+ // which causes the read to fail. Fortunately we can use fptr->rbuf.len to
192
+ // find out if that's the case.
193
+ // See: https://github.com/digital-fabric/polyphony/issues/30
194
+ if (fptr->rbuf.len > 0) {
195
+ lseek(fptr->fd, -fptr->rbuf.len, SEEK_CUR);
196
+ fptr->rbuf.len = 0;
197
+ }
198
+ }
199
+
200
+ inline double current_time() {
201
+ struct timespec ts;
202
+ clock_gettime(CLOCK_MONOTONIC, &ts);
203
+ long long ns = ts.tv_sec;
204
+ ns = ns * 1e9 + ts.tv_nsec;
205
+ double t = ns;
206
+ return t / 1e9;
207
+ }
208
+
209
+ inline VALUE backend_timeout_exception(VALUE exception) {
210
+ if (rb_obj_is_kind_of(exception, rb_cArray) == Qtrue)
211
+ return rb_funcall(rb_ary_entry(exception, 0), ID_new, 1, rb_ary_entry(exception, 1));
212
+ else if (rb_obj_is_kind_of(exception, rb_cClass) == Qtrue)
213
+ return rb_funcall(exception, ID_new, 0);
214
+ else
215
+ return rb_funcall(rb_eRuntimeError, ID_new, 1, exception);
216
+ }
217
+
218
+ VALUE Backend_timeout_safe(VALUE arg) {
219
+ return rb_yield(arg);
220
+ }
221
+
222
+ VALUE Backend_timeout_rescue(VALUE arg, VALUE exception) {
223
+ return exception;
224
+ }
225
+
226
+ VALUE Backend_timeout_ensure_safe(VALUE arg) {
227
+ return rb_rescue2(Backend_timeout_safe, Qnil, Backend_timeout_rescue, Qnil, rb_eException, (VALUE)0);
228
+ }
229
+
230
+ static VALUE empty_string = Qnil;
231
+
232
+ VALUE Backend_sendv(VALUE self, VALUE io, VALUE ary, VALUE flags) {
233
+ switch (RARRAY_LEN(ary)) {
234
+ case 0:
235
+ return Qnil;
236
+ case 1:
237
+ return Backend_send(self, io, RARRAY_AREF(ary, 0), flags);
238
+ default:
239
+ if (empty_string == Qnil) {
240
+ empty_string = rb_str_new_literal("");
241
+ rb_global_variable(&empty_string);
242
+ }
243
+ VALUE joined = rb_ary_join(ary, empty_string);
244
+ VALUE result = Backend_send(self, io, joined, flags);
245
+ RB_GC_GUARD(joined);
246
+ return result;
247
+ }
248
+ }
249
+
250
+ inline void io_verify_blocking_mode(rb_io_t *fptr, VALUE io, VALUE blocking) {
251
+ VALUE blocking_mode = rb_ivar_get(io, ID_ivar_blocking_mode);
252
+ if (blocking == blocking_mode) return;
253
+
254
+ rb_ivar_set(io, ID_ivar_blocking_mode, blocking);
255
+
256
+ #ifdef _WIN32
257
+ if (blocking != Qtrue)
258
+ rb_w32_set_nonblock(fptr->fd);
259
+ #elif defined(F_GETFL)
260
+ int flags = fcntl(fptr->fd, F_GETFL);
261
+ if (flags == -1) return;
262
+ int is_nonblocking = flags & O_NONBLOCK;
263
+
264
+ if (blocking == Qtrue) {
265
+ if (!is_nonblocking) return;
266
+ flags &= ~O_NONBLOCK;
267
+ } else {
268
+ if (is_nonblocking) return;
269
+ flags |= O_NONBLOCK;
270
+ }
271
+ fcntl(fptr->fd, F_SETFL, flags);
272
+ #endif
273
+ }
274
+
275
+ inline void backend_run_idle_tasks(struct Backend_base *base) {
276
+ if (base->idle_proc != Qnil)
277
+ rb_funcall(base->idle_proc, ID_call, 0);
278
+
279
+ if (base->idle_gc_period == 0) return;
280
+
281
+ double now = current_time();
282
+ if (now - base->idle_gc_last_time < base->idle_gc_period) return;
283
+
284
+ base->idle_gc_last_time = now;
285
+ rb_gc_enable();
286
+ rb_gc_start();
287
+ rb_gc_disable();
288
+ }
@@ -1,17 +1,42 @@
1
- #include <time.h>
1
+ #ifndef BACKEND_COMMON_H
2
+ #define BACKEND_COMMON_H
2
3
 
3
4
  #include "ruby.h"
4
5
  #include "ruby/io.h"
6
+ #include "runqueue.h"
5
7
 
8
+ struct backend_stats {
9
+ int scheduled_fibers;
10
+ int waiting_fibers;
11
+ int pending_ops;
12
+ };
6
13
 
7
- #ifdef POLYPHONY_USE_PIDFD_OPEN
8
- #ifndef __NR_pidfd_open
9
- #define __NR_pidfd_open 434 /* System call # on most architectures */
10
- #endif
14
+ struct Backend_base {
15
+ runqueue_t runqueue;
16
+ unsigned int currently_polling;
17
+ unsigned int pending_count;
18
+ double idle_gc_period;
19
+ double idle_gc_last_time;
20
+ VALUE idle_proc;
21
+ VALUE trace_proc;
22
+ };
11
23
 
12
- static int pidfd_open(pid_t pid, unsigned int flags) {
13
- return syscall(__NR_pidfd_open, pid, flags);
14
- }
24
+ void backend_base_initialize(struct Backend_base *base);
25
+ void backend_base_finalize(struct Backend_base *base);
26
+ void backend_base_mark(struct Backend_base *base);
27
+ VALUE backend_base_switch_fiber(VALUE backend, struct Backend_base *base);
28
+ void backend_base_schedule_fiber(VALUE thread, VALUE backend, struct Backend_base *base, VALUE fiber, VALUE value, int prioritize);
29
+ void backend_trace(struct Backend_base *base, int argc, VALUE *argv);
30
+
31
+ // tracing
32
+ #define SHOULD_TRACE(base) ((base)->trace_proc != Qnil)
33
+ #define TRACE(base, ...) rb_funcall((base)->trace_proc, ID_call, __VA_ARGS__)
34
+ #define COND_TRACE(base, ...) if (SHOULD_TRACE(base)) { TRACE(base, __VA_ARGS__); }
35
+
36
+
37
+
38
+ #ifdef POLYPHONY_USE_PIDFD_OPEN
39
+ int pidfd_open(pid_t pid, unsigned int flags);
15
40
  #endif
16
41
 
17
42
  //////////////////////////////////////////////////////////////////////
@@ -26,75 +51,19 @@ struct io_internal_read_struct {
26
51
 
27
52
  #define StringValue(v) rb_string_value(&(v))
28
53
 
29
- inline int io_setstrbuf(VALUE *str, long len) {
30
- #ifdef _WIN32
31
- len = (len + 1) & ~1L; /* round up for wide char */
32
- #endif
33
- if (*str == Qnil) {
34
- *str = rb_str_new(0, len);
35
- return 1;
36
- }
37
- else {
38
- VALUE s = StringValue(*str);
39
- long clen = RSTRING_LEN(s);
40
- if (clen >= len) {
41
- rb_str_modify(s);
42
- return 0;
43
- }
44
- len -= clen;
45
- }
46
- rb_str_modify_expand(*str, len);
47
- return 0;
48
- }
49
-
50
- #define MAX_REALLOC_GAP 4096
51
-
52
- inline void io_shrink_read_string(VALUE str, long n) {
53
- if (rb_str_capacity(str) - n > MAX_REALLOC_GAP) {
54
- rb_str_resize(str, n);
55
- }
56
- }
57
-
58
- inline void io_set_read_length(VALUE str, long n, int shrinkable) {
59
- if (RSTRING_LEN(str) != n) {
60
- rb_str_modify(str);
61
- rb_str_set_len(str, n);
62
- if (shrinkable) io_shrink_read_string(str, n);
63
- }
64
- }
65
-
66
- inline rb_encoding* io_read_encoding(rb_io_t *fptr) {
67
- if (fptr->encs.enc) {
68
- return fptr->encs.enc;
69
- }
70
- return rb_default_external_encoding();
71
- }
72
-
73
- inline VALUE io_enc_str(VALUE str, rb_io_t *fptr) {
74
- OBJ_TAINT(str);
75
- rb_enc_associate(str, io_read_encoding(fptr));
76
- return str;
77
- }
54
+ int io_setstrbuf(VALUE *str, long len);
55
+ void io_shrink_read_string(VALUE str, long n);
56
+ void io_set_read_length(VALUE str, long n, int shrinkable);
57
+ rb_encoding* io_read_encoding(rb_io_t *fptr);
58
+ VALUE io_enc_str(VALUE str, rb_io_t *fptr);
78
59
 
79
60
  //////////////////////////////////////////////////////////////////////
80
61
  //////////////////////////////////////////////////////////////////////
81
62
 
82
- inline VALUE backend_await(Backend_t *backend) {
83
- VALUE ret;
84
- backend->pending_count++;
85
- ret = Thread_switch_fiber(rb_thread_current());
86
- backend->pending_count--;
87
- RB_GC_GUARD(ret);
88
- return ret;
89
- }
90
-
91
- inline VALUE backend_snooze() {
92
- Fiber_make_runnable(rb_fiber_current(), Qnil);
93
- return Thread_switch_fiber(rb_thread_current());
94
- }
63
+ VALUE backend_await(struct Backend_base *backend);
64
+ VALUE backend_snooze();
95
65
 
96
66
  // macros for doing read loops
97
-
98
67
  #define READ_LOOP_PREPARE_STR() { \
99
68
  str = Qnil; \
100
69
  shrinkable = io_setstrbuf(&str, len); \
@@ -117,63 +86,13 @@ inline VALUE backend_snooze() {
117
86
  READ_LOOP_PREPARE_STR(); \
118
87
  }
119
88
 
120
- inline void rectify_io_file_pos(rb_io_t *fptr) {
121
- // Apparently after reopening a closed file, the file position is not reset,
122
- // which causes the read to fail. Fortunately we can use fptr->rbuf.len to
123
- // find out if that's the case.
124
- // See: https://github.com/digital-fabric/polyphony/issues/30
125
- if (fptr->rbuf.len > 0) {
126
- lseek(fptr->fd, -fptr->rbuf.len, SEEK_CUR);
127
- fptr->rbuf.len = 0;
128
- }
129
- }
130
-
131
- inline double current_time() {
132
- struct timespec ts;
133
- clock_gettime(CLOCK_MONOTONIC, &ts);
134
- long long ns = ts.tv_sec;
135
- ns = ns * 1e9 + ts.tv_nsec;
136
- double t = ns;
137
- return t / 1e9;
138
- }
139
-
140
- inline VALUE backend_timeout_exception(VALUE exception) {
141
- if (rb_obj_is_kind_of(exception, rb_cArray) == Qtrue)
142
- return rb_funcall(rb_ary_entry(exception, 0), ID_new, 1, rb_ary_entry(exception, 1));
143
- else if (rb_obj_is_kind_of(exception, rb_cClass) == Qtrue)
144
- return rb_funcall(exception, ID_new, 0);
145
- else
146
- return rb_funcall(rb_eRuntimeError, ID_new, 1, exception);
147
- }
89
+ void rectify_io_file_pos(rb_io_t *fptr);
90
+ double current_time();
91
+ VALUE backend_timeout_exception(VALUE exception);
92
+ VALUE Backend_timeout_ensure_safe(VALUE arg);
93
+ VALUE Backend_timeout_ensure_safe(VALUE arg);
94
+ VALUE Backend_sendv(VALUE self, VALUE io, VALUE ary, VALUE flags);
95
+ void backend_run_idle_tasks(struct Backend_base *base);
96
+ void io_verify_blocking_mode(rb_io_t *fptr, VALUE io, VALUE blocking);
148
97
 
149
- VALUE Backend_timeout_safe(VALUE arg) {
150
- return rb_yield(arg);
151
- }
152
-
153
- VALUE Backend_timeout_rescue(VALUE arg, VALUE exception) {
154
- return exception;
155
- }
156
-
157
- VALUE Backend_timeout_ensure_safe(VALUE arg) {
158
- return rb_rescue2(Backend_timeout_safe, Qnil, Backend_timeout_rescue, Qnil, rb_eException, (VALUE)0);
159
- }
160
-
161
- static VALUE empty_string = Qnil;
162
-
163
- VALUE Backend_sendv(VALUE self, VALUE io, VALUE ary, VALUE flags) {
164
- switch (RARRAY_LEN(ary)) {
165
- case 0:
166
- return Qnil;
167
- case 1:
168
- return Backend_send(self, io, RARRAY_AREF(ary, 0), flags);
169
- default:
170
- if (empty_string == Qnil) {
171
- empty_string = rb_str_new_literal("");
172
- rb_global_variable(&empty_string);
173
- }
174
- VALUE joined = rb_ary_join(ary, empty_string);
175
- VALUE result = Backend_send(self, io, joined, flags);
176
- RB_GC_GUARD(joined);
177
- return result;
178
- }
179
- }
98
+ #endif /* BACKEND_COMMON_H */