polyphony 0.55.0 → 0.59.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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 */