polyphony 0.86 → 0.90
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/.github/workflows/test_io_uring.yml +8 -4
- data/CHANGELOG.md +16 -0
- data/Gemfile.lock +1 -1
- data/examples/io/http1_splice_chunked.rb +8 -0
- data/examples/io/static_web_server.rb +46 -0
- data/ext/polyphony/backend_common.c +1 -3
- data/ext/polyphony/backend_io_uring.c +46 -31
- data/ext/polyphony/backend_libev.c +5 -2
- data/ext/polyphony/extconf.rb +4 -2
- data/ext/polyphony/io_extensions.c +162 -54
- data/ext/polyphony/polyphony.c +7 -8
- data/ext/polyphony/polyphony.h +5 -0
- data/lib/polyphony/version.rb +1 -1
- data/test/test_io.rb +204 -9
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d9f125403a124d0374ebef23adfd4fe7f553767797003d63513ef780a248370b
|
4
|
+
data.tar.gz: 682134740af1614ac5e31d49c7d6a8ac8b881bbc26e5911e2f12a81f5ea52b27
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d6292c9f2ddd6faf3d8df9565f8e6dce3be9ecd64fff95201bd2907cfacdb46eac5ef87a9281a3e88324471951493b6ebaa150dad40afb6a19958dd1e5992c0c
|
7
|
+
data.tar.gz: 10f8e607a0bd403664318e03abd4bbb4ab16704a69d1c0858ce5c9269c5c883e081dd95c51488dce7732ae471bf04cd139c0cbfb9623d6751a857551304e60f5
|
@@ -8,15 +8,19 @@ jobs:
|
|
8
8
|
fail-fast: false
|
9
9
|
matrix:
|
10
10
|
os: [ubuntu-latest]
|
11
|
-
ruby: [2.
|
11
|
+
ruby: ['2.7', '3.0', '3.1', 'head']
|
12
12
|
|
13
13
|
name: >-
|
14
14
|
${{matrix.os}}, ${{matrix.ruby}}
|
15
15
|
|
16
16
|
runs-on: ${{matrix.os}}
|
17
17
|
steps:
|
18
|
-
-
|
19
|
-
|
18
|
+
- name: Checkout repository and submodules
|
19
|
+
uses: actions/checkout@v2
|
20
|
+
with:
|
21
|
+
submodules: recursive
|
22
|
+
- name: Setup Ruby
|
23
|
+
uses: ruby/setup-ruby@v1
|
20
24
|
with:
|
21
25
|
ruby-version: ${{matrix.ruby}}
|
22
26
|
bundler-cache: true # 'bundle install' and cache
|
@@ -29,4 +33,4 @@ jobs:
|
|
29
33
|
- name: Compile C-extension
|
30
34
|
run: bundle exec rake compile
|
31
35
|
- name: Run tests
|
32
|
-
run: bundle exec
|
36
|
+
run: bundle exec ruby test/run.rb --verbose --name test_sleep
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,19 @@
|
|
1
|
+
## 0.90 2022-03-21
|
2
|
+
|
3
|
+
- Fix possible compilation error on Ruby 2.7.5 (#79)
|
4
|
+
|
5
|
+
## 0.89 2022-03-21
|
6
|
+
|
7
|
+
- Implement compression/decompression to/from strings (#86)
|
8
|
+
|
9
|
+
## 0.88 2022-03-18
|
10
|
+
|
11
|
+
- Improve IO stream compression utilities (release GVL, cleanup on exception)
|
12
|
+
|
13
|
+
## 0.87 2022-03-17
|
14
|
+
|
15
|
+
- Fix compilation on non-Linux OSes
|
16
|
+
|
1
17
|
## 0.86 2022-03-14
|
2
18
|
|
3
19
|
- Fix gemspec
|
data/Gemfile.lock
CHANGED
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bundler/setup'
|
4
|
+
|
5
|
+
require 'polyphony'
|
6
|
+
require 'h1p'
|
7
|
+
|
8
|
+
server = Polyphony::Net.tcp_listen('localhost', 1234,
|
9
|
+
reuse_addr: true, reuse_port: true, dont_linger: true
|
10
|
+
)
|
11
|
+
puts 'Serving HTTP on port 1234'
|
12
|
+
|
13
|
+
def respond_default(conn)
|
14
|
+
conn << "HTTP/1.1 204\r\n\r\n"
|
15
|
+
end
|
16
|
+
|
17
|
+
def respond_splice(conn, path)
|
18
|
+
f = File.open(path, 'r') do |f|
|
19
|
+
conn << "HTTP/1.1 200\r\nTransfer-Encoding: chunked\r\n\r\n"
|
20
|
+
IO.http1_splice_chunked(f, conn, 16384)
|
21
|
+
end
|
22
|
+
rescue => e
|
23
|
+
p e
|
24
|
+
# conn << "HTTP/1.1 500\r\nContent-Length: 0\r\n\r\n"
|
25
|
+
end
|
26
|
+
|
27
|
+
def handle_client(conn)
|
28
|
+
parser = H1P::Parser.new(conn, :server)
|
29
|
+
while true
|
30
|
+
headers = parser.parse_headers
|
31
|
+
break unless headers
|
32
|
+
|
33
|
+
case headers[':path']
|
34
|
+
when /^\/splice\/(.+)$/
|
35
|
+
respond_splice(conn, $1)
|
36
|
+
else
|
37
|
+
respond_default(conn)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
rescue Errno::ECONNRESET
|
41
|
+
# ignore
|
42
|
+
end
|
43
|
+
|
44
|
+
server.accept_loop do |conn|
|
45
|
+
handle_client(conn)
|
46
|
+
end
|
@@ -363,8 +363,6 @@ inline void set_fd_blocking_mode(int fd, int blocking) {
|
|
363
363
|
}
|
364
364
|
|
365
365
|
inline void io_verify_blocking_mode(rb_io_t *fptr, VALUE io, VALUE blocking) {
|
366
|
-
int flags;
|
367
|
-
int is_nonblocking;
|
368
366
|
VALUE blocking_mode = rb_ivar_get(io, ID_ivar_blocking_mode);
|
369
367
|
if (blocking == blocking_mode) return;
|
370
368
|
|
@@ -481,7 +479,7 @@ struct io_buffer get_io_buffer(VALUE in) {
|
|
481
479
|
struct raw_buffer *raw = FIX2PTR(in);
|
482
480
|
return (struct io_buffer){ raw->ptr, raw->len, 1 };
|
483
481
|
}
|
484
|
-
return (struct io_buffer){ RSTRING_PTR(in), RSTRING_LEN(in), 0 };
|
482
|
+
return (struct io_buffer){ (unsigned char *)RSTRING_PTR(in), RSTRING_LEN(in), 0 };
|
485
483
|
}
|
486
484
|
|
487
485
|
VALUE coerce_io_string_or_buffer(VALUE buf) {
|
@@ -262,6 +262,21 @@ inline struct backend_stats backend_get_stats(VALUE self) {
|
|
262
262
|
return backend_base_stats(&backend->base);
|
263
263
|
}
|
264
264
|
|
265
|
+
static inline struct io_uring_sqe *io_uring_backend_get_sqe(Backend_t *backend) {
|
266
|
+
struct io_uring_sqe *sqe;
|
267
|
+
sqe = io_uring_get_sqe(&backend->ring);
|
268
|
+
if (sqe) goto done;
|
269
|
+
|
270
|
+
if (backend->pending_sqes)
|
271
|
+
io_uring_backend_immediate_submit(backend);
|
272
|
+
else {
|
273
|
+
VALUE resume_value = backend_snooze(&backend->base);
|
274
|
+
RAISE_IF_EXCEPTION(resume_value);
|
275
|
+
}
|
276
|
+
done:
|
277
|
+
return sqe;
|
278
|
+
}
|
279
|
+
|
265
280
|
VALUE Backend_wakeup(VALUE self) {
|
266
281
|
Backend_t *backend;
|
267
282
|
GetBackend(self, backend);
|
@@ -269,7 +284,7 @@ VALUE Backend_wakeup(VALUE self) {
|
|
269
284
|
if (backend->base.currently_polling) {
|
270
285
|
// Since we're currently blocking while waiting for a completion, we add a
|
271
286
|
// NOP which would cause the io_uring_enter syscall to return
|
272
|
-
struct io_uring_sqe *sqe =
|
287
|
+
struct io_uring_sqe *sqe = io_uring_backend_get_sqe(backend);
|
273
288
|
io_uring_prep_nop(sqe);
|
274
289
|
io_uring_backend_immediate_submit(backend);
|
275
290
|
|
@@ -302,7 +317,7 @@ int io_uring_backend_defer_submit_and_await(
|
|
302
317
|
|
303
318
|
// op was not completed (an exception was raised), so we need to cancel it
|
304
319
|
ctx->result = -ECANCELED;
|
305
|
-
sqe =
|
320
|
+
sqe = io_uring_backend_get_sqe(backend);
|
306
321
|
io_uring_prep_cancel(sqe, (__u64)ctx, 0);
|
307
322
|
io_uring_backend_immediate_submit(backend);
|
308
323
|
}
|
@@ -317,7 +332,7 @@ VALUE io_uring_backend_wait_fd(Backend_t *backend, int fd, int write) {
|
|
317
332
|
op_context_t *ctx = context_store_acquire(&backend->store, OP_POLL);
|
318
333
|
VALUE resumed_value = Qnil;
|
319
334
|
|
320
|
-
struct io_uring_sqe *sqe =
|
335
|
+
struct io_uring_sqe *sqe = io_uring_backend_get_sqe(backend);
|
321
336
|
io_uring_prep_poll_add(sqe, fd, write ? POLLOUT : POLLIN);
|
322
337
|
|
323
338
|
io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resumed_value);
|
@@ -368,7 +383,7 @@ VALUE Backend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof,
|
|
368
383
|
|
369
384
|
if (string_cap < expected_read_length + buf_pos) {
|
370
385
|
shrinkable_string = io_setstrbuf(&str, expected_read_length + buf_pos);
|
371
|
-
buffer.ptr = RSTRING_PTR(str) + buf_pos;
|
386
|
+
buffer.ptr = (unsigned char *)RSTRING_PTR(str) + buf_pos;
|
372
387
|
buffer.len = expected_read_length;
|
373
388
|
}
|
374
389
|
else {
|
@@ -385,7 +400,7 @@ VALUE Backend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof,
|
|
385
400
|
while (1) {
|
386
401
|
VALUE resume_value = Qnil;
|
387
402
|
op_context_t *ctx = context_store_acquire(&backend->store, OP_READ);
|
388
|
-
struct io_uring_sqe *sqe =
|
403
|
+
struct io_uring_sqe *sqe = io_uring_backend_get_sqe(backend);
|
389
404
|
int result;
|
390
405
|
int completed;
|
391
406
|
|
@@ -415,7 +430,7 @@ VALUE Backend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof,
|
|
415
430
|
rb_str_resize(str, total + buf_pos);
|
416
431
|
rb_str_modify_expand(str, rb_str_capacity(str));
|
417
432
|
shrinkable_string = 0;
|
418
|
-
buffer.ptr = RSTRING_PTR(str) + total + buf_pos;
|
433
|
+
buffer.ptr = (unsigned char *)RSTRING_PTR(str) + total + buf_pos;
|
419
434
|
buffer.len = rb_str_capacity(str) - total - buf_pos;
|
420
435
|
}
|
421
436
|
else {
|
@@ -453,7 +468,7 @@ VALUE Backend_read_loop(VALUE self, VALUE io, VALUE maxlen) {
|
|
453
468
|
while (1) {
|
454
469
|
VALUE resume_value = Qnil;
|
455
470
|
op_context_t *ctx = context_store_acquire(&backend->store, OP_READ);
|
456
|
-
struct io_uring_sqe *sqe =
|
471
|
+
struct io_uring_sqe *sqe = io_uring_backend_get_sqe(backend);
|
457
472
|
ssize_t result;
|
458
473
|
int completed;
|
459
474
|
|
@@ -502,7 +517,7 @@ VALUE Backend_feed_loop(VALUE self, VALUE io, VALUE receiver, VALUE method) {
|
|
502
517
|
while (1) {
|
503
518
|
VALUE resume_value = Qnil;
|
504
519
|
op_context_t *ctx = context_store_acquire(&backend->store, OP_READ);
|
505
|
-
struct io_uring_sqe *sqe =
|
520
|
+
struct io_uring_sqe *sqe = io_uring_backend_get_sqe(backend);
|
506
521
|
ssize_t result;
|
507
522
|
int completed;
|
508
523
|
|
@@ -546,7 +561,7 @@ VALUE Backend_write(VALUE self, VALUE io, VALUE str) {
|
|
546
561
|
while (left > 0) {
|
547
562
|
VALUE resume_value = Qnil;
|
548
563
|
op_context_t *ctx = context_store_acquire(&backend->store, OP_WRITE);
|
549
|
-
struct io_uring_sqe *sqe =
|
564
|
+
struct io_uring_sqe *sqe = io_uring_backend_get_sqe(backend);
|
550
565
|
int result;
|
551
566
|
int completed;
|
552
567
|
|
@@ -597,7 +612,7 @@ VALUE Backend_writev(VALUE self, VALUE io, int argc, VALUE *argv) {
|
|
597
612
|
while (1) {
|
598
613
|
VALUE resume_value = Qnil;
|
599
614
|
op_context_t *ctx = context_store_acquire(&backend->store, OP_WRITEV);
|
600
|
-
struct io_uring_sqe *sqe =
|
615
|
+
struct io_uring_sqe *sqe = io_uring_backend_get_sqe(backend);
|
601
616
|
int result;
|
602
617
|
int completed;
|
603
618
|
|
@@ -672,7 +687,7 @@ VALUE Backend_recv(VALUE self, VALUE io, VALUE str, VALUE length, VALUE pos) {
|
|
672
687
|
|
673
688
|
if (string_cap < expected_read_length + buf_pos) {
|
674
689
|
shrinkable_string = io_setstrbuf(&str, expected_read_length + buf_pos);
|
675
|
-
buffer.ptr = RSTRING_PTR(str) + buf_pos;
|
690
|
+
buffer.ptr = (unsigned char *)RSTRING_PTR(str) + buf_pos;
|
676
691
|
buffer.len = expected_read_length;
|
677
692
|
}
|
678
693
|
else {
|
@@ -689,7 +704,7 @@ VALUE Backend_recv(VALUE self, VALUE io, VALUE str, VALUE length, VALUE pos) {
|
|
689
704
|
while (1) {
|
690
705
|
VALUE resume_value = Qnil;
|
691
706
|
op_context_t *ctx = context_store_acquire(&backend->store, OP_RECV);
|
692
|
-
struct io_uring_sqe *sqe =
|
707
|
+
struct io_uring_sqe *sqe = io_uring_backend_get_sqe(backend);
|
693
708
|
int result;
|
694
709
|
int completed;
|
695
710
|
|
@@ -739,7 +754,7 @@ VALUE Backend_recv_loop(VALUE self, VALUE io, VALUE maxlen) {
|
|
739
754
|
while (1) {
|
740
755
|
VALUE resume_value = Qnil;
|
741
756
|
op_context_t *ctx = context_store_acquire(&backend->store, OP_RECV);
|
742
|
-
struct io_uring_sqe *sqe =
|
757
|
+
struct io_uring_sqe *sqe = io_uring_backend_get_sqe(backend);
|
743
758
|
int result;
|
744
759
|
int completed;
|
745
760
|
|
@@ -787,7 +802,7 @@ VALUE Backend_recv_feed_loop(VALUE self, VALUE io, VALUE receiver, VALUE method)
|
|
787
802
|
while (1) {
|
788
803
|
VALUE resume_value = Qnil;
|
789
804
|
op_context_t *ctx = context_store_acquire(&backend->store, OP_RECV);
|
790
|
-
struct io_uring_sqe *sqe =
|
805
|
+
struct io_uring_sqe *sqe = io_uring_backend_get_sqe(backend);
|
791
806
|
int result;
|
792
807
|
int completed;
|
793
808
|
|
@@ -831,7 +846,7 @@ VALUE Backend_send(VALUE self, VALUE io, VALUE str, VALUE flags) {
|
|
831
846
|
while (left > 0) {
|
832
847
|
VALUE resume_value = Qnil;
|
833
848
|
op_context_t *ctx = context_store_acquire(&backend->store, OP_SEND);
|
834
|
-
struct io_uring_sqe *sqe =
|
849
|
+
struct io_uring_sqe *sqe = io_uring_backend_get_sqe(backend);
|
835
850
|
int result;
|
836
851
|
int completed;
|
837
852
|
|
@@ -869,7 +884,7 @@ VALUE io_uring_backend_accept(Backend_t *backend, VALUE server_socket, VALUE soc
|
|
869
884
|
while (1) {
|
870
885
|
VALUE resume_value = Qnil;
|
871
886
|
op_context_t *ctx = context_store_acquire(&backend->store, OP_ACCEPT);
|
872
|
-
struct io_uring_sqe *sqe =
|
887
|
+
struct io_uring_sqe *sqe = io_uring_backend_get_sqe(backend);
|
873
888
|
int fd;
|
874
889
|
int completed;
|
875
890
|
|
@@ -935,7 +950,7 @@ VALUE io_uring_backend_splice(Backend_t *backend, VALUE src, VALUE dest, VALUE m
|
|
935
950
|
|
936
951
|
while (1) {
|
937
952
|
op_context_t *ctx = context_store_acquire(&backend->store, OP_SPLICE);
|
938
|
-
struct io_uring_sqe *sqe =
|
953
|
+
struct io_uring_sqe *sqe = io_uring_backend_get_sqe(backend);
|
939
954
|
int result;
|
940
955
|
int completed;
|
941
956
|
|
@@ -984,7 +999,7 @@ VALUE Backend_tee(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
|
|
984
999
|
|
985
1000
|
while (1) {
|
986
1001
|
op_context_t *ctx = context_store_acquire(&backend->store, OP_SPLICE);
|
987
|
-
struct io_uring_sqe *sqe =
|
1002
|
+
struct io_uring_sqe *sqe = io_uring_backend_get_sqe(backend);
|
988
1003
|
int result;
|
989
1004
|
int completed;
|
990
1005
|
|
@@ -1021,7 +1036,7 @@ VALUE Backend_connect(VALUE self, VALUE sock, VALUE host, VALUE port) {
|
|
1021
1036
|
GetBackend(self, backend);
|
1022
1037
|
fd = fd_from_io(sock, &fptr, 1, 0);
|
1023
1038
|
ctx = context_store_acquire(&backend->store, OP_CONNECT);
|
1024
|
-
sqe =
|
1039
|
+
sqe = io_uring_backend_get_sqe(backend);
|
1025
1040
|
io_uring_prep_connect(sqe, fd, ai_addr, ai_addrlen);
|
1026
1041
|
result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
|
1027
1042
|
completed = context_store_release(&backend->store, ctx);
|
@@ -1063,7 +1078,7 @@ VALUE Backend_wait_io(VALUE self, VALUE io, VALUE write) {
|
|
1063
1078
|
// io_unset_nonblock(fptr, io);
|
1064
1079
|
|
1065
1080
|
// ctx = context_store_acquire(&backend->store, OP_CLOSE);
|
1066
|
-
// sqe =
|
1081
|
+
// sqe = io_uring_backend_get_sqe(backend);
|
1067
1082
|
// io_uring_prep_close(sqe, fd);
|
1068
1083
|
// result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
|
1069
1084
|
// completed = context_store_release(&backend->store, ctx);
|
@@ -1094,7 +1109,7 @@ inline struct __kernel_timespec duration_to_timespec(VALUE duration) {
|
|
1094
1109
|
// returns true if completed, 0 otherwise
|
1095
1110
|
int io_uring_backend_submit_timeout_and_await(Backend_t *backend, double duration, VALUE *resume_value) {
|
1096
1111
|
struct __kernel_timespec ts = double_to_timespec(duration);
|
1097
|
-
struct io_uring_sqe *sqe =
|
1112
|
+
struct io_uring_sqe *sqe = io_uring_backend_get_sqe(backend);
|
1098
1113
|
op_context_t *ctx = context_store_acquire(&backend->store, OP_TIMEOUT);
|
1099
1114
|
|
1100
1115
|
io_uring_prep_timeout(sqe, &ts, 0, 0);
|
@@ -1183,7 +1198,7 @@ VALUE Backend_timeout(int argc, VALUE *argv, VALUE self) {
|
|
1183
1198
|
GetBackend(self, backend);
|
1184
1199
|
timeout = rb_funcall(cTimeoutException, ID_new, 0);
|
1185
1200
|
|
1186
|
-
sqe =
|
1201
|
+
sqe = io_uring_backend_get_sqe(backend);
|
1187
1202
|
ctx = context_store_acquire(&backend->store, OP_TIMEOUT);
|
1188
1203
|
ctx->resume_value = timeout;
|
1189
1204
|
io_uring_prep_timeout(sqe, &ts, 0, 0);
|
@@ -1259,7 +1274,7 @@ VALUE Backend_wait_event(VALUE self, VALUE raise) {
|
|
1259
1274
|
struct io_uring_sqe *sqe;
|
1260
1275
|
|
1261
1276
|
backend->event_fd_ctx = context_store_acquire(&backend->store, OP_POLL);
|
1262
|
-
sqe =
|
1277
|
+
sqe = io_uring_backend_get_sqe(backend);
|
1263
1278
|
io_uring_prep_poll_add(sqe, backend->event_fd, POLLIN);
|
1264
1279
|
backend->base.op_count++;
|
1265
1280
|
io_uring_sqe_set_data(sqe, backend->event_fd_ctx);
|
@@ -1275,7 +1290,7 @@ VALUE Backend_wait_event(VALUE self, VALUE raise) {
|
|
1275
1290
|
|
1276
1291
|
// last fiber to use the eventfd, so we cancel the ongoing poll
|
1277
1292
|
struct io_uring_sqe *sqe;
|
1278
|
-
sqe =
|
1293
|
+
sqe = io_uring_backend_get_sqe(backend);
|
1279
1294
|
io_uring_prep_cancel(sqe, (__u64)backend->event_fd_ctx, 0);
|
1280
1295
|
io_uring_backend_immediate_submit(backend);
|
1281
1296
|
backend->event_fd_ctx = NULL;
|
@@ -1296,7 +1311,7 @@ struct io_uring_sqe *Backend_chain_prepare_write(Backend_t *backend, VALUE io, V
|
|
1296
1311
|
struct io_uring_sqe *sqe;
|
1297
1312
|
|
1298
1313
|
fd = fd_from_io(io, &fptr, 1, 0);
|
1299
|
-
sqe =
|
1314
|
+
sqe = io_uring_backend_get_sqe(backend);
|
1300
1315
|
io_uring_prep_write(sqe, fd, StringValuePtr(str), RSTRING_LEN(str), 0);
|
1301
1316
|
return sqe;
|
1302
1317
|
}
|
@@ -1308,7 +1323,7 @@ struct io_uring_sqe *Backend_chain_prepare_send(Backend_t *backend, VALUE io, VA
|
|
1308
1323
|
|
1309
1324
|
fd = fd_from_io(io, &fptr, 1, 0);
|
1310
1325
|
|
1311
|
-
sqe =
|
1326
|
+
sqe = io_uring_backend_get_sqe(backend);
|
1312
1327
|
io_uring_prep_send(sqe, fd, StringValuePtr(str), RSTRING_LEN(str), NUM2INT(flags));
|
1313
1328
|
return sqe;
|
1314
1329
|
}
|
@@ -1322,7 +1337,7 @@ struct io_uring_sqe *Backend_chain_prepare_splice(Backend_t *backend, VALUE src,
|
|
1322
1337
|
|
1323
1338
|
src_fd = fd_from_io(src, &src_fptr, 0, 0);
|
1324
1339
|
dest_fd = fd_from_io(dest, &dest_fptr, 1, 0);
|
1325
|
-
sqe =
|
1340
|
+
sqe = io_uring_backend_get_sqe(backend);
|
1326
1341
|
io_uring_prep_splice(sqe, src_fd, -1, dest_fd, -1, NUM2INT(maxlen), 0);
|
1327
1342
|
return sqe;
|
1328
1343
|
}
|
@@ -1381,7 +1396,7 @@ VALUE Backend_chain(int argc,VALUE *argv, VALUE self) {
|
|
1381
1396
|
|
1382
1397
|
ctx->ref_count = sqe_count;
|
1383
1398
|
ctx->result = -ECANCELED;
|
1384
|
-
sqe =
|
1399
|
+
sqe = io_uring_backend_get_sqe(backend);
|
1385
1400
|
io_uring_prep_cancel(sqe, (__u64)ctx, 0);
|
1386
1401
|
io_uring_backend_immediate_submit(backend);
|
1387
1402
|
}
|
@@ -1411,7 +1426,7 @@ VALUE Backend_chain(int argc,VALUE *argv, VALUE self) {
|
|
1411
1426
|
|
1412
1427
|
// op was not completed (an exception was raised), so we need to cancel it
|
1413
1428
|
ctx->result = -ECANCELED;
|
1414
|
-
sqe =
|
1429
|
+
sqe = io_uring_backend_get_sqe(backend);
|
1415
1430
|
io_uring_prep_cancel(sqe, (__u64)ctx, 0);
|
1416
1431
|
io_uring_backend_immediate_submit(backend);
|
1417
1432
|
RAISE_IF_EXCEPTION(resume_value);
|
@@ -1470,14 +1485,14 @@ static inline void splice_chunks_get_sqe(
|
|
1470
1485
|
}
|
1471
1486
|
else
|
1472
1487
|
*ctx = context_store_acquire(&backend->store, type);
|
1473
|
-
(*sqe) =
|
1488
|
+
(*sqe) = io_uring_backend_get_sqe(backend);
|
1474
1489
|
}
|
1475
1490
|
|
1476
1491
|
static inline void splice_chunks_cancel(Backend_t *backend, op_context_t *ctx) {
|
1477
1492
|
struct io_uring_sqe *sqe;
|
1478
1493
|
|
1479
1494
|
ctx->result = -ECANCELED;
|
1480
|
-
sqe =
|
1495
|
+
sqe = io_uring_backend_get_sqe(backend);
|
1481
1496
|
io_uring_prep_cancel(sqe, (__u64)ctx, 0);
|
1482
1497
|
io_uring_backend_immediate_submit(backend);
|
1483
1498
|
}
|
@@ -311,7 +311,7 @@ VALUE Backend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof,
|
|
311
311
|
|
312
312
|
if (string_cap < expected_read_length + buf_pos) {
|
313
313
|
shrinkable_string = io_setstrbuf(&str, expected_read_length + buf_pos);
|
314
|
-
buffer.ptr = RSTRING_PTR(str) + buf_pos;
|
314
|
+
buffer.ptr = (unsigned char *)RSTRING_PTR(str) + buf_pos;
|
315
315
|
buffer.len = expected_read_length;
|
316
316
|
}
|
317
317
|
else {
|
@@ -353,7 +353,7 @@ VALUE Backend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof,
|
|
353
353
|
rb_str_resize(str, total + buf_pos);
|
354
354
|
rb_str_modify_expand(str, rb_str_capacity(str));
|
355
355
|
shrinkable_string = 0;
|
356
|
-
buffer.ptr = RSTRING_PTR(str) + total + buf_pos;
|
356
|
+
buffer.ptr = (unsigned char *)RSTRING_PTR(str) + total + buf_pos;
|
357
357
|
buffer.len = rb_str_capacity(str) - total - buf_pos;
|
358
358
|
}
|
359
359
|
else {
|
@@ -1645,6 +1645,9 @@ void Init_Backend() {
|
|
1645
1645
|
|
1646
1646
|
rb_define_method(cBackend, "splice", Backend_splice, 3);
|
1647
1647
|
rb_define_method(cBackend, "splice_to_eof", Backend_splice_to_eof, 3);
|
1648
|
+
#ifdef POLYPHONY_LINUX
|
1649
|
+
rb_define_method(cBackend, "tee", Backend_tee, 3);
|
1650
|
+
#endif
|
1648
1651
|
|
1649
1652
|
rb_define_method(cBackend, "timeout", Backend_timeout, -1);
|
1650
1653
|
rb_define_method(cBackend, "timer_loop", Backend_timer_loop, 1);
|
data/ext/polyphony/extconf.rb
CHANGED
@@ -47,6 +47,7 @@ end
|
|
47
47
|
$defs << '-DPOLYPHONY_USE_PIDFD_OPEN' if config[:pidfd_open]
|
48
48
|
if config[:io_uring]
|
49
49
|
$defs << "-DPOLYPHONY_BACKEND_LIBURING"
|
50
|
+
$defs << "-DPOLYPHONY_LINUX"
|
50
51
|
$defs << "-DPOLYPHONY_UNSET_NONBLOCK" if RUBY_VERSION =~ /^3/
|
51
52
|
$CFLAGS << " -Wno-pointer-arith"
|
52
53
|
else
|
@@ -70,7 +71,8 @@ $defs << '-DPOLYPHONY_PLAYGROUND' if ENV['POLYPHONY_PLAYGROUND']
|
|
70
71
|
|
71
72
|
CONFIG['optflags'] << ' -fno-strict-aliasing' unless RUBY_PLATFORM =~ /mswin/
|
72
73
|
|
73
|
-
|
74
|
-
|
74
|
+
if RUBY_VERSION >= '3.1'
|
75
|
+
have_func('rb_fiber_transfer', 'ruby.h')
|
76
|
+
end
|
75
77
|
|
76
78
|
create_makefile 'polyphony_ext'
|
@@ -10,6 +10,7 @@
|
|
10
10
|
#include "polyphony.h"
|
11
11
|
#include "zlib.h"
|
12
12
|
#include "assert.h"
|
13
|
+
#include "ruby/thread.h"
|
13
14
|
|
14
15
|
ID ID_at;
|
15
16
|
ID ID_read_method;
|
@@ -29,6 +30,7 @@ VALUE SYM_orig_name;
|
|
29
30
|
VALUE SYM_readpartial;
|
30
31
|
|
31
32
|
enum read_method {
|
33
|
+
RM_STRING,
|
32
34
|
RM_BACKEND_READ,
|
33
35
|
RM_BACKEND_RECV,
|
34
36
|
RM_READPARTIAL,
|
@@ -36,13 +38,15 @@ enum read_method {
|
|
36
38
|
};
|
37
39
|
|
38
40
|
enum write_method {
|
41
|
+
WM_STRING,
|
39
42
|
WM_BACKEND_WRITE,
|
40
43
|
WM_BACKEND_SEND,
|
41
44
|
WM_WRITE,
|
42
45
|
WM_CALL
|
43
46
|
};
|
44
47
|
|
45
|
-
static enum read_method detect_read_method(VALUE io) {
|
48
|
+
static inline enum read_method detect_read_method(VALUE io) {
|
49
|
+
if (TYPE(io) == T_STRING) return RM_STRING;
|
46
50
|
if (rb_respond_to(io, ID_read_method)) {
|
47
51
|
VALUE method = rb_funcall(io, ID_read_method, 0);
|
48
52
|
if (method == SYM_readpartial) return RM_READPARTIAL;
|
@@ -58,7 +62,8 @@ static enum read_method detect_read_method(VALUE io) {
|
|
58
62
|
rb_raise(rb_eRuntimeError, "Given io instance should be a callable or respond to #__read_method__");
|
59
63
|
}
|
60
64
|
|
61
|
-
static enum write_method detect_write_method(VALUE io) {
|
65
|
+
static inline enum write_method detect_write_method(VALUE io) {
|
66
|
+
if (TYPE(io) == T_STRING) return WM_STRING;
|
62
67
|
if (rb_respond_to(io, ID_write_method)) {
|
63
68
|
VALUE method = rb_funcall(io, ID_write_method, 0);
|
64
69
|
if (method == SYM_readpartial) return WM_WRITE;
|
@@ -136,11 +141,18 @@ static inline int read_to_raw_buffer(VALUE backend, VALUE io, enum read_method m
|
|
136
141
|
RB_GC_GUARD(str);
|
137
142
|
return len;
|
138
143
|
}
|
144
|
+
default: {
|
145
|
+
rb_raise(rb_eRuntimeError, "Invalid read method");
|
146
|
+
}
|
139
147
|
}
|
140
148
|
}
|
141
149
|
|
142
150
|
static inline int write_from_raw_buffer(VALUE backend, VALUE io, enum write_method method, struct raw_buffer *buffer) {
|
143
151
|
switch (method) {
|
152
|
+
case WM_STRING: {
|
153
|
+
rb_str_buf_cat(io, (char *)buffer->ptr, buffer->len);
|
154
|
+
return buffer->len;
|
155
|
+
}
|
144
156
|
case WM_BACKEND_WRITE: {
|
145
157
|
VALUE len = Backend_write(backend, io, PTR2FIX(buffer));
|
146
158
|
return FIX2INT(len);
|
@@ -165,6 +177,9 @@ static inline int write_from_raw_buffer(VALUE backend, VALUE io, enum write_meth
|
|
165
177
|
RB_GC_GUARD(str);
|
166
178
|
return buffer->len;
|
167
179
|
}
|
180
|
+
default: {
|
181
|
+
rb_raise(rb_eRuntimeError, "Invalid write method");
|
182
|
+
}
|
168
183
|
}
|
169
184
|
}
|
170
185
|
|
@@ -228,7 +243,7 @@ static inline time_t time_from_object(VALUE o) {
|
|
228
243
|
return FIX2INT(rb_funcall(o, rb_intern("to_i"), 0));
|
229
244
|
}
|
230
245
|
|
231
|
-
int gzip_prepare_header(struct gzip_header_ctx *ctx, char *buffer, int maxlen) {
|
246
|
+
int gzip_prepare_header(struct gzip_header_ctx *ctx, unsigned char *buffer, int maxlen) {
|
232
247
|
int len = 0;
|
233
248
|
unsigned char flags = 0, extraflags = 0;
|
234
249
|
|
@@ -259,7 +274,7 @@ int gzip_prepare_header(struct gzip_header_ctx *ctx, char *buffer, int maxlen) {
|
|
259
274
|
return len;
|
260
275
|
}
|
261
276
|
|
262
|
-
int gzip_prepare_footer(unsigned long crc32, unsigned long total_in, char *buffer, int maxlen) {
|
277
|
+
static inline int gzip_prepare_footer(unsigned long crc32, unsigned long total_in, unsigned char *buffer, int maxlen) {
|
263
278
|
assert(maxlen >= GZIP_FOOTER_LEN);
|
264
279
|
|
265
280
|
gzfile_set32(crc32, buffer);
|
@@ -287,8 +302,8 @@ struct z_stream_ctx {
|
|
287
302
|
|
288
303
|
unsigned char in[CHUNK];
|
289
304
|
unsigned char out[CHUNK];
|
290
|
-
int in_pos;
|
291
|
-
int out_pos;
|
305
|
+
unsigned int in_pos;
|
306
|
+
unsigned int out_pos;
|
292
307
|
unsigned long in_total;
|
293
308
|
unsigned long out_total;
|
294
309
|
|
@@ -297,8 +312,8 @@ struct z_stream_ctx {
|
|
297
312
|
|
298
313
|
typedef int (*zlib_func)(z_streamp, int);
|
299
314
|
|
300
|
-
void read_gzip_header_str(struct raw_buffer *buffer, VALUE *str, int *in_pos, unsigned long *total_read) {
|
301
|
-
|
315
|
+
void read_gzip_header_str(struct raw_buffer *buffer, VALUE *str, unsigned int *in_pos, unsigned long *total_read) {
|
316
|
+
unsigned long null_pos;
|
302
317
|
// find null terminator
|
303
318
|
for (null_pos = *in_pos; null_pos < *total_read; null_pos++) {
|
304
319
|
if (!buffer->ptr[null_pos]) break;
|
@@ -306,28 +321,37 @@ void read_gzip_header_str(struct raw_buffer *buffer, VALUE *str, int *in_pos, un
|
|
306
321
|
if (null_pos == *total_read)
|
307
322
|
rb_raise(rb_eRuntimeError, "Invalid gzip header");
|
308
323
|
|
309
|
-
*str = rb_str_new_cstr(buffer->ptr + *in_pos);
|
324
|
+
*str = rb_str_new_cstr((char *)buffer->ptr + *in_pos);
|
310
325
|
*in_pos = null_pos + 1;
|
311
326
|
}
|
312
327
|
|
313
328
|
void gzip_read_header(struct z_stream_ctx *ctx, struct gzip_header_ctx *header_ctx) {
|
314
|
-
struct raw_buffer in_buffer
|
329
|
+
struct raw_buffer in_buffer;
|
315
330
|
int flags;
|
316
331
|
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
ctx->in_total
|
332
|
+
if (ctx->src_read_method == RM_STRING) {
|
333
|
+
in_buffer.ptr = (unsigned char *)RSTRING_PTR(ctx->src);
|
334
|
+
in_buffer.len = RSTRING_LEN(ctx->src);
|
335
|
+
ctx->in_total = in_buffer.len;
|
321
336
|
}
|
337
|
+
else {
|
338
|
+
in_buffer.ptr = ctx->in;
|
339
|
+
in_buffer.len = CHUNK;
|
340
|
+
while (ctx->in_total < 10) {
|
341
|
+
int read = read_to_raw_buffer(ctx->backend, ctx->src, ctx->src_read_method, &in_buffer);
|
342
|
+
if (read == 0) goto error;
|
343
|
+
ctx->in_total += read;
|
344
|
+
}
|
345
|
+
}
|
346
|
+
|
322
347
|
// PRINT_BUFFER("read gzip header", ctx->in, ctx->in_total);
|
323
|
-
if (
|
324
|
-
if (
|
325
|
-
if (
|
326
|
-
flags =
|
348
|
+
if (in_buffer.ptr[0] != GZ_MAGIC1) goto error;
|
349
|
+
if (in_buffer.ptr[1] != GZ_MAGIC2) goto error;
|
350
|
+
if (in_buffer.ptr[2] != GZ_METHOD_DEFLATE) goto error;
|
351
|
+
flags = in_buffer.ptr[3];
|
327
352
|
|
328
|
-
unsigned long mtime = gzfile_get32(
|
353
|
+
unsigned long mtime = gzfile_get32(in_buffer.ptr + 4);
|
329
354
|
header_ctx->mtime = INT2FIX(mtime);
|
330
|
-
|
331
355
|
ctx->in_pos = 10;
|
332
356
|
|
333
357
|
if (flags & GZ_FLAG_ORIG_NAME)
|
@@ -344,6 +368,26 @@ error:
|
|
344
368
|
rb_raise(rb_eRuntimeError, "Invalid gzip header");
|
345
369
|
}
|
346
370
|
|
371
|
+
struct process_z_stream_ctx {
|
372
|
+
z_stream *strm;
|
373
|
+
int flags;
|
374
|
+
zlib_func fun;
|
375
|
+
int ret;
|
376
|
+
};
|
377
|
+
|
378
|
+
void *do_process_z_stream_without_gvl(void *ptr) {
|
379
|
+
struct process_z_stream_ctx *ctx = (struct process_z_stream_ctx *)ptr;
|
380
|
+
|
381
|
+
ctx->ret = (ctx->fun)(ctx->strm, ctx->flags);
|
382
|
+
return NULL;
|
383
|
+
}
|
384
|
+
|
385
|
+
static inline int process_without_gvl(zlib_func fun, z_stream *strm, int flags) {
|
386
|
+
struct process_z_stream_ctx ctx = { strm, flags, fun, 0 };
|
387
|
+
rb_thread_call_without_gvl2(do_process_z_stream_without_gvl, (void *)&ctx, RUBY_UBF_IO, 0);
|
388
|
+
return ctx.ret;
|
389
|
+
}
|
390
|
+
|
347
391
|
static inline int z_stream_write_out(struct z_stream_ctx *ctx, zlib_func fun, int eof) {
|
348
392
|
int ret;
|
349
393
|
int written;
|
@@ -351,7 +395,7 @@ static inline int z_stream_write_out(struct z_stream_ctx *ctx, zlib_func fun, in
|
|
351
395
|
|
352
396
|
int avail_out_pre = ctx->strm.avail_out = CHUNK - ctx->out_pos;
|
353
397
|
ctx->strm.next_out = ctx->out + ctx->out_pos;
|
354
|
-
ret = fun
|
398
|
+
ret = process_without_gvl(fun, &ctx->strm, eof ? Z_FINISH : Z_NO_FLUSH);
|
355
399
|
assert(ret != Z_STREAM_ERROR);
|
356
400
|
written = avail_out_pre - ctx->strm.avail_out;
|
357
401
|
out_buffer.ptr = ctx->out;
|
@@ -372,12 +416,13 @@ static inline int z_stream_write_out(struct z_stream_ctx *ctx, zlib_func fun, in
|
|
372
416
|
return ctx->strm.avail_out;
|
373
417
|
}
|
374
418
|
|
375
|
-
|
419
|
+
VALUE z_stream_io_loop(struct z_stream_ctx *ctx) {
|
376
420
|
zlib_func fun = (ctx->mode == SM_DEFLATE) ? deflate : inflate;
|
377
421
|
|
378
|
-
if (ctx->in_total > ctx->in_pos) {
|
422
|
+
if ((ctx->src_read_method != RM_STRING) && (ctx->in_total > ctx->in_pos)) {
|
379
423
|
// In bytes already read for parsing gzip header, so we need to process the
|
380
424
|
// rest.
|
425
|
+
|
381
426
|
ctx->strm.next_in = ctx->in + ctx->in_pos;
|
382
427
|
ctx->strm.avail_in = ctx->in_total -= ctx->in_pos;
|
383
428
|
|
@@ -389,14 +434,27 @@ void z_stream_io_loop(struct z_stream_ctx *ctx) {
|
|
389
434
|
}
|
390
435
|
|
391
436
|
while (1) {
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
ctx->
|
437
|
+
int eof;
|
438
|
+
int read_len;
|
439
|
+
if (ctx->src_read_method == RM_STRING) {
|
440
|
+
struct raw_buffer in_buffer = {
|
441
|
+
(unsigned char *)RSTRING_PTR(ctx->src) + ctx->in_pos,
|
442
|
+
RSTRING_LEN(ctx->src) - ctx->in_pos
|
443
|
+
};
|
444
|
+
ctx->strm.next_in = in_buffer.ptr;
|
445
|
+
read_len = ctx->strm.avail_in = in_buffer.len;
|
446
|
+
eof = 1;
|
447
|
+
if (ctx->mode == SM_DEFLATE) ctx->crc32 = crc32(ctx->crc32, in_buffer.ptr, read_len);
|
448
|
+
}
|
449
|
+
else {
|
450
|
+
struct raw_buffer in_buffer = {ctx->in, CHUNK};
|
451
|
+
ctx->strm.next_in = ctx->in;
|
452
|
+
read_len = ctx->strm.avail_in = read_to_raw_buffer(ctx->backend, ctx->src, ctx->src_read_method, &in_buffer);
|
453
|
+
if (!read_len) break;
|
454
|
+
eof = read_len < CHUNK;
|
455
|
+
if (ctx->mode == SM_DEFLATE) ctx->crc32 = crc32(ctx->crc32, ctx->in, read_len);
|
456
|
+
}
|
457
|
+
|
400
458
|
ctx->in_total += read_len;
|
401
459
|
|
402
460
|
// PRINT_BUFFER("read stream", ctx->in, read_len);
|
@@ -407,13 +465,15 @@ void z_stream_io_loop(struct z_stream_ctx *ctx) {
|
|
407
465
|
if (z_stream_write_out(ctx, fun, eof)) break;
|
408
466
|
}
|
409
467
|
|
410
|
-
if (eof)
|
468
|
+
if (eof) goto done;
|
411
469
|
}
|
412
470
|
|
413
471
|
//flush
|
414
472
|
ctx->strm.avail_in = 0;
|
415
473
|
ctx->strm.next_in = ctx->in;
|
416
474
|
z_stream_write_out(ctx, fun, 1);
|
475
|
+
done:
|
476
|
+
return Qnil;
|
417
477
|
}
|
418
478
|
|
419
479
|
static inline void setup_ctx(struct z_stream_ctx *ctx, enum stream_mode mode, VALUE src, VALUE dest) {
|
@@ -434,6 +494,18 @@ static inline void setup_ctx(struct z_stream_ctx *ctx, enum stream_mode mode, VA
|
|
434
494
|
ctx->crc32 = 0;
|
435
495
|
}
|
436
496
|
|
497
|
+
static inline VALUE z_stream_cleanup(struct z_stream_ctx *ctx) {
|
498
|
+
if (ctx->mode == SM_DEFLATE)
|
499
|
+
deflateEnd(&ctx->strm);
|
500
|
+
else
|
501
|
+
inflateEnd(&ctx->strm);
|
502
|
+
return Qnil;
|
503
|
+
}
|
504
|
+
|
505
|
+
#define SAFE(f) (VALUE (*)(VALUE))(f)
|
506
|
+
#define Z_STREAM_SAFE_IO_LOOP_WITH_CLEANUP(ctx) \
|
507
|
+
rb_ensure(SAFE(z_stream_io_loop), (VALUE)&ctx, SAFE(z_stream_cleanup), (VALUE)&ctx)
|
508
|
+
|
437
509
|
VALUE IO_gzip(int argc, VALUE *argv, VALUE self) {
|
438
510
|
VALUE src;
|
439
511
|
VALUE dest;
|
@@ -450,18 +522,16 @@ VALUE IO_gzip(int argc, VALUE *argv, VALUE self) {
|
|
450
522
|
};
|
451
523
|
|
452
524
|
struct z_stream_ctx ctx;
|
453
|
-
int level = DEFAULT_LEVEL;
|
454
525
|
int ret;
|
455
526
|
|
456
527
|
setup_ctx(&ctx, SM_DEFLATE, src, dest);
|
457
528
|
ctx.f_gzip_footer = 1; // write gzip footer
|
458
|
-
ctx.out_pos = gzip_prepare_header(&header_ctx, ctx.out, sizeof(ctx.out));
|
529
|
+
ctx.out_total = ctx.out_pos = gzip_prepare_header(&header_ctx, ctx.out, sizeof(ctx.out));
|
459
530
|
|
460
|
-
ret = deflateInit2(&ctx.strm,
|
461
|
-
if (ret != Z_OK)
|
462
|
-
|
463
|
-
|
464
|
-
|
531
|
+
ret = deflateInit2(&ctx.strm, DEFAULT_LEVEL, Z_DEFLATED, -MAX_WBITS, DEFAULT_MEM_LEVEL, Z_DEFAULT_STRATEGY);
|
532
|
+
if (ret != Z_OK)
|
533
|
+
rb_raise(rb_eRuntimeError, "zlib error: %s\n", ctx.strm.msg);
|
534
|
+
Z_STREAM_SAFE_IO_LOOP_WITH_CLEANUP(ctx);
|
465
535
|
return INT2FIX(ctx.out_total);
|
466
536
|
}
|
467
537
|
|
@@ -482,21 +552,25 @@ VALUE IO_gunzip(int argc, VALUE *argv, VALUE self) {
|
|
482
552
|
setup_ctx(&ctx, SM_INFLATE, src, dest);
|
483
553
|
gzip_read_header(&ctx, &header_ctx);
|
484
554
|
|
555
|
+
ret = inflateInit2(&ctx.strm, -MAX_WBITS);
|
556
|
+
if (ret != Z_OK)
|
557
|
+
rb_raise(rb_eRuntimeError, "zlib error: %s\n", ctx.strm.msg);
|
558
|
+
|
559
|
+
Z_STREAM_SAFE_IO_LOOP_WITH_CLEANUP(ctx);
|
560
|
+
|
561
|
+
// gzip_read_footer(&ctx, &footer_ctx);
|
562
|
+
// TODO: verify crc32
|
563
|
+
// TODO: verify total length
|
564
|
+
|
485
565
|
if (info != Qnil) {
|
486
566
|
rb_hash_aset(info, SYM_mtime, FIX2TIME(header_ctx.mtime));
|
487
567
|
rb_hash_aset(info, SYM_orig_name, header_ctx.orig_name);
|
488
568
|
rb_hash_aset(info, SYM_comment, header_ctx.comment);
|
489
569
|
}
|
570
|
+
RB_GC_GUARD(header_ctx.orig_name);
|
571
|
+
RB_GC_GUARD(header_ctx.comment);
|
490
572
|
|
491
|
-
|
492
|
-
if (ret != Z_OK) return INT2FIX(ret);
|
493
|
-
z_stream_io_loop(&ctx);
|
494
|
-
inflateEnd(&ctx.strm);
|
495
|
-
|
496
|
-
// gzip_read_footer(&ctx, &footer_ctx);
|
497
|
-
// TODO: verify crc32
|
498
|
-
// TODO: verify total length
|
499
|
-
return self;
|
573
|
+
return INT2FIX(ctx.out_total);
|
500
574
|
}
|
501
575
|
|
502
576
|
VALUE IO_deflate(VALUE self, VALUE src, VALUE dest) {
|
@@ -506,9 +580,10 @@ VALUE IO_deflate(VALUE self, VALUE src, VALUE dest) {
|
|
506
580
|
|
507
581
|
setup_ctx(&ctx, SM_DEFLATE, src, dest);
|
508
582
|
ret = deflateInit(&ctx.strm, level);
|
509
|
-
if (ret != Z_OK)
|
510
|
-
|
511
|
-
|
583
|
+
if (ret != Z_OK)
|
584
|
+
rb_raise(rb_eRuntimeError, "zlib error: %s\n", ctx.strm.msg);
|
585
|
+
|
586
|
+
Z_STREAM_SAFE_IO_LOOP_WITH_CLEANUP(ctx);
|
512
587
|
|
513
588
|
return INT2FIX(ctx.out_total);
|
514
589
|
}
|
@@ -519,19 +594,52 @@ VALUE IO_inflate(VALUE self, VALUE src, VALUE dest) {
|
|
519
594
|
|
520
595
|
setup_ctx(&ctx, SM_INFLATE, src, dest);
|
521
596
|
ret = inflateInit(&ctx.strm);
|
522
|
-
if (ret != Z_OK)
|
523
|
-
|
524
|
-
|
597
|
+
if (ret != Z_OK)
|
598
|
+
rb_raise(rb_eRuntimeError, "zlib error: %s\n", ctx.strm.msg);
|
599
|
+
|
600
|
+
Z_STREAM_SAFE_IO_LOOP_WITH_CLEANUP(ctx);
|
525
601
|
|
526
602
|
return INT2FIX(ctx.out_total);
|
527
603
|
}
|
528
604
|
|
605
|
+
VALUE IO_http1_splice_chunked(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
|
606
|
+
enum write_method method = detect_write_method(dest);
|
607
|
+
VALUE backend = BACKEND();
|
608
|
+
VALUE pipe = rb_funcall(cPipe, ID_new, 0);
|
609
|
+
unsigned char out[128];
|
610
|
+
struct raw_buffer buffer = { out, 0 };
|
611
|
+
|
612
|
+
while (1) {
|
613
|
+
int len = FIX2INT(Backend_splice(backend, src, pipe, maxlen));
|
614
|
+
if (!len) break;
|
615
|
+
|
616
|
+
// write chunk header
|
617
|
+
buffer.len += sprintf((char *)buffer.ptr + buffer.len, "%x\r\n", len);
|
618
|
+
write_from_raw_buffer(backend, dest, method, &buffer);
|
619
|
+
buffer.len = 0;
|
620
|
+
while (len) {
|
621
|
+
int spliced = FIX2INT(Backend_splice(backend, pipe, dest, INT2FIX(len)));
|
622
|
+
len -= spliced;
|
623
|
+
}
|
624
|
+
buffer.len += sprintf((char *)buffer.ptr + buffer.len, "\r\n");
|
625
|
+
}
|
626
|
+
buffer.len += sprintf((char *)buffer.ptr + buffer.len, "0\r\n\r\n");
|
627
|
+
write_from_raw_buffer(backend, dest, method, &buffer);
|
628
|
+
|
629
|
+
Pipe_close(pipe);
|
630
|
+
RB_GC_GUARD(pipe);
|
631
|
+
|
632
|
+
return self;
|
633
|
+
}
|
634
|
+
|
529
635
|
void Init_IOExtensions() {
|
530
636
|
rb_define_singleton_method(rb_cIO, "gzip", IO_gzip, -1);
|
531
637
|
rb_define_singleton_method(rb_cIO, "gunzip", IO_gunzip, -1);
|
532
638
|
rb_define_singleton_method(rb_cIO, "deflate", IO_deflate, 2);
|
533
639
|
rb_define_singleton_method(rb_cIO, "inflate", IO_inflate, 2);
|
534
640
|
|
641
|
+
rb_define_singleton_method(rb_cIO, "http1_splice_chunked", IO_http1_splice_chunked, 3);
|
642
|
+
|
535
643
|
ID_at = rb_intern("at");
|
536
644
|
ID_read_method = rb_intern("__read_method__");
|
537
645
|
ID_readpartial = rb_intern("readpartial");
|
data/ext/polyphony/polyphony.c
CHANGED
@@ -94,9 +94,11 @@ VALUE Polyphony_backend_splice_to_eof(VALUE self, VALUE src, VALUE dest, VALUE c
|
|
94
94
|
return Backend_splice_to_eof(BACKEND(), src, dest, chunksize);
|
95
95
|
}
|
96
96
|
|
97
|
+
#ifdef POLYPHONY_LINUX
|
97
98
|
VALUE Polyphony_backend_tee(VALUE self, VALUE src, VALUE dest, VALUE chunksize) {
|
98
99
|
return Backend_tee(BACKEND(), src, dest, chunksize);
|
99
100
|
}
|
101
|
+
#endif
|
100
102
|
|
101
103
|
VALUE Polyphony_backend_timeout(int argc,VALUE *argv, VALUE self) {
|
102
104
|
return Backend_timeout(argc, argv, BACKEND());
|
@@ -143,7 +145,7 @@ VALUE Polyphony_raw_buffer_get(int argc, VALUE *argv, VALUE self) {
|
|
143
145
|
int length = (len == Qnil) ? buffer->len : FIX2INT(len);
|
144
146
|
|
145
147
|
if (length > buffer->len) length = buffer->len;
|
146
|
-
return rb_utf8_str_new(buffer->ptr, length);
|
148
|
+
return rb_utf8_str_new((char *)buffer->ptr, length);
|
147
149
|
}
|
148
150
|
|
149
151
|
VALUE Polyphony_raw_buffer_set(VALUE self, VALUE buf, VALUE str) {
|
@@ -162,12 +164,6 @@ VALUE Polyphony_raw_buffer_size(VALUE self, VALUE buf) {
|
|
162
164
|
return INT2FIX(buffer->len);
|
163
165
|
}
|
164
166
|
|
165
|
-
VALUE Polyphony_backend_test(VALUE self, VALUE io, VALUE str) {
|
166
|
-
struct raw_buffer buffer = { RSTRING_PTR(str), RSTRING_LEN(str) };
|
167
|
-
VALUE args[2] = { io, PTR2FIX(&buffer) };
|
168
|
-
return Polyphony_backend_write(2, args, self);
|
169
|
-
}
|
170
|
-
|
171
167
|
// VALUE Polyphony_backend_close(VALUE self, VALUE io) {
|
172
168
|
// return Backend_close(BACKEND(), io);
|
173
169
|
// }
|
@@ -190,7 +186,11 @@ void Init_Polyphony() {
|
|
190
186
|
rb_define_singleton_method(mPolyphony, "backend_sleep", Polyphony_backend_sleep, 1);
|
191
187
|
rb_define_singleton_method(mPolyphony, "backend_splice", Polyphony_backend_splice, 3);
|
192
188
|
rb_define_singleton_method(mPolyphony, "backend_splice_to_eof", Polyphony_backend_splice_to_eof, 3);
|
189
|
+
|
190
|
+
#ifdef POLYPHONY_LINUX
|
193
191
|
rb_define_singleton_method(mPolyphony, "backend_tee", Polyphony_backend_tee, 3);
|
192
|
+
#endif
|
193
|
+
|
194
194
|
rb_define_singleton_method(mPolyphony, "backend_timeout", Polyphony_backend_timeout, -1);
|
195
195
|
rb_define_singleton_method(mPolyphony, "backend_timer_loop", Polyphony_backend_timer_loop, 1);
|
196
196
|
rb_define_singleton_method(mPolyphony, "backend_wait_event", Polyphony_backend_wait_event, 1);
|
@@ -204,7 +204,6 @@ void Init_Polyphony() {
|
|
204
204
|
rb_define_singleton_method(mPolyphony, "__raw_buffer_get__", Polyphony_raw_buffer_get, -1);
|
205
205
|
rb_define_singleton_method(mPolyphony, "__raw_buffer_set__", Polyphony_raw_buffer_set, 2);
|
206
206
|
rb_define_singleton_method(mPolyphony, "__raw_buffer_size__", Polyphony_raw_buffer_size, 1);
|
207
|
-
rb_define_singleton_method(mPolyphony, "backend_test", Polyphony_backend_test, 2);
|
208
207
|
|
209
208
|
rb_define_global_function("snooze", Polyphony_snooze, 0);
|
210
209
|
rb_define_global_function("suspend", Polyphony_suspend, 0);
|
data/ext/polyphony/polyphony.h
CHANGED
@@ -90,6 +90,7 @@ int Runqueue_should_poll_nonblocking(VALUE self);
|
|
90
90
|
|
91
91
|
void Pipe_verify_blocking_mode(VALUE self, VALUE blocking);
|
92
92
|
int Pipe_get_fd(VALUE self, int write_mode);
|
93
|
+
VALUE Pipe_close(VALUE self);
|
93
94
|
|
94
95
|
#ifdef POLYPHONY_BACKEND_LIBEV
|
95
96
|
#define Backend_recv_loop Backend_read_loop
|
@@ -112,7 +113,11 @@ VALUE Backend_sendv(VALUE self, VALUE io, VALUE ary, VALUE flags);
|
|
112
113
|
VALUE Backend_sleep(VALUE self, VALUE duration);
|
113
114
|
VALUE Backend_splice(VALUE self, VALUE src, VALUE dest, VALUE maxlen);
|
114
115
|
VALUE Backend_splice_to_eof(VALUE self, VALUE src, VALUE dest, VALUE chunksize);
|
116
|
+
|
117
|
+
#ifdef POLYPHONY_LINUX
|
115
118
|
VALUE Backend_tee(VALUE self, VALUE src, VALUE dest, VALUE maxlen);
|
119
|
+
#endif
|
120
|
+
|
116
121
|
VALUE Backend_timeout(int argc,VALUE *argv, VALUE self);
|
117
122
|
VALUE Backend_timer_loop(VALUE self, VALUE interval);
|
118
123
|
VALUE Backend_wait_event(VALUE self, VALUE raise);
|
data/lib/polyphony/version.rb
CHANGED
data/test/test_io.rb
CHANGED
@@ -656,19 +656,75 @@ class IOExtensionsTest < MiniTest::Test
|
|
656
656
|
i, o = IO.pipe
|
657
657
|
r, w = IO.pipe
|
658
658
|
|
659
|
-
|
660
|
-
|
659
|
+
ret = nil
|
660
|
+
f = spin {
|
661
|
+
ret = IO.deflate(i, w)
|
661
662
|
w.close
|
662
663
|
}
|
663
664
|
|
664
665
|
o << 'foobar' * 20
|
665
666
|
o.close
|
666
667
|
|
668
|
+
f.await
|
669
|
+
assert_equal 17, ret
|
670
|
+
|
667
671
|
data = r.read
|
668
672
|
msg = Zlib::Inflate.inflate(data)
|
669
673
|
assert_equal 'foobar' * 20, msg
|
670
674
|
end
|
671
675
|
|
676
|
+
def test_deflate_to_string
|
677
|
+
i, o = IO.pipe
|
678
|
+
r, w = IO.pipe
|
679
|
+
str = +''
|
680
|
+
|
681
|
+
ret = nil
|
682
|
+
f = spin {
|
683
|
+
ret = IO.deflate(i, str)
|
684
|
+
w << str
|
685
|
+
w.close
|
686
|
+
}
|
687
|
+
|
688
|
+
o << 'foobar' * 20
|
689
|
+
o.close
|
690
|
+
|
691
|
+
f.await
|
692
|
+
assert_equal 17, ret
|
693
|
+
|
694
|
+
data = r.read
|
695
|
+
msg = Zlib::Inflate.inflate(data)
|
696
|
+
assert_equal 'foobar' * 20, msg
|
697
|
+
end
|
698
|
+
|
699
|
+
def test_deflate_to_frozen_string
|
700
|
+
i, o = IO.pipe
|
701
|
+
str = '' # frozen
|
702
|
+
|
703
|
+
f = spin {
|
704
|
+
o << 'foobar' * 20
|
705
|
+
o.close
|
706
|
+
}
|
707
|
+
|
708
|
+
assert_raises(FrozenError) { IO.deflate(i, str) }
|
709
|
+
end
|
710
|
+
|
711
|
+
def test_deflate_from_string
|
712
|
+
r, w = IO.pipe
|
713
|
+
str = 'foobar' * 10000
|
714
|
+
ret = nil
|
715
|
+
|
716
|
+
f = spin {
|
717
|
+
ret = IO.deflate(str, w)
|
718
|
+
w.close
|
719
|
+
}
|
720
|
+
f.await
|
721
|
+
assert_equal 118, ret
|
722
|
+
|
723
|
+
data = r.read
|
724
|
+
msg = Zlib::Inflate.inflate(data)
|
725
|
+
assert_equal str, msg
|
726
|
+
end
|
727
|
+
|
672
728
|
def test_inflate
|
673
729
|
i, o = IO.pipe
|
674
730
|
r, w = IO.pipe
|
@@ -679,7 +735,35 @@ class IOExtensionsTest < MiniTest::Test
|
|
679
735
|
o.close
|
680
736
|
}
|
681
737
|
|
682
|
-
IO.inflate(i, w)
|
738
|
+
ret = IO.inflate(i, w)
|
739
|
+
assert_equal 6, ret
|
740
|
+
w.close
|
741
|
+
msg = r.read
|
742
|
+
assert_equal 'foobar', msg
|
743
|
+
end
|
744
|
+
|
745
|
+
def test_inflate_to_string
|
746
|
+
i, o = IO.pipe
|
747
|
+
str = +''
|
748
|
+
|
749
|
+
spin {
|
750
|
+
data = Zlib::Deflate.deflate('foobar', 9)
|
751
|
+
o << data
|
752
|
+
o.close
|
753
|
+
}
|
754
|
+
|
755
|
+
ret = IO.inflate(i, str)
|
756
|
+
assert_equal 6, ret
|
757
|
+
assert_equal 6, str.bytesize
|
758
|
+
assert_equal 'foobar', str
|
759
|
+
end
|
760
|
+
|
761
|
+
def test_inflate_from_string
|
762
|
+
r, w = IO.pipe
|
763
|
+
str = Zlib::Deflate.deflate('foobar', 9)
|
764
|
+
|
765
|
+
ret = IO.inflate(str, w)
|
766
|
+
assert_equal 6, ret
|
683
767
|
w.close
|
684
768
|
msg = r.read
|
685
769
|
assert_equal 'foobar', msg
|
@@ -690,7 +774,7 @@ class IOExtensionsTest < MiniTest::Test
|
|
690
774
|
dest = Polyphony.pipe
|
691
775
|
now = nil
|
692
776
|
|
693
|
-
spin {
|
777
|
+
f = spin {
|
694
778
|
now = Time.now
|
695
779
|
IO.gzip(src, dest)
|
696
780
|
dest.close
|
@@ -698,6 +782,32 @@ class IOExtensionsTest < MiniTest::Test
|
|
698
782
|
|
699
783
|
src << IO.read(__FILE__)
|
700
784
|
src.close
|
785
|
+
f.await
|
786
|
+
|
787
|
+
gz = Zlib::GzipReader.new(dest)
|
788
|
+
data = gz.read
|
789
|
+
assert_equal IO.read(__FILE__), data
|
790
|
+
assert_in_range (now-2)..(now+1), gz.mtime
|
791
|
+
assert_nil gz.orig_name
|
792
|
+
assert_nil gz.comment
|
793
|
+
end
|
794
|
+
|
795
|
+
def test_gzip_to_string
|
796
|
+
src = Polyphony.pipe
|
797
|
+
dest = Polyphony.pipe
|
798
|
+
str = +''
|
799
|
+
now = nil
|
800
|
+
|
801
|
+
f = spin {
|
802
|
+
now = Time.now
|
803
|
+
IO.gzip(src, str)
|
804
|
+
dest << str
|
805
|
+
dest.close
|
806
|
+
}
|
807
|
+
|
808
|
+
src << IO.read(__FILE__)
|
809
|
+
src.close
|
810
|
+
f.await
|
701
811
|
|
702
812
|
gz = Zlib::GzipReader.new(dest)
|
703
813
|
data = gz.read
|
@@ -707,6 +817,39 @@ class IOExtensionsTest < MiniTest::Test
|
|
707
817
|
assert_nil gz.comment
|
708
818
|
end
|
709
819
|
|
820
|
+
def test_gzip_from_string
|
821
|
+
str = IO.read(__FILE__)
|
822
|
+
dest = Polyphony.pipe
|
823
|
+
now = nil
|
824
|
+
|
825
|
+
IO.gzip(str, dest)
|
826
|
+
dest.close
|
827
|
+
|
828
|
+
gz = Zlib::GzipReader.new(dest)
|
829
|
+
data = gz.read
|
830
|
+
assert_equal IO.read(__FILE__), data
|
831
|
+
end
|
832
|
+
|
833
|
+
def test_gzip_return_value
|
834
|
+
src = Polyphony.pipe
|
835
|
+
dest = Polyphony.pipe
|
836
|
+
now = nil
|
837
|
+
ret = nil
|
838
|
+
|
839
|
+
f = spin {
|
840
|
+
now = Time.now
|
841
|
+
ret = IO.gzip(src, dest)
|
842
|
+
dest.close
|
843
|
+
}
|
844
|
+
|
845
|
+
src << IO.read(__FILE__)
|
846
|
+
src.close
|
847
|
+
f.await
|
848
|
+
|
849
|
+
gzipped = dest.read
|
850
|
+
assert_equal gzipped.bytesize, ret
|
851
|
+
end
|
852
|
+
|
710
853
|
def test_gzip_with_mtime_int
|
711
854
|
src = Polyphony.pipe
|
712
855
|
dest = Polyphony.pipe
|
@@ -801,19 +944,51 @@ class IOExtensionsTest < MiniTest::Test
|
|
801
944
|
def test_gunzip
|
802
945
|
src = Polyphony.pipe
|
803
946
|
dest = Polyphony.pipe
|
947
|
+
ret = nil
|
804
948
|
|
805
|
-
spin {
|
806
|
-
IO.gunzip(src, dest)
|
949
|
+
f = spin {
|
950
|
+
ret = IO.gunzip(src, dest)
|
807
951
|
dest.close
|
808
952
|
}
|
809
953
|
|
810
954
|
gz = Zlib::GzipWriter.new(src, 9)
|
811
|
-
gz <<
|
955
|
+
gz << IO.read(__FILE__)
|
812
956
|
gz.close
|
957
|
+
f.await
|
813
958
|
|
814
959
|
data = dest.read
|
815
|
-
|
816
|
-
assert_equal
|
960
|
+
assert_equal IO.read(__FILE__).bytesize, ret
|
961
|
+
assert_equal IO.read(__FILE__), data
|
962
|
+
end
|
963
|
+
|
964
|
+
def test_gunzip_to_string
|
965
|
+
src = Polyphony.pipe
|
966
|
+
str = +''
|
967
|
+
ret = nil
|
968
|
+
|
969
|
+
f = spin {
|
970
|
+
ret = IO.gunzip(src, str)
|
971
|
+
}
|
972
|
+
|
973
|
+
gz = Zlib::GzipWriter.new(src, 9)
|
974
|
+
gz << IO.read(__FILE__)
|
975
|
+
gz.close
|
976
|
+
f.await
|
977
|
+
|
978
|
+
assert_equal IO.read(__FILE__).bytesize, ret
|
979
|
+
assert_equal IO.read(__FILE__), str
|
980
|
+
end
|
981
|
+
|
982
|
+
def test_gunzip_from_string
|
983
|
+
src_data = 'foobar' * 1000
|
984
|
+
str = Zlib.gzip(src_data, level: 9)
|
985
|
+
dest = Polyphony.pipe
|
986
|
+
ret = IO.gunzip(str, dest)
|
987
|
+
dest.close
|
988
|
+
|
989
|
+
dest_data = dest.read
|
990
|
+
assert_equal src_data.bytesize, ret
|
991
|
+
assert_equal src_data, dest_data
|
817
992
|
end
|
818
993
|
|
819
994
|
def test_gunzip_multi
|
@@ -893,4 +1068,24 @@ class IOExtensionsTest < MiniTest::Test
|
|
893
1068
|
assert_equal 'foo.bar', dest_info[:orig_name]
|
894
1069
|
assert_equal 'hello!', dest_info[:comment]
|
895
1070
|
end
|
1071
|
+
|
1072
|
+
def test_deflate_inflate_strings
|
1073
|
+
src_data = IO.read(__FILE__)
|
1074
|
+
deflated = +''
|
1075
|
+
IO.deflate(src_data, deflated)
|
1076
|
+
inflated = +''
|
1077
|
+
IO.inflate(deflated, inflated)
|
1078
|
+
|
1079
|
+
assert_equal src_data, inflated
|
1080
|
+
end
|
1081
|
+
|
1082
|
+
def test_gzip_gunzip_strings
|
1083
|
+
src_data = IO.read(__FILE__)
|
1084
|
+
gzipped = +''
|
1085
|
+
IO.gzip(src_data, gzipped)
|
1086
|
+
gunzipped = +''
|
1087
|
+
IO.gunzip(gzipped, gunzipped)
|
1088
|
+
|
1089
|
+
assert_equal src_data, gunzipped
|
1090
|
+
end
|
896
1091
|
end
|
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.
|
4
|
+
version: '0.90'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sharon Rosner
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-03-
|
11
|
+
date: 2022-03-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake-compiler
|
@@ -262,6 +262,7 @@ files:
|
|
262
262
|
- examples/io/echo_stdin.rb
|
263
263
|
- examples/io/gzip.rb
|
264
264
|
- examples/io/happy-eyeballs.rb
|
265
|
+
- examples/io/http1_splice_chunked.rb
|
265
266
|
- examples/io/httparty.rb
|
266
267
|
- examples/io/https_server.rb
|
267
268
|
- examples/io/irb.rb
|
@@ -274,6 +275,7 @@ files:
|
|
274
275
|
- examples/io/reline.rb
|
275
276
|
- examples/io/splice_chunks.rb
|
276
277
|
- examples/io/splice_echo_server.rb
|
278
|
+
- examples/io/static_web_server.rb
|
277
279
|
- examples/io/stdio.rb
|
278
280
|
- examples/io/system.rb
|
279
281
|
- examples/io/tcp_proxy.rb
|