polyphony 0.53.0 → 0.53.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 +4 -0
- data/Gemfile.lock +1 -1
- data/TODO.md +4 -4
- data/ext/polyphony/backend_io_uring.c +10 -9
- data/ext/polyphony/backend_libev.c +41 -8
- data/ext/polyphony/thread.c +8 -2
- data/lib/polyphony/version.rb +1 -1
- data/test/test_backend.rb +87 -4
- data/test/test_thread.rb +4 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b769b5e71cbfa8679a639d369817abd8e5870efec8ea21feb60bb1e46ee7ca29
|
4
|
+
data.tar.gz: 0b2ded84eb0e120d20dd17c9973e2cab784cdf61c20bd20fcb6a13c2b6e9f58a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e6d3a3d2c130c31483ead4d8f13cc90a87149ac6c461871a43144111cdc37e6e2544c130dc447af26e39e9519b48e3abe1a10197dd4512a7292968e4939e676b
|
7
|
+
data.tar.gz: dc1b53918032fc74578ca528abb5a96e423ea8e7a070047f7936720cb6553d9f247cbb625e9d82c7b9b58795a4a3a74aae566231a7a4bb8443f5352c00d10b2a
|
data/CHANGELOG.md
CHANGED
data/Gemfile.lock
CHANGED
data/TODO.md
CHANGED
@@ -35,8 +35,8 @@
|
|
35
35
|
|
36
36
|
```ruby
|
37
37
|
Thread.current.backend.submit(
|
38
|
-
[sock,
|
39
|
-
[
|
38
|
+
[:send, sock, chunk_header(len)],
|
39
|
+
[:splice, file, sock, len]
|
40
40
|
)
|
41
41
|
```
|
42
42
|
|
@@ -51,8 +51,8 @@
|
|
51
51
|
break if len == 0
|
52
52
|
|
53
53
|
backend.submit(
|
54
|
-
[sock,
|
55
|
-
[
|
54
|
+
[:write, sock, chunk_header(len)],
|
55
|
+
[:splice, i, sock, len]
|
56
56
|
)
|
57
57
|
end
|
58
58
|
end
|
@@ -150,7 +150,7 @@ static inline bool cq_ring_needs_flush(struct io_uring *ring) {
|
|
150
150
|
return IO_URING_READ_ONCE(*ring->sq.kflags) & IORING_SQ_CQ_OVERFLOW;
|
151
151
|
}
|
152
152
|
|
153
|
-
void io_uring_backend_handle_completion(struct io_uring_cqe *cqe, Backend_t *backend) {
|
153
|
+
static inline void io_uring_backend_handle_completion(struct io_uring_cqe *cqe, Backend_t *backend) {
|
154
154
|
op_context_t *ctx = io_uring_cqe_get_data(cqe);
|
155
155
|
if (!ctx) return;
|
156
156
|
|
@@ -169,7 +169,7 @@ void io_uring_backend_handle_completion(struct io_uring_cqe *cqe, Backend_t *bac
|
|
169
169
|
}
|
170
170
|
|
171
171
|
// adapted from io_uring_peek_batch_cqe in queue.c
|
172
|
-
// this peeks at cqes and
|
172
|
+
// this peeks at cqes and handles each available cqe
|
173
173
|
void io_uring_backend_handle_ready_cqes(Backend_t *backend) {
|
174
174
|
struct io_uring *ring = &backend->ring;
|
175
175
|
bool overflow_checked = false;
|
@@ -315,8 +315,8 @@ VALUE Backend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof)
|
|
315
315
|
Backend_t *backend;
|
316
316
|
rb_io_t *fptr;
|
317
317
|
long dynamic_len = length == Qnil;
|
318
|
-
long
|
319
|
-
int shrinkable = io_setstrbuf(&str,
|
318
|
+
long buffer_size = dynamic_len ? 4096 : NUM2INT(length);
|
319
|
+
int shrinkable = io_setstrbuf(&str, buffer_size);
|
320
320
|
char *buf = RSTRING_PTR(str);
|
321
321
|
long total = 0;
|
322
322
|
int read_to_eof = RTEST(to_eof);
|
@@ -334,7 +334,7 @@ VALUE Backend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof)
|
|
334
334
|
VALUE resume_value = Qnil;
|
335
335
|
op_context_t *ctx = OP_CONTEXT_ACQUIRE(&backend->store, OP_READ);
|
336
336
|
struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
|
337
|
-
io_uring_prep_read(sqe, fptr->fd, buf,
|
337
|
+
io_uring_prep_read(sqe, fptr->fd, buf, buffer_size - total, -1);
|
338
338
|
|
339
339
|
int result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
|
340
340
|
OP_CONTEXT_RELEASE(&backend->store, ctx);
|
@@ -350,14 +350,15 @@ VALUE Backend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof)
|
|
350
350
|
total += result;
|
351
351
|
if (!read_to_eof) break;
|
352
352
|
|
353
|
-
if (total ==
|
353
|
+
if (total == buffer_size) {
|
354
354
|
if (!dynamic_len) break;
|
355
355
|
|
356
|
+
// resize buffer
|
356
357
|
rb_str_resize(str, total);
|
357
|
-
rb_str_modify_expand(str,
|
358
|
+
rb_str_modify_expand(str, buffer_size);
|
358
359
|
buf = RSTRING_PTR(str) + total;
|
359
360
|
shrinkable = 0;
|
360
|
-
|
361
|
+
buffer_size += buffer_size;
|
361
362
|
}
|
362
363
|
else buf += result;
|
363
364
|
}
|
@@ -857,8 +858,8 @@ VALUE io_uring_backend_splice(Backend_t *backend, VALUE src, VALUE dest, VALUE m
|
|
857
858
|
if (result < 0)
|
858
859
|
rb_syserr_fail(-result, strerror(-result));
|
859
860
|
|
860
|
-
if (result == 0 || !loop) return INT2NUM(total);
|
861
861
|
total += result;
|
862
|
+
if (result == 0 || !loop) return INT2NUM(total);
|
862
863
|
}
|
863
864
|
|
864
865
|
RB_GC_GUARD(resume_value);
|
@@ -58,6 +58,9 @@ thread.
|
|
58
58
|
#include "ruby/io.h"
|
59
59
|
|
60
60
|
VALUE SYM_libev;
|
61
|
+
VALUE SYM_send;
|
62
|
+
VALUE SYM_splice;
|
63
|
+
VALUE SYM_write;
|
61
64
|
|
62
65
|
ID ID_ivar_is_nonblocking;
|
63
66
|
|
@@ -773,6 +776,7 @@ error:
|
|
773
776
|
return RAISE_EXCEPTION(switchpoint_result);
|
774
777
|
}
|
775
778
|
|
779
|
+
#ifndef POLYPHONY_LINUX
|
776
780
|
VALUE Backend_splice(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
|
777
781
|
Backend_t *backend;
|
778
782
|
struct libev_io watcher;
|
@@ -782,10 +786,6 @@ VALUE Backend_splice(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
|
|
782
786
|
rb_io_t *dest_fptr;
|
783
787
|
int len;
|
784
788
|
|
785
|
-
#ifndef POLYPHONY_LINUX
|
786
|
-
rb_raise(rb_eRuntimeError, "splice not supported");
|
787
|
-
#endif
|
788
|
-
|
789
789
|
GetBackend(self, backend);
|
790
790
|
|
791
791
|
underlying_io = rb_ivar_get(src, ID_ivar_io);
|
@@ -840,10 +840,6 @@ VALUE Backend_splice_to_eof(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
|
|
840
840
|
int len;
|
841
841
|
int total = 0;
|
842
842
|
|
843
|
-
#ifndef POLYPHONY_LINUX
|
844
|
-
rb_raise(rb_eRuntimeError, "splice not supported");
|
845
|
-
#endif
|
846
|
-
|
847
843
|
GetBackend(self, backend);
|
848
844
|
|
849
845
|
underlying_io = rb_ivar_get(src, ID_ivar_io);
|
@@ -890,6 +886,7 @@ VALUE Backend_splice_to_eof(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
|
|
890
886
|
error:
|
891
887
|
return RAISE_EXCEPTION(switchpoint_result);
|
892
888
|
}
|
889
|
+
#endif
|
893
890
|
|
894
891
|
VALUE Backend_wait_io(VALUE self, VALUE io, VALUE write) {
|
895
892
|
Backend_t *backend;
|
@@ -1103,6 +1100,31 @@ VALUE Backend_kind(VALUE self) {
|
|
1103
1100
|
return SYM_libev;
|
1104
1101
|
}
|
1105
1102
|
|
1103
|
+
VALUE Backend_chain(int argc,VALUE *argv, VALUE self) {
|
1104
|
+
VALUE result = Qnil;
|
1105
|
+
if (argc == 0) return result;
|
1106
|
+
|
1107
|
+
for (int i = 0; i < argc; i++) {
|
1108
|
+
VALUE op = argv[i];
|
1109
|
+
VALUE op_type = RARRAY_AREF(op, 0);
|
1110
|
+
VALUE op_len = RARRAY_LEN(op);
|
1111
|
+
|
1112
|
+
if (op_type == SYM_write && op_len == 3)
|
1113
|
+
result = Backend_write(self, RARRAY_AREF(op, 1), RARRAY_AREF(op, 2));
|
1114
|
+
else if (op_type == SYM_send && op_len == 4)
|
1115
|
+
result = Backend_send(self, RARRAY_AREF(op, 1), RARRAY_AREF(op, 2), RARRAY_AREF(op, 3));
|
1116
|
+
#ifndef POLYPHONY_LINUX
|
1117
|
+
else if (op_type == SYM_splice && op_len == 4)
|
1118
|
+
result = Backend_splice(self, RARRAY_AREF(op, 1), RARRAY_AREF(op, 2), RARRAY_AREF(op, 3));
|
1119
|
+
#endif
|
1120
|
+
else
|
1121
|
+
rb_raise(rb_eRuntimeError, "Invalid op specified or bad op arity");
|
1122
|
+
}
|
1123
|
+
|
1124
|
+
RB_GC_GUARD(result);
|
1125
|
+
return result;
|
1126
|
+
}
|
1127
|
+
|
1106
1128
|
void Init_Backend() {
|
1107
1129
|
ev_set_allocator(xrealloc);
|
1108
1130
|
|
@@ -1116,6 +1138,7 @@ void Init_Backend() {
|
|
1116
1138
|
rb_define_method(cBackend, "poll", Backend_poll, 3);
|
1117
1139
|
rb_define_method(cBackend, "break", Backend_wakeup, 0);
|
1118
1140
|
rb_define_method(cBackend, "kind", Backend_kind, 0);
|
1141
|
+
rb_define_method(cBackend, "chain", Backend_chain, -1);
|
1119
1142
|
|
1120
1143
|
rb_define_method(cBackend, "accept", Backend_accept, 2);
|
1121
1144
|
rb_define_method(cBackend, "accept_loop", Backend_accept_loop, 2);
|
@@ -1129,6 +1152,12 @@ void Init_Backend() {
|
|
1129
1152
|
rb_define_method(cBackend, "send", Backend_send, 3);
|
1130
1153
|
rb_define_method(cBackend, "sendv", Backend_sendv, 3);
|
1131
1154
|
rb_define_method(cBackend, "sleep", Backend_sleep, 1);
|
1155
|
+
|
1156
|
+
#ifndef POLYPHONY_LINUX
|
1157
|
+
rb_define_method(cBackend, "splice", Backend_splice, 3);
|
1158
|
+
rb_define_method(cBackend, "splice_to_eof", Backend_splice_to_eof, 3);
|
1159
|
+
#endif
|
1160
|
+
|
1132
1161
|
rb_define_method(cBackend, "timeout", Backend_timeout, -1);
|
1133
1162
|
rb_define_method(cBackend, "timer_loop", Backend_timer_loop, 1);
|
1134
1163
|
rb_define_method(cBackend, "wait_event", Backend_wait_event, 1);
|
@@ -1138,6 +1167,10 @@ void Init_Backend() {
|
|
1138
1167
|
|
1139
1168
|
ID_ivar_is_nonblocking = rb_intern("@is_nonblocking");
|
1140
1169
|
SYM_libev = ID2SYM(rb_intern("libev"));
|
1170
|
+
|
1171
|
+
SYM_send = ID2SYM(rb_intern("send"));
|
1172
|
+
SYM_splice = ID2SYM(rb_intern("splice"));
|
1173
|
+
SYM_write = ID2SYM(rb_intern("write"));
|
1141
1174
|
}
|
1142
1175
|
|
1143
1176
|
#endif // POLYPHONY_BACKEND_LIBEV
|
data/ext/polyphony/thread.c
CHANGED
@@ -21,7 +21,7 @@ static VALUE SYM_scheduled_fibers;
|
|
21
21
|
static VALUE SYM_pending_watchers;
|
22
22
|
|
23
23
|
static VALUE Thread_fiber_scheduling_stats(VALUE self) {
|
24
|
-
VALUE backend = rb_ivar_get(self,ID_ivar_backend);
|
24
|
+
VALUE backend = rb_ivar_get(self, ID_ivar_backend);
|
25
25
|
VALUE stats = rb_hash_new();
|
26
26
|
VALUE runqueue = rb_ivar_get(self, ID_ivar_runqueue);
|
27
27
|
long pending_count;
|
@@ -53,7 +53,7 @@ void schedule_fiber(VALUE self, VALUE fiber, VALUE value, int prioritize) {
|
|
53
53
|
// event selector. Otherwise it's gonna be stuck waiting for an event to
|
54
54
|
// happen, not knowing that it there's already a fiber ready to run in its
|
55
55
|
// run queue.
|
56
|
-
VALUE backend = rb_ivar_get(self,ID_ivar_backend);
|
56
|
+
VALUE backend = rb_ivar_get(self, ID_ivar_backend);
|
57
57
|
Backend_wakeup(backend);
|
58
58
|
}
|
59
59
|
}
|
@@ -138,6 +138,10 @@ VALUE Thread_debug(VALUE self) {
|
|
138
138
|
return self;
|
139
139
|
}
|
140
140
|
|
141
|
+
VALUE Thread_class_backend(VALUE _self) {
|
142
|
+
return rb_ivar_get(rb_thread_current(), ID_ivar_backend);
|
143
|
+
}
|
144
|
+
|
141
145
|
void Init_Thread() {
|
142
146
|
rb_define_method(rb_cThread, "setup_fiber_scheduling", Thread_setup_fiber_scheduling, 0);
|
143
147
|
rb_define_method(rb_cThread, "fiber_scheduling_stats", Thread_fiber_scheduling_stats, 0);
|
@@ -150,6 +154,8 @@ void Init_Thread() {
|
|
150
154
|
rb_define_method(rb_cThread, "fiber_scheduling_index", Thread_fiber_scheduling_index, 1);
|
151
155
|
rb_define_method(rb_cThread, "fiber_unschedule", Thread_fiber_unschedule, 1);
|
152
156
|
|
157
|
+
rb_define_singleton_method(rb_cThread, "backend", Thread_class_backend, 0);
|
158
|
+
|
153
159
|
rb_define_method(rb_cThread, "debug!", Thread_debug, 0);
|
154
160
|
|
155
161
|
ID_deactivate_all_watchers_post_fork = rb_intern("deactivate_all_watchers_post_fork");
|
data/lib/polyphony/version.rb
CHANGED
data/test/test_backend.rb
CHANGED
@@ -241,9 +241,10 @@ class BackendTest < MiniTest::Test
|
|
241
241
|
def test_splice
|
242
242
|
i1, o1 = IO.pipe
|
243
243
|
i2, o2 = IO.pipe
|
244
|
+
len = nil
|
244
245
|
|
245
246
|
spin {
|
246
|
-
o2.splice(i1, 1000)
|
247
|
+
len = o2.splice(i1, 1000)
|
247
248
|
o2.close
|
248
249
|
}
|
249
250
|
|
@@ -251,14 +252,16 @@ class BackendTest < MiniTest::Test
|
|
251
252
|
result = i2.read
|
252
253
|
|
253
254
|
assert_equal 'foobar', result
|
255
|
+
assert_equal 6, len
|
254
256
|
end
|
255
257
|
|
256
258
|
def test_splice_to_eof
|
257
259
|
i1, o1 = IO.pipe
|
258
260
|
i2, o2 = IO.pipe
|
261
|
+
len = nil
|
259
262
|
|
260
263
|
f = spin {
|
261
|
-
o2.splice_to_eof(i1, 1000)
|
264
|
+
len = o2.splice_to_eof(i1, 1000)
|
262
265
|
o2.close
|
263
266
|
}
|
264
267
|
|
@@ -269,8 +272,88 @@ class BackendTest < MiniTest::Test
|
|
269
272
|
o1.write('bar')
|
270
273
|
result = i2.readpartial(1000)
|
271
274
|
assert_equal 'bar', result
|
272
|
-
|
273
|
-
f.interrupt
|
275
|
+
o1.close
|
274
276
|
f.await
|
277
|
+
assert_equal 6, len
|
278
|
+
ensure
|
279
|
+
if f.alive?
|
280
|
+
f.interrupt
|
281
|
+
f.await
|
282
|
+
end
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
class BackendChainTest < MiniTest::Test
|
287
|
+
def setup
|
288
|
+
super
|
289
|
+
@prev_backend = Thread.current.backend
|
290
|
+
@backend = Polyphony::Backend.new
|
291
|
+
Thread.current.backend = @backend
|
292
|
+
end
|
293
|
+
|
294
|
+
def teardown
|
295
|
+
@backend.finalize
|
296
|
+
Thread.current.backend = @prev_backend
|
297
|
+
end
|
298
|
+
|
299
|
+
def test_simple_write_chain
|
300
|
+
i, o = IO.pipe
|
301
|
+
|
302
|
+
result = Thread.backend.chain(
|
303
|
+
[:write, o, 'hello'],
|
304
|
+
[:write, o, ' world']
|
305
|
+
)
|
306
|
+
|
307
|
+
assert_equal 6, result
|
308
|
+
o.close
|
309
|
+
assert_equal 'hello world', i.read
|
310
|
+
end
|
311
|
+
|
312
|
+
def chunk_header(len)
|
313
|
+
"Content-Length: #{len}\r\n\r\n"
|
314
|
+
end
|
315
|
+
|
316
|
+
def serve_io(from, to)
|
317
|
+
i, o = IO.pipe
|
318
|
+
backend = Thread.current.backend
|
319
|
+
while true
|
320
|
+
len = o.splice(from, 8192)
|
321
|
+
break if len == 0
|
322
|
+
|
323
|
+
backend.chain(
|
324
|
+
[:write, to, chunk_header(len)],
|
325
|
+
[:splice, i, to, len]
|
326
|
+
)
|
327
|
+
end
|
328
|
+
to.close
|
329
|
+
end
|
330
|
+
|
331
|
+
def test_chain_with_splice
|
332
|
+
from_r, from_w = IO.pipe
|
333
|
+
to_r, to_w = IO.pipe
|
334
|
+
|
335
|
+
result = nil
|
336
|
+
f = spin { serve_io(from_r, to_w) }
|
337
|
+
|
338
|
+
from_w << 'Hello world!'
|
339
|
+
from_w.close
|
340
|
+
|
341
|
+
assert_equal "Content-Length: 12\r\n\r\nHello world!", to_r.read
|
342
|
+
end
|
343
|
+
|
344
|
+
def test_invalid_op
|
345
|
+
i, o = IO.pipe
|
346
|
+
|
347
|
+
assert_raises(RuntimeError) {
|
348
|
+
Thread.backend.chain(
|
349
|
+
[:read, o]
|
350
|
+
)
|
351
|
+
}
|
352
|
+
|
353
|
+
assert_raises(TypeError) {
|
354
|
+
Thread.backend.chain(
|
355
|
+
[:write, o]
|
356
|
+
)
|
357
|
+
}
|
275
358
|
end
|
276
359
|
end
|
data/test/test_thread.rb
CHANGED
@@ -124,6 +124,10 @@ class ThreadTest < MiniTest::Test
|
|
124
124
|
t&.join
|
125
125
|
end
|
126
126
|
|
127
|
+
def test_backend_class_method
|
128
|
+
assert_equal Thread.current.backend, Thread.backend
|
129
|
+
end
|
130
|
+
|
127
131
|
def test_that_suspend_returns_immediately_if_no_watchers
|
128
132
|
records = []
|
129
133
|
t = Polyphony::Trace.new(:fiber_all) do |r|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: polyphony
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.53.
|
4
|
+
version: 0.53.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sharon Rosner
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-05-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake-compiler
|