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 +4 -4
- data/CHANGELOG.md +26 -0
- data/Gemfile.lock +1 -1
- data/examples/core/idle_gc.rb +21 -0
- data/examples/io/pipe.rb +11 -0
- data/examples/io/splice_chunks.rb +29 -0
- data/ext/polyphony/backend_common.c +288 -0
- data/ext/polyphony/backend_common.h +49 -130
- data/ext/polyphony/backend_io_uring.c +263 -54
- data/ext/polyphony/backend_io_uring_context.c +2 -3
- data/ext/polyphony/backend_libev.c +466 -84
- data/ext/polyphony/fiber.c +0 -2
- data/ext/polyphony/polyphony.c +17 -22
- data/ext/polyphony/polyphony.h +8 -16
- data/ext/polyphony/polyphony_ext.c +0 -4
- data/ext/polyphony/runqueue.c +17 -82
- data/ext/polyphony/runqueue.h +27 -0
- data/ext/polyphony/thread.c +10 -94
- data/lib/polyphony/core/timer.rb +2 -2
- data/lib/polyphony/extensions/fiber.rb +2 -2
- data/lib/polyphony/extensions/thread.rb +8 -0
- data/lib/polyphony/version.rb +1 -1
- data/test/test_backend.rb +91 -0
- data/test/test_thread.rb +57 -11
- data/test/test_timer.rb +7 -7
- data/test/test_trace.rb +27 -49
- metadata +7 -4
- data/ext/polyphony/tracing.c +0 -11
- data/lib/polyphony/adapters/trace.rb +0 -138
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3aba9d551e940893d6d3303d93d514b520b7b649a1db99b101bdbbcdec762501
|
4
|
+
data.tar.gz: 601d33709dc92bb0c029d9dba5d555441a2b5a6721b807185a36d65cb5ef8170
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
@@ -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
|
data/examples/io/pipe.rb
ADDED
@@ -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
|
-
#
|
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
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
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
|
-
|
13
|
-
|
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
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
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
|
-
|
83
|
-
|
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
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
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
|
-
|
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 */
|