polyphony 0.90 → 0.91

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d9f125403a124d0374ebef23adfd4fe7f553767797003d63513ef780a248370b
4
- data.tar.gz: 682134740af1614ac5e31d49c7d6a8ac8b881bbc26e5911e2f12a81f5ea52b27
3
+ metadata.gz: d1b4a68e893c4cdca8e308cf3297fbf2549f70c0a7038f7c492f5c7c0a6ca6b4
4
+ data.tar.gz: 65a1cd1b10fa1a29c37c24972c0a47c324f744ff0ddba6e67464000da2a2707e
5
5
  SHA512:
6
- metadata.gz: d6292c9f2ddd6faf3d8df9565f8e6dce3be9ecd64fff95201bd2907cfacdb46eac5ef87a9281a3e88324471951493b6ebaa150dad40afb6a19958dd1e5992c0c
7
- data.tar.gz: 10f8e607a0bd403664318e03abd4bbb4ab16704a69d1c0858ce5c9269c5c883e081dd95c51488dce7732ae471bf04cd139c0cbfb9623d6751a857551304e60f5
6
+ metadata.gz: 316969779f3e2ce5a055c886ee89448b1908e57dcfb06775f5aa01491e895b450cfdaa3ec3d1f72f3a59017326dd7c3478b21e85cb7914a8bf2c17544bd47de7
7
+ data.tar.gz: 0a5badc5927cac1b111a13dd65eac242e2c2a7436dbf9a42e2376c4b321d042e27012e7d6b3906bf1d434d62a85bbfa8651700f2d4585475cbf89bc52de64569
data/CHANGELOG.md CHANGED
@@ -1,3 +1,9 @@
1
+ ## 0.91 2022-03-23
2
+
3
+ - Add pipe examples
4
+ - Implement `Backend#double_splice_to_eof` (io_ring only)
5
+ - Improve safety of tracing
6
+
1
7
  ## 0.90 2022-03-21
2
8
 
3
9
  - Fix possible compilation error on Ruby 2.7.5 (#79)
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- polyphony (0.90)
4
+ polyphony (0.91)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+ require 'polyphony'
5
+
6
+ def handle_client(conn)
7
+ spin do
8
+ buffer = Polyphony.pipe
9
+ spin { IO.splice_to_eof(conn, buffer) }
10
+ IO.splice_to_eof(buffer, conn)
11
+ end
12
+ rescue SystemCallError
13
+ # ignore
14
+ end
15
+
16
+ puts "Serving echo on port 1234..."
17
+ TCPServer.new('127.0.0.1', 1234).accept_loop { |c| handle_client(c) }
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+ require 'polyphony'
5
+
6
+ IO.gunzip(STDIN, STDOUT)
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+ require 'polyphony'
5
+
6
+ IO.gzip(STDIN, STDOUT)
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/inline'
4
+
5
+ gemfile do
6
+ gem 'h1p'
7
+ gem 'polyphony', path: '.'
8
+ end
9
+
10
+ require 'polyphony'
11
+ require 'h1p'
12
+
13
+ def handle_client(conn)
14
+ spin do
15
+ parser = H1P::Parser.new(conn, :server)
16
+
17
+ while true # assuming persistent connection
18
+ headers = parser.parse_headers
19
+ break unless headers
20
+
21
+ raw_buffer = Polyphony.pipe
22
+ gzip_buffer = Polyphony.pipe
23
+
24
+ # splice request body to buffer
25
+ spin do
26
+ parser.splice_body_to(raw_buffer)
27
+ raw_buffer.close
28
+ end
29
+
30
+ # zip data from buffer into gzip buffer
31
+ spin do
32
+ IO.gzip(raw_buffer, gzip_buffer)
33
+ gzip_buffer.close
34
+ end
35
+
36
+ # send headers and splice response from gzip buffer
37
+ conn << "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n"
38
+ IO.http1_splice_chunked(gzip_buffer, conn, 65535)
39
+ end
40
+ rescue H1P::Error
41
+ puts 'Got invalid request, closing connection...'
42
+ ensure
43
+ conn.close
44
+ end
45
+ end
46
+
47
+ puts "Serving echo on port 1234..."
48
+ TCPServer.new('127.0.0.1', 1234).accept_loop { |c| handle_client(c) }
49
+
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+ require 'polyphony'
5
+
6
+ DESTINATION = ['127.0.0.1', 1234]
7
+
8
+ def handle_client(conn)
9
+ spin do
10
+ dest = TCPSocket.new(*DESTINATION)
11
+ w_buffer = Polyphony.pipe
12
+ r_buffer = Polyphony.pipe
13
+
14
+ spin { IO.splice_to_eof(conn, w_buffer) }
15
+ spin { IO.splice_to_eof(w_buffer, dest) }
16
+
17
+ spin { IO.splice_to_eof(dest, r_buffer) }
18
+ spin { IO.splice_to_eof(r_buffer, conn) }
19
+
20
+ Fiber.current.await_all_children
21
+ end
22
+ rescue SystemCallError
23
+ dest.close rescue nil
24
+ # ignore
25
+ end
26
+
27
+ puts "Serving TCP proxy on port 4321..."
28
+ TCPServer.new('127.0.0.1', 4321).accept_loop { |c| handle_client(c) }
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+ require 'polyphony'
5
+
6
+ chunk_size = 1 << 16
7
+ file_path = ARGV[0]
8
+
9
+ File.open(file_path, 'w+') do |f|
10
+ loop do
11
+ len = IO.tee(STDIN, STDOUT, chunk_size)
12
+ break if len == 0
13
+ IO.splice(STDIN, f, len)
14
+ end
15
+ end
@@ -140,7 +140,9 @@ inline void backend_base_unpark_fiber(struct Backend_base *base, VALUE fiber) {
140
140
  inline void backend_trace(struct Backend_base *base, int argc, VALUE *argv) {
141
141
  if (base->trace_proc == Qnil || base->in_trace_proc) return;
142
142
 
143
+ base->in_trace_proc = 1;
143
144
  rb_funcallv(base->trace_proc, ID_call, argc, argv);
145
+ base->in_trace_proc = 0;
144
146
  }
145
147
 
146
148
  #ifdef POLYPHONY_USE_PIDFD_OPEN
@@ -416,13 +418,13 @@ VALUE Backend_stats(VALUE self) {
416
418
  struct backend_stats backend_stats = backend_get_stats(self);
417
419
 
418
420
  VALUE stats = rb_hash_new();
419
- rb_hash_aset(stats, SYM_runqueue_size, INT2NUM(backend_stats.runqueue_size));
420
- rb_hash_aset(stats, SYM_runqueue_length, INT2NUM(backend_stats.runqueue_length));
421
- rb_hash_aset(stats, SYM_runqueue_max_length, INT2NUM(backend_stats.runqueue_max_length));
422
- rb_hash_aset(stats, SYM_op_count, INT2NUM(backend_stats.op_count));
423
- rb_hash_aset(stats, SYM_switch_count, INT2NUM(backend_stats.switch_count));
424
- rb_hash_aset(stats, SYM_poll_count, INT2NUM(backend_stats.poll_count));
425
- rb_hash_aset(stats, SYM_pending_ops, INT2NUM(backend_stats.pending_ops));
421
+ rb_hash_aset(stats, SYM_runqueue_size, INT2FIX(backend_stats.runqueue_size));
422
+ rb_hash_aset(stats, SYM_runqueue_length, INT2FIX(backend_stats.runqueue_length));
423
+ rb_hash_aset(stats, SYM_runqueue_max_length, INT2FIX(backend_stats.runqueue_max_length));
424
+ rb_hash_aset(stats, SYM_op_count, INT2FIX(backend_stats.op_count));
425
+ rb_hash_aset(stats, SYM_switch_count, INT2FIX(backend_stats.switch_count));
426
+ rb_hash_aset(stats, SYM_poll_count, INT2FIX(backend_stats.poll_count));
427
+ rb_hash_aset(stats, SYM_pending_ops, INT2FIX(backend_stats.pending_ops));
426
428
  RB_GC_GUARD(stats);
427
429
  return stats;
428
430
  }
@@ -364,7 +364,7 @@ VALUE Backend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof,
364
364
  int fd;
365
365
  rb_io_t *fptr;
366
366
  struct io_buffer buffer = get_io_buffer(str);
367
- long buf_pos = NUM2INT(pos);
367
+ long buf_pos = FIX2INT(pos);
368
368
  int shrinkable_string = 0;
369
369
  int expandable_buffer = 0;
370
370
  long total = 0;
@@ -456,7 +456,7 @@ VALUE Backend_read_loop(VALUE self, VALUE io, VALUE maxlen) {
456
456
  rb_io_t *fptr;
457
457
  VALUE str;
458
458
  long total;
459
- long len = NUM2INT(maxlen);
459
+ long len = FIX2INT(maxlen);
460
460
  int shrinkable;
461
461
  char *buf;
462
462
 
@@ -584,7 +584,7 @@ VALUE Backend_write(VALUE self, VALUE io, VALUE str) {
584
584
  }
585
585
  }
586
586
 
587
- return INT2NUM(buffer.len);
587
+ return INT2FIX(buffer.len);
588
588
  }
589
589
 
590
590
  VALUE Backend_writev(VALUE self, VALUE io, int argc, VALUE *argv) {
@@ -652,7 +652,7 @@ VALUE Backend_writev(VALUE self, VALUE io, int argc, VALUE *argv) {
652
652
  }
653
653
 
654
654
  free(iov);
655
- return INT2NUM(total_written);
655
+ return INT2FIX(total_written);
656
656
  }
657
657
 
658
658
  VALUE Backend_write_m(int argc, VALUE *argv, VALUE self) {
@@ -669,7 +669,7 @@ VALUE Backend_recv(VALUE self, VALUE io, VALUE str, VALUE length, VALUE pos) {
669
669
  int fd;
670
670
  rb_io_t *fptr;
671
671
  struct io_buffer buffer = get_io_buffer(str);
672
- long buf_pos = NUM2INT(pos);
672
+ long buf_pos = FIX2INT(pos);
673
673
  int shrinkable_string = 0;
674
674
  int expandable_buffer = 0;
675
675
  long total = 0;
@@ -742,7 +742,7 @@ VALUE Backend_recv_loop(VALUE self, VALUE io, VALUE maxlen) {
742
742
  rb_io_t *fptr;
743
743
  VALUE str;
744
744
  long total;
745
- long len = NUM2INT(maxlen);
745
+ long len = FIX2INT(maxlen);
746
746
  int shrinkable;
747
747
  char *buf;
748
748
 
@@ -838,7 +838,7 @@ VALUE Backend_send(VALUE self, VALUE io, VALUE str, VALUE flags) {
838
838
 
839
839
  struct io_buffer buffer = get_io_buffer(str);
840
840
  long left = buffer.len;
841
- int flags_int = NUM2INT(flags);
841
+ int flags_int = FIX2INT(flags);
842
842
 
843
843
  GetBackend(self, backend);
844
844
  fd = fd_from_io(io, &fptr, 1, 0);
@@ -869,7 +869,7 @@ VALUE Backend_send(VALUE self, VALUE io, VALUE str, VALUE flags) {
869
869
  }
870
870
  }
871
871
 
872
- return INT2NUM(buffer.len);
872
+ return INT2FIX(buffer.len);
873
873
  }
874
874
 
875
875
  VALUE io_uring_backend_accept(Backend_t *backend, VALUE server_socket, VALUE socket_class, int loop) {
@@ -954,7 +954,7 @@ VALUE io_uring_backend_splice(Backend_t *backend, VALUE src, VALUE dest, VALUE m
954
954
  int result;
955
955
  int completed;
956
956
 
957
- io_uring_prep_splice(sqe, src_fd, -1, dest_fd, -1, NUM2INT(maxlen), 0);
957
+ io_uring_prep_splice(sqe, src_fd, -1, dest_fd, -1, FIX2INT(maxlen), 0);
958
958
 
959
959
  result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
960
960
  completed = context_store_release(&backend->store, ctx);
@@ -965,7 +965,7 @@ VALUE io_uring_backend_splice(Backend_t *backend, VALUE src, VALUE dest, VALUE m
965
965
  rb_syserr_fail(-result, strerror(-result));
966
966
 
967
967
  total += result;
968
- if (result == 0 || !loop) return INT2NUM(total);
968
+ if (result == 0 || !loop) return INT2FIX(total);
969
969
  }
970
970
 
971
971
  RB_GC_GUARD(resume_value);
@@ -984,6 +984,112 @@ VALUE Backend_splice_to_eof(VALUE self, VALUE src, VALUE dest, VALUE chunksize)
984
984
  return io_uring_backend_splice(backend, src, dest, chunksize, 1);
985
985
  }
986
986
 
987
+ struct double_splice_ctx {
988
+ Backend_t *backend;
989
+ VALUE src;
990
+ VALUE dest;
991
+ int pipefd[2];
992
+ };
993
+
994
+ #define DOUBLE_SPLICE_MAXLEN (1 << 16)
995
+
996
+ static inline op_context_t *prepare_double_splice_ctx(Backend_t *backend, int src_fd, int dest_fd) {
997
+ op_context_t *ctx = context_store_acquire(&backend->store, OP_SPLICE);
998
+ struct io_uring_sqe *sqe = io_uring_backend_get_sqe(backend);
999
+ io_uring_prep_splice(sqe, src_fd, -1, dest_fd, -1, DOUBLE_SPLICE_MAXLEN, 0);
1000
+ io_uring_sqe_set_data(sqe, ctx);
1001
+ io_uring_sqe_set_flags(sqe, IOSQE_ASYNC);
1002
+ backend->base.op_count += 1;
1003
+ backend->pending_sqes += 1;
1004
+
1005
+ return ctx;
1006
+ }
1007
+
1008
+ static inline void io_uring_backend_cancel(Backend_t *backend, op_context_t *ctx) {
1009
+ struct io_uring_sqe *sqe = io_uring_backend_get_sqe(backend);
1010
+ ctx->result = -ECANCELED;
1011
+ io_uring_prep_cancel(sqe, (__u64)ctx, 0);
1012
+ }
1013
+
1014
+ VALUE double_splice_to_eof_safe(struct double_splice_ctx *ctx) {
1015
+ int src_fd;
1016
+ int dest_fd;
1017
+ rb_io_t *src_fptr;
1018
+ rb_io_t *dest_fptr;
1019
+ int total = 0;
1020
+ VALUE resume_value = Qnil;
1021
+
1022
+ src_fd = fd_from_io(ctx->src, &src_fptr, 0, 0);
1023
+ dest_fd = fd_from_io(ctx->dest, &dest_fptr, 1, 0);
1024
+
1025
+ op_context_t *ctx_src = prepare_double_splice_ctx(ctx->backend, src_fd, ctx->pipefd[1]);
1026
+ op_context_t *ctx_dest = prepare_double_splice_ctx(ctx->backend, ctx->pipefd[0], dest_fd);
1027
+
1028
+ if (ctx->backend->pending_sqes >= ctx->backend->prepared_limit)
1029
+ io_uring_backend_immediate_submit(ctx->backend);
1030
+
1031
+ while (1) {
1032
+ resume_value = backend_await((struct Backend_base *)ctx->backend);
1033
+
1034
+ if ((ctx_src && ctx_src->ref_count == 2 && ctx_dest && ctx_dest->ref_count == 2) || TEST_EXCEPTION(resume_value)) {
1035
+ if (ctx_src) {
1036
+ context_store_release(&ctx->backend->store, ctx_src);
1037
+ io_uring_backend_cancel(ctx->backend, ctx_src);
1038
+ }
1039
+ if (ctx_dest) {
1040
+ context_store_release(&ctx->backend->store, ctx_dest);
1041
+ io_uring_backend_cancel(ctx->backend, ctx_dest);
1042
+ }
1043
+ io_uring_backend_immediate_submit(ctx->backend);
1044
+ RAISE_IF_EXCEPTION(resume_value);
1045
+ return resume_value;
1046
+ }
1047
+
1048
+ if (ctx_src && ctx_src->ref_count == 1) {
1049
+ context_store_release(&ctx->backend->store, ctx_src);
1050
+ if (ctx_src->result == 0) {
1051
+ // close write end of pipe
1052
+ close(ctx->pipefd[1]);
1053
+ ctx_src = NULL;
1054
+ }
1055
+ else {
1056
+ ctx_src = prepare_double_splice_ctx(ctx->backend, src_fd, ctx->pipefd[1]);
1057
+ }
1058
+ }
1059
+ if (ctx_dest && ctx_dest->ref_count == 1) {
1060
+ context_store_release(&ctx->backend->store, ctx_dest);
1061
+ if (ctx_dest->result == 0)
1062
+ break;
1063
+ else {
1064
+ total += ctx_dest->result;
1065
+ ctx_dest = prepare_double_splice_ctx(ctx->backend, ctx->pipefd[0], dest_fd);
1066
+ }
1067
+ }
1068
+
1069
+ if (ctx->backend->pending_sqes >= ctx->backend->prepared_limit)
1070
+ io_uring_backend_immediate_submit(ctx->backend);
1071
+ }
1072
+ RB_GC_GUARD(resume_value);
1073
+ return INT2FIX(total);
1074
+ }
1075
+
1076
+ VALUE double_splice_to_eof_cleanup(struct double_splice_ctx *ctx) {
1077
+ if (ctx->pipefd[0]) close(ctx->pipefd[0]);
1078
+ if (ctx->pipefd[1]) close(ctx->pipefd[1]);
1079
+ return Qnil;
1080
+ }
1081
+
1082
+ VALUE Backend_double_splice_to_eof(VALUE self, VALUE src, VALUE dest) {
1083
+ struct double_splice_ctx ctx = { NULL, src, dest, 0, 0 };
1084
+ GetBackend(self, ctx.backend);
1085
+ if (pipe(ctx.pipefd) == -1) rb_syserr_fail(errno, strerror(errno));
1086
+
1087
+ return rb_ensure(
1088
+ SAFE(double_splice_to_eof_safe), (VALUE)&ctx,
1089
+ SAFE(double_splice_to_eof_cleanup), (VALUE)&ctx
1090
+ );
1091
+ }
1092
+
987
1093
  VALUE Backend_tee(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
988
1094
  Backend_t *backend;
989
1095
  GetBackend(self, backend);
@@ -1003,7 +1109,7 @@ VALUE Backend_tee(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
1003
1109
  int result;
1004
1110
  int completed;
1005
1111
 
1006
- io_uring_prep_tee(sqe, src_fd, dest_fd, NUM2INT(maxlen), 0);
1112
+ io_uring_prep_tee(sqe, src_fd, dest_fd, FIX2INT(maxlen), 0);
1007
1113
 
1008
1114
  result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
1009
1115
  completed = context_store_release(&backend->store, ctx);
@@ -1013,7 +1119,7 @@ VALUE Backend_tee(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
1013
1119
  if (result < 0)
1014
1120
  rb_syserr_fail(-result, strerror(-result));
1015
1121
 
1016
- return INT2NUM(result);
1122
+ return INT2FIX(result);
1017
1123
  }
1018
1124
 
1019
1125
  RB_GC_GUARD(resume_value);
@@ -1222,7 +1328,7 @@ VALUE Backend_timeout(int argc, VALUE *argv, VALUE self) {
1222
1328
  }
1223
1329
 
1224
1330
  VALUE Backend_waitpid(VALUE self, VALUE pid) {
1225
- int pid_int = NUM2INT(pid);
1331
+ int pid_int = FIX2INT(pid);
1226
1332
  int fd = pidfd_open(pid_int, 0);
1227
1333
  int status;
1228
1334
  pid_t ret;
@@ -1246,7 +1352,7 @@ VALUE Backend_waitpid(VALUE self, VALUE pid) {
1246
1352
  else
1247
1353
  rb_syserr_fail(e, strerror(e));
1248
1354
  }
1249
- return rb_ary_new_from_args(2, INT2NUM(ret), INT2NUM(WEXITSTATUS(status)));
1355
+ return rb_ary_new_from_args(2, INT2FIX(ret), INT2FIX(WEXITSTATUS(status)));
1250
1356
  }
1251
1357
 
1252
1358
  /*
@@ -1324,7 +1430,7 @@ struct io_uring_sqe *Backend_chain_prepare_send(Backend_t *backend, VALUE io, VA
1324
1430
  fd = fd_from_io(io, &fptr, 1, 0);
1325
1431
 
1326
1432
  sqe = io_uring_backend_get_sqe(backend);
1327
- io_uring_prep_send(sqe, fd, StringValuePtr(str), RSTRING_LEN(str), NUM2INT(flags));
1433
+ io_uring_prep_send(sqe, fd, StringValuePtr(str), RSTRING_LEN(str), FIX2INT(flags));
1328
1434
  return sqe;
1329
1435
  }
1330
1436
 
@@ -1338,7 +1444,7 @@ struct io_uring_sqe *Backend_chain_prepare_splice(Backend_t *backend, VALUE src,
1338
1444
  src_fd = fd_from_io(src, &src_fptr, 0, 0);
1339
1445
  dest_fd = fd_from_io(dest, &dest_fptr, 1, 0);
1340
1446
  sqe = io_uring_backend_get_sqe(backend);
1341
- io_uring_prep_splice(sqe, src_fd, -1, dest_fd, -1, NUM2INT(maxlen), 0);
1447
+ io_uring_prep_splice(sqe, src_fd, -1, dest_fd, -1, FIX2INT(maxlen), 0);
1342
1448
  return sqe;
1343
1449
  }
1344
1450
 
@@ -1434,7 +1540,7 @@ VALUE Backend_chain(int argc,VALUE *argv, VALUE self) {
1434
1540
  }
1435
1541
 
1436
1542
  RB_GC_GUARD(resume_value);
1437
- return INT2NUM(result);
1543
+ return INT2FIX(result);
1438
1544
  }
1439
1545
 
1440
1546
  VALUE Backend_idle_gc_period_set(VALUE self, VALUE period) {
@@ -1540,7 +1646,7 @@ VALUE Backend_splice_chunks(VALUE self, VALUE src, VALUE dest, VALUE prefix, VAL
1540
1646
  src_fd = fd_from_io(src, &src_fptr, 0, 0);
1541
1647
  dest_fd = fd_from_io(dest, &dest_fptr, 1, 0);
1542
1648
 
1543
- maxlen = NUM2INT(chunk_size);
1649
+ maxlen = FIX2INT(chunk_size);
1544
1650
 
1545
1651
  if (pipe(pipefd) == -1) {
1546
1652
  err = errno;
@@ -1566,7 +1672,7 @@ VALUE Backend_splice_chunks(VALUE self, VALUE src, VALUE dest, VALUE prefix, VAL
1566
1672
  if (chunk_len == 0) break;
1567
1673
 
1568
1674
  total += chunk_len;
1569
- chunk_len_value = INT2NUM(chunk_len);
1675
+ chunk_len_value = INT2FIX(chunk_len);
1570
1676
 
1571
1677
 
1572
1678
  if (chunk_prefix != Qnil) {
@@ -1605,7 +1711,7 @@ VALUE Backend_splice_chunks(VALUE self, VALUE src, VALUE dest, VALUE prefix, VAL
1605
1711
  RB_GC_GUARD(switchpoint_result);
1606
1712
  if (pipefd[0] != -1) close(pipefd[0]);
1607
1713
  if (pipefd[1] != -1) close(pipefd[1]);
1608
- return INT2NUM(total);
1714
+ return INT2FIX(total);
1609
1715
  syscallerror:
1610
1716
  if (pipefd[0] != -1) close(pipefd[0]);
1611
1717
  if (pipefd[1] != -1) close(pipefd[1]);
@@ -1693,9 +1799,12 @@ void Init_Backend() {
1693
1799
  rb_define_method(cBackend, "send", Backend_send, 3);
1694
1800
  rb_define_method(cBackend, "sendv", Backend_sendv, 3);
1695
1801
  rb_define_method(cBackend, "sleep", Backend_sleep, 1);
1802
+
1696
1803
  rb_define_method(cBackend, "splice", Backend_splice, 3);
1697
1804
  rb_define_method(cBackend, "splice_to_eof", Backend_splice_to_eof, 3);
1805
+ rb_define_method(cBackend, "double_splice_to_eof", Backend_double_splice_to_eof, 2);
1698
1806
  rb_define_method(cBackend, "tee", Backend_tee, 3);
1807
+
1699
1808
  rb_define_method(cBackend, "timeout", Backend_timeout, -1);
1700
1809
  rb_define_method(cBackend, "timer_loop", Backend_timer_loop, 1);
1701
1810
  rb_define_method(cBackend, "wait_event", Backend_wait_event, 1);
@@ -291,7 +291,7 @@ VALUE Backend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof,
291
291
  rb_io_t *fptr;
292
292
 
293
293
  struct io_buffer buffer = get_io_buffer(str);
294
- long buf_pos = NUM2INT(pos);
294
+ long buf_pos = FIX2INT(pos);
295
295
  int shrinkable_string = 0;
296
296
  int expandable_buffer = 0;
297
297
  long total = 0;
@@ -389,7 +389,7 @@ VALUE Backend_read_loop(VALUE self, VALUE io, VALUE maxlen) {
389
389
  rb_io_t *fptr;
390
390
  VALUE str;
391
391
  long total;
392
- long len = NUM2INT(maxlen);
392
+ long len = FIX2INT(maxlen);
393
393
  int shrinkable;
394
394
  char *buf;
395
395
  VALUE switchpoint_result = Qnil;
@@ -519,7 +519,7 @@ VALUE Backend_write(VALUE self, VALUE io, VALUE str) {
519
519
  RB_GC_GUARD(watcher.fiber);
520
520
  RB_GC_GUARD(switchpoint_result);
521
521
 
522
- return INT2NUM(buffer.len);
522
+ return INT2FIX(buffer.len);
523
523
  error:
524
524
  return RAISE_EXCEPTION(switchpoint_result);
525
525
  }
@@ -590,7 +590,7 @@ VALUE Backend_writev(VALUE self, VALUE io, int argc, VALUE *argv) {
590
590
  RB_GC_GUARD(switchpoint_result);
591
591
 
592
592
  free(iov);
593
- return INT2NUM(total_written);
593
+ return INT2FIX(total_written);
594
594
  error:
595
595
  free(iov);
596
596
  return RAISE_EXCEPTION(switchpoint_result);
@@ -763,7 +763,7 @@ VALUE Backend_send(VALUE self, VALUE io, VALUE str, VALUE flags) {
763
763
 
764
764
  struct io_buffer buffer = get_io_buffer(str);
765
765
  long left = buffer.len;
766
- int flags_int = NUM2INT(flags);
766
+ int flags_int = FIX2INT(flags);
767
767
 
768
768
  GetBackend(self, backend);
769
769
  fd = fd_from_io(io, &fptr, 1, 0);
@@ -795,7 +795,7 @@ VALUE Backend_send(VALUE self, VALUE io, VALUE str, VALUE flags) {
795
795
  RB_GC_GUARD(watcher.fiber);
796
796
  RB_GC_GUARD(switchpoint_result);
797
797
 
798
- return INT2NUM(buffer.len);
798
+ return INT2FIX(buffer.len);
799
799
  error:
800
800
  return RAISE_EXCEPTION(switchpoint_result);
801
801
  }
@@ -868,7 +868,7 @@ VALUE Backend_splice(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
868
868
 
869
869
  while (1) {
870
870
  backend->base.op_count++;
871
- len = splice(src_fd, 0, dest_fd, 0, NUM2INT(maxlen), 0);
871
+ len = splice(src_fd, 0, dest_fd, 0, FIX2INT(maxlen), 0);
872
872
  if (len < 0) {
873
873
  int e = errno;
874
874
  if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
@@ -889,7 +889,7 @@ VALUE Backend_splice(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
889
889
  RB_GC_GUARD(watcher.ctx.fiber);
890
890
  RB_GC_GUARD(switchpoint_result);
891
891
 
892
- return INT2NUM(len);
892
+ return INT2FIX(len);
893
893
  error:
894
894
  return RAISE_EXCEPTION(switchpoint_result);
895
895
  }
@@ -912,7 +912,7 @@ VALUE Backend_splice_to_eof(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
912
912
 
913
913
  while (1) {
914
914
  backend->base.op_count++;
915
- len = splice(src_fd, 0, dest_fd, 0, NUM2INT(maxlen), 0);
915
+ len = splice(src_fd, 0, dest_fd, 0, FIX2INT(maxlen), 0);
916
916
  if (len < 0) {
917
917
  int e = errno;
918
918
  if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
@@ -936,7 +936,7 @@ VALUE Backend_splice_to_eof(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
936
936
  RB_GC_GUARD(watcher.ctx.fiber);
937
937
  RB_GC_GUARD(switchpoint_result);
938
938
 
939
- return INT2NUM(total);
939
+ return INT2FIX(total);
940
940
  error:
941
941
  return RAISE_EXCEPTION(switchpoint_result);
942
942
  }
@@ -958,7 +958,7 @@ VALUE Backend_tee(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
958
958
 
959
959
  while (1) {
960
960
  backend->base.op_count++;
961
- len = tee(src_fd, dest_fd, NUM2INT(maxlen), 0);
961
+ len = tee(src_fd, dest_fd, FIX2INT(maxlen), 0);
962
962
  if (len < 0) {
963
963
  int e = errno;
964
964
  if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
@@ -979,7 +979,7 @@ VALUE Backend_tee(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
979
979
  RB_GC_GUARD(watcher.ctx.fiber);
980
980
  RB_GC_GUARD(switchpoint_result);
981
981
 
982
- return INT2NUM(len);
982
+ return INT2FIX(len);
983
983
  error:
984
984
  return RAISE_EXCEPTION(switchpoint_result);
985
985
  }
@@ -994,7 +994,7 @@ VALUE Backend_splice(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
994
994
  int dest_fd;
995
995
  rb_io_t *src_fptr;
996
996
  rb_io_t *dest_fptr;
997
- int len = NUM2INT(maxlen);
997
+ int len = FIX2INT(maxlen);
998
998
  VALUE str = rb_str_new(0, len);
999
999
  char *buf = RSTRING_PTR(str);
1000
1000
  int left = 0;
@@ -1047,7 +1047,7 @@ VALUE Backend_splice(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
1047
1047
  RB_GC_GUARD(switchpoint_result);
1048
1048
  RB_GC_GUARD(str);
1049
1049
 
1050
- return INT2NUM(total);
1050
+ return INT2FIX(total);
1051
1051
  error:
1052
1052
  return RAISE_EXCEPTION(switchpoint_result);
1053
1053
  }
@@ -1060,7 +1060,7 @@ VALUE Backend_splice_to_eof(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
1060
1060
  int dest_fd;
1061
1061
  rb_io_t *src_fptr;
1062
1062
  rb_io_t *dest_fptr;
1063
- int len = NUM2INT(maxlen);
1063
+ int len = FIX2INT(maxlen);
1064
1064
  VALUE str = rb_str_new(0, len);
1065
1065
  char *buf = RSTRING_PTR(str);
1066
1066
  int left = 0;
@@ -1118,7 +1118,7 @@ done:
1118
1118
  RB_GC_GUARD(switchpoint_result);
1119
1119
  RB_GC_GUARD(str);
1120
1120
 
1121
- return INT2NUM(total);
1121
+ return INT2FIX(total);
1122
1122
  error:
1123
1123
  return RAISE_EXCEPTION(switchpoint_result);
1124
1124
  }
@@ -1263,7 +1263,7 @@ VALUE Backend_timeout(int argc,VALUE *argv, VALUE self) {
1263
1263
 
1264
1264
  #ifdef POLYPHONY_USE_PIDFD_OPEN
1265
1265
  VALUE Backend_waitpid(VALUE self, VALUE pid) {
1266
- int pid_int = NUM2INT(pid);
1266
+ int pid_int = FIX2INT(pid);
1267
1267
  int fd = pidfd_open(pid_int, 0);
1268
1268
  if (fd >= 0) {
1269
1269
  Backend_t *backend;
@@ -1286,7 +1286,7 @@ VALUE Backend_waitpid(VALUE self, VALUE pid) {
1286
1286
  int e = errno;
1287
1287
  rb_syserr_fail(e, strerror(e));
1288
1288
  }
1289
- return rb_ary_new_from_args(2, INT2NUM(ret), INT2NUM(WEXITSTATUS(status)));
1289
+ return rb_ary_new_from_args(2, INT2FIX(ret), INT2FIX(WEXITSTATUS(status)));
1290
1290
  }
1291
1291
  #else
1292
1292
  struct libev_child {
@@ -1299,7 +1299,7 @@ void Backend_child_callback(EV_P_ ev_child *w, int revents) {
1299
1299
  int exit_status = WEXITSTATUS(w->rstatus);
1300
1300
  VALUE status;
1301
1301
 
1302
- status = rb_ary_new_from_args(2, INT2NUM(w->rpid), INT2NUM(exit_status));
1302
+ status = rb_ary_new_from_args(2, INT2FIX(w->rpid), INT2FIX(exit_status));
1303
1303
  Fiber_make_runnable(watcher->fiber, status);
1304
1304
  }
1305
1305
 
@@ -1310,7 +1310,7 @@ VALUE Backend_waitpid(VALUE self, VALUE pid) {
1310
1310
  GetBackend(self, backend);
1311
1311
 
1312
1312
  watcher.fiber = rb_fiber_current();
1313
- ev_child_init(&watcher.child, Backend_child_callback, NUM2INT(pid), 0);
1313
+ ev_child_init(&watcher.child, Backend_child_callback, FIX2INT(pid), 0);
1314
1314
  ev_child_start(backend->ev_loop, &watcher.child);
1315
1315
  backend->base.op_count++;
1316
1316
 
@@ -1491,7 +1491,7 @@ VALUE Backend_splice_chunks(VALUE self, VALUE src, VALUE dest, VALUE prefix, VAL
1491
1491
 
1492
1492
  struct libev_rw_io watcher;
1493
1493
  watcher.ctx.fiber = Qnil;
1494
- int maxlen = NUM2INT(chunk_size);
1494
+ int maxlen = FIX2INT(chunk_size);
1495
1495
  VALUE str = Qnil;
1496
1496
  VALUE chunk_len_value = Qnil;
1497
1497
 
@@ -1515,7 +1515,7 @@ VALUE Backend_splice_chunks(VALUE self, VALUE src, VALUE dest, VALUE prefix, VAL
1515
1515
  if (chunk_len == 0) break;
1516
1516
 
1517
1517
  total += chunk_len;
1518
- chunk_len_value = INT2NUM(chunk_len);
1518
+ chunk_len_value = INT2FIX(chunk_len);
1519
1519
 
1520
1520
  if (chunk_prefix != Qnil) {
1521
1521
  VALUE str = (TYPE(chunk_prefix) == T_STRING) ? chunk_prefix : rb_funcall(chunk_prefix, ID_call, 1, chunk_len_value);
@@ -1553,7 +1553,7 @@ VALUE Backend_splice_chunks(VALUE self, VALUE src, VALUE dest, VALUE prefix, VAL
1553
1553
  RB_GC_GUARD(result);
1554
1554
  if (pipefd[0] != -1) close(pipefd[0]);
1555
1555
  if (pipefd[1] != -1) close(pipefd[1]);
1556
- return INT2NUM(total);
1556
+ return INT2FIX(total);
1557
1557
  syscallerror:
1558
1558
  if (pipefd[0] != -1) close(pipefd[0]);
1559
1559
  if (pipefd[1] != -1) close(pipefd[1]);
@@ -502,7 +502,6 @@ static inline VALUE z_stream_cleanup(struct z_stream_ctx *ctx) {
502
502
  return Qnil;
503
503
  }
504
504
 
505
- #define SAFE(f) (VALUE (*)(VALUE))(f)
506
505
  #define Z_STREAM_SAFE_IO_LOOP_WITH_CLEANUP(ctx) \
507
506
  rb_ensure(SAFE(z_stream_io_loop), (VALUE)&ctx, SAFE(z_stream_cleanup), (VALUE)&ctx)
508
507
 
@@ -94,6 +94,12 @@ 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_BACKEND_LIBURING
98
+ VALUE Polyphony_backend_double_splice_to_eof(VALUE self, VALUE src, VALUE dest) {
99
+ return Backend_double_splice_to_eof(BACKEND(), src, dest);
100
+ }
101
+ #endif
102
+
97
103
  #ifdef POLYPHONY_LINUX
98
104
  VALUE Polyphony_backend_tee(VALUE self, VALUE src, VALUE dest, VALUE chunksize) {
99
105
  return Backend_tee(BACKEND(), src, dest, chunksize);
@@ -126,7 +132,7 @@ VALUE Polyphony_backend_write(int argc, VALUE *argv, VALUE self) {
126
132
 
127
133
  VALUE Polyphony_with_raw_buffer(VALUE self, VALUE size) {
128
134
  struct raw_buffer buffer;
129
- buffer.len = NUM2INT(size);
135
+ buffer.len = FIX2INT(size);
130
136
  buffer.ptr = malloc(buffer.len);
131
137
  if (!buffer.ptr)
132
138
  rb_raise(rb_eRuntimeError, "Failed to allocate buffer");
@@ -187,6 +193,10 @@ void Init_Polyphony() {
187
193
  rb_define_singleton_method(mPolyphony, "backend_splice", Polyphony_backend_splice, 3);
188
194
  rb_define_singleton_method(mPolyphony, "backend_splice_to_eof", Polyphony_backend_splice_to_eof, 3);
189
195
 
196
+ #ifdef POLYPHONY_BACKEND_LIBURING
197
+ rb_define_singleton_method(mPolyphony, "backend_double_splice_to_eof", Polyphony_backend_double_splice_to_eof, 2);
198
+ #endif
199
+
190
200
  #ifdef POLYPHONY_LINUX
191
201
  rb_define_singleton_method(mPolyphony, "backend_tee", Polyphony_backend_tee, 3);
192
202
  #endif
@@ -35,6 +35,9 @@
35
35
 
36
36
  #define BACKEND() (rb_ivar_get(rb_thread_current(), ID_ivar_backend))
37
37
 
38
+ // SAFE is used to cast functions used in rb_ensure
39
+ #define SAFE(f) (VALUE (*)(VALUE))(f)
40
+
38
41
  extern VALUE mPolyphony;
39
42
  extern VALUE cPipe;
40
43
  extern VALUE cQueue;
@@ -114,6 +117,10 @@ VALUE Backend_sleep(VALUE self, VALUE duration);
114
117
  VALUE Backend_splice(VALUE self, VALUE src, VALUE dest, VALUE maxlen);
115
118
  VALUE Backend_splice_to_eof(VALUE self, VALUE src, VALUE dest, VALUE chunksize);
116
119
 
120
+ #ifdef POLYPHONY_BACKEND_LIBURING
121
+ VALUE Backend_double_splice_to_eof(VALUE self, VALUE src, VALUE dest);
122
+ #endif
123
+
117
124
  #ifdef POLYPHONY_LINUX
118
125
  VALUE Backend_tee(VALUE self, VALUE src, VALUE dest, VALUE maxlen);
119
126
  #endif
@@ -210,7 +210,7 @@ VALUE Queue_capped_p(VALUE self) {
210
210
  Queue_t *queue;
211
211
  GetQueue(self, queue);
212
212
 
213
- return queue->capacity ? UINT2NUM(queue->capacity) : Qnil;
213
+ return queue->capacity ? INT2FIX(queue->capacity) : Qnil;
214
214
  }
215
215
 
216
216
  VALUE Queue_clear(VALUE self) {
@@ -280,14 +280,14 @@ VALUE Queue_num_waiting(VALUE self) {
280
280
  Queue_t *queue;
281
281
  GetQueue(self, queue);
282
282
 
283
- return INT2NUM(queue->shift_queue.count);
283
+ return INT2FIX(queue->shift_queue.count);
284
284
  }
285
285
 
286
286
  VALUE Queue_size_m(VALUE self) {
287
287
  Queue_t *queue;
288
288
  GetQueue(self, queue);
289
289
 
290
- return INT2NUM(queue->values.count);
290
+ return INT2FIX(queue->values.count);
291
291
  }
292
292
 
293
293
  VALUE Queue_closed_p(VALUE self) {
@@ -6,13 +6,13 @@ VALUE Socket_send(VALUE self, VALUE msg, VALUE flags) {
6
6
 
7
7
  VALUE Socket_write(int argc, VALUE *argv, VALUE self) {
8
8
  VALUE ary = rb_ary_new_from_values(argc, argv);
9
- VALUE result = Backend_sendv(BACKEND(), self, ary, INT2NUM(0));
9
+ VALUE result = Backend_sendv(BACKEND(), self, ary, INT2FIX(0));
10
10
  RB_GC_GUARD(ary);
11
11
  return result;
12
12
  }
13
13
 
14
14
  VALUE Socket_double_chevron(VALUE self, VALUE msg) {
15
- Backend_send(BACKEND(), self, msg, INT2NUM(0));
15
+ Backend_send(BACKEND(), self, msg, INT2FIX(0));
16
16
  return self;
17
17
  }
18
18
 
@@ -83,6 +83,10 @@ class ::IO
83
83
  end
84
84
 
85
85
  if RUBY_PLATFORM =~ /linux/
86
+ def double_splice_to_eof(src, dest)
87
+ Polyphony.backend_double_splice_to_eof(src, dest)
88
+ end
89
+
86
90
  def tee(src, dest, maxlen)
87
91
  Polyphony.backend_tee(src, dest, maxlen)
88
92
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Polyphony
4
- VERSION = '0.90'
4
+ VERSION = '0.91'
5
5
  end
data/test/stress.rb CHANGED
@@ -3,7 +3,7 @@
3
3
  count = ARGV[0] ? ARGV[0].to_i : 100
4
4
  test_name = ARGV[1]
5
5
 
6
- $test_cmd = +'ruby test/run.rb --name test_cross_thread_send_receive'
6
+ $test_cmd = +'ruby test/run.rb --name test_signal_handler_trace'
7
7
  if test_name
8
8
  $test_cmd << " --name #{test_name}"
9
9
  end
data/test/test_io.rb CHANGED
@@ -368,6 +368,31 @@ class IOTest < MiniTest::Test
368
368
  end
369
369
  end
370
370
 
371
+ def test_double_splice_to_eof
372
+ if Thread.current.backend.kind == :io_uring
373
+ skip "IO.double_splice_to_eof available only with io_uring"
374
+ end
375
+
376
+ src = Polyphony.pipe
377
+ dest = Polyphony.pipe
378
+ ret = nil
379
+ data = 'foobar' * 10
380
+
381
+ f1 = spin {
382
+ ret = IO.double_splice_to_eof(src, dest)
383
+ dest.close
384
+ }
385
+
386
+ src << data
387
+ src.close
388
+
389
+ f1.await
390
+
391
+ spliced = dest.read
392
+ assert_equal data, spliced
393
+ assert_equal data.bytesize, ret
394
+ end
395
+
371
396
  def test_tee_from
372
397
  skip "tested only on Linux" unless RUBY_PLATFORM =~ /linux/
373
398
 
data/test/test_signal.rb CHANGED
@@ -32,11 +32,15 @@ class SignalTrapTest < Minitest::Test
32
32
  trap ('SIGINT') { raise Interrupt }
33
33
  end
34
34
 
35
+ # remove [:spin, :oob], [:schedule, :oob], since they are generated inside
36
+ # the signal handler, which occur while the trace proc is running.
37
+ events = events.reject do |e|
38
+ e == [:spin, :oob] || e == [:schedule, :oob]
39
+ end
40
+
35
41
  expected = [
36
42
  [:block, :main],
37
43
  [:enter_poll, :main],
38
- [:spin, :oob],
39
- [:schedule, :oob],
40
44
  [:leave_poll, :main],
41
45
  [:unblock, :oob],
42
46
  [:terminate, :oob],
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.90'
4
+ version: '0.91'
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-21 00:00:00.000000000 Z
11
+ date: 2022-03-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake-compiler
@@ -305,6 +305,12 @@ files:
305
305
  - examples/performance/thread-vs-fiber/threaded_server.rb
306
306
  - examples/performance/thread_pool_perf.rb
307
307
  - examples/performance/thread_switch.rb
308
+ - examples/pipes/echo_server.rb
309
+ - examples/pipes/gunzip.rb
310
+ - examples/pipes/gzip.rb
311
+ - examples/pipes/gzip_http_server.rb
312
+ - examples/pipes/tcp_proxy.rb
313
+ - examples/pipes/tee.rb
308
314
  - ext/libev/Changes
309
315
  - ext/libev/LICENSE
310
316
  - ext/libev/README