polyphony 0.56.0 → 0.57.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c6c9247de0e5049128fffb31879fd0a79ba6fe304e3802a5b7fa69baa4445ecf
4
- data.tar.gz: f2e432fb0f9d6362b7d38834a0e37c7907328c6a27d6544e1dad5363ba4f715c
3
+ metadata.gz: e350937221c476548465a2881e64689042137eab59571a0940f63236a249563f
4
+ data.tar.gz: 14870d9e38e7aa9f300d09cdfa0693929edb59756e0d17e7b3e7ae2d4a9dab0d
5
5
  SHA512:
6
- metadata.gz: 3d69bdb3f12cb65cf580cc243ea0a4f479b5306cf087bcfc5e9d8def1fee49b998528f881844649bc01b501ea22371632fd1f4f08d1f8118692bdf2331f698e6
7
- data.tar.gz: d49152bd9c9be94835a020f829c7ddc7f3766fcac9fca8e652bd8df43daab7a6eda90c94b40e57b3cef064d1ff2a957f42030aedefae1b2d1ad6a6587cf6fa86
6
+ metadata.gz: 8087b84c7a583c8f3905c20d06aa4ad119fd2901be2594e66f56b577c1fa141f9e203971aedd27ad7c57b9c184adad1b97d0fbe4024702bfe3ef6bdaa861ac92
7
+ data.tar.gz: 6943231ef2b29dac3e33cfee865dbc6b3b35549c62e04dd1ed08d40fbd90a5c179417baf3b27b965e80c3ee7e137cbe167be9e8187dff46d89892978beb8a40d
data/CHANGELOG.md CHANGED
@@ -1,3 +1,9 @@
1
+ ## 0.57.0 2021-06-23
2
+
3
+ - Implement `Backend#splice_chunks` method for both libev and io_uring backends
4
+ - Improve waiting for readiness in libev `Backend#splice`, `#splice_to_eof`
5
+ - Enable splice op in libev `Backend#chain` for non-Linux OS
6
+
1
7
  ## 0.56.0 2021-06-22
2
8
 
3
9
  - Implement fake `Backend#splice`, `Backend#splice_to_eof` methods for non-Linux
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- polyphony (0.56.0)
4
+ polyphony (0.57.0)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+ require 'polyphony'
5
+
6
+ i, o = IO.pipe
7
+
8
+ f = spin do
9
+ i.read_loop { |data| STDOUT << data }
10
+ end
11
+
12
+ result = nil
13
+ # File.open(__FILE__, 'r') do |f|
14
+ File.open('../tipi/log', 'r') do |f|
15
+ result = Thread.current.backend.splice_chunks(
16
+ f,
17
+ o,
18
+ "Content-Type: ruby\n\n",
19
+ "0\r\n\r\n",
20
+ ->(len) { "#{len.to_s(16)}\r\n" },
21
+ "\r\n",
22
+ 16384
23
+ )
24
+ end
25
+
26
+
27
+ o.close
28
+ f.await
29
+ p result: result
@@ -239,8 +239,10 @@ int io_uring_backend_defer_submit_and_await(
239
239
  {
240
240
  VALUE switchpoint_result = Qnil;
241
241
 
242
- io_uring_sqe_set_data(sqe, ctx);
243
- io_uring_sqe_set_flags(sqe, IOSQE_ASYNC);
242
+ if (sqe) {
243
+ io_uring_sqe_set_data(sqe, ctx);
244
+ io_uring_sqe_set_flags(sqe, IOSQE_ASYNC);
245
+ }
244
246
  io_uring_backend_defer_submit(backend);
245
247
 
246
248
  switchpoint_result = backend_await((struct Backend_base *)backend);
@@ -446,7 +448,7 @@ VALUE Backend_write(VALUE self, VALUE io, VALUE str) {
446
448
  VALUE resume_value = Qnil;
447
449
  op_context_t *ctx = context_store_acquire(&backend->store, OP_WRITE);
448
450
  struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
449
- io_uring_prep_write(sqe, fptr->fd, buf, left, -1);
451
+ io_uring_prep_write(sqe, fptr->fd, buf, left, 0);
450
452
 
451
453
  int result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
452
454
  int completed = context_store_release(&backend->store, ctx);
@@ -1065,7 +1067,7 @@ struct io_uring_sqe *Backend_chain_prepare_write(Backend_t *backend, VALUE io, V
1065
1067
  long len = RSTRING_LEN(str);
1066
1068
 
1067
1069
  struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
1068
- io_uring_prep_write(sqe, fptr->fd, buf, len, -1);
1070
+ io_uring_prep_write(sqe, fptr->fd, buf, len, 0);
1069
1071
  return sqe;
1070
1072
  }
1071
1073
 
@@ -1190,6 +1192,160 @@ inline VALUE Backend_run_idle_tasks(VALUE self) {
1190
1192
  return self;
1191
1193
  }
1192
1194
 
1195
+ static inline void splice_chunks_prep_write(op_context_t *ctx, struct io_uring_sqe *sqe, int fd, VALUE str) {
1196
+ char *buf = RSTRING_PTR(str);
1197
+ int len = RSTRING_LEN(str);
1198
+ io_uring_prep_write(sqe, fd, buf, len, 0);
1199
+ // io_uring_prep_send(sqe, fd, buf, len, 0);
1200
+ io_uring_sqe_set_data(sqe, ctx);
1201
+ }
1202
+
1203
+ static inline void splice_chunks_prep_splice(op_context_t *ctx, struct io_uring_sqe *sqe, int src, int dest, int maxlen) {
1204
+ io_uring_prep_splice(sqe, src, -1, dest, -1, maxlen, 0);
1205
+ io_uring_sqe_set_data(sqe, ctx);
1206
+ }
1207
+
1208
+ static inline void splice_chunks_get_sqe(
1209
+ Backend_t *backend,
1210
+ op_context_t **ctx,
1211
+ struct io_uring_sqe **sqe,
1212
+ enum op_type type
1213
+ )
1214
+ {
1215
+ if (*ctx) {
1216
+ if (*sqe) (*sqe)->flags |= IOSQE_IO_LINK;
1217
+ (*ctx)->ref_count++;
1218
+ }
1219
+ else
1220
+ *ctx = context_store_acquire(&backend->store, type);
1221
+ (*sqe) = io_uring_get_sqe(&backend->ring);
1222
+ }
1223
+
1224
+ static inline void splice_chunks_cancel(Backend_t *backend, op_context_t *ctx) {
1225
+ ctx->result = -ECANCELED;
1226
+ struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
1227
+ io_uring_prep_cancel(sqe, ctx, 0);
1228
+ backend->pending_sqes = 0;
1229
+ io_uring_submit(&backend->ring);
1230
+ }
1231
+
1232
+ static inline int splice_chunks_await_ops(
1233
+ Backend_t *backend,
1234
+ op_context_t **ctx,
1235
+ int *result,
1236
+ VALUE *switchpoint_result
1237
+ )
1238
+ {
1239
+ int res = io_uring_backend_defer_submit_and_await(backend, 0, *ctx, switchpoint_result);
1240
+ if (result) (*result) = res;
1241
+ int completed = context_store_release(&backend->store, *ctx);
1242
+ if (!completed) {
1243
+ splice_chunks_cancel(backend, *ctx);
1244
+ if (TEST_EXCEPTION(*switchpoint_result)) return 1;
1245
+ }
1246
+ *ctx = 0;
1247
+ return 0;
1248
+ }
1249
+
1250
+ #define SPLICE_CHUNKS_AWAIT_OPS(backend, ctx, result, switchpoint_result) \
1251
+ if (splice_chunks_await_ops(backend, ctx, result, switchpoint_result)) goto error;
1252
+
1253
+ VALUE Backend_splice_chunks(VALUE self, VALUE src, VALUE dest, VALUE prefix, VALUE postfix, VALUE chunk_prefix, VALUE chunk_postfix, VALUE chunk_size) {
1254
+ Backend_t *backend;
1255
+ GetBackend(self, backend);
1256
+ int total = 0;
1257
+ int err = 0;
1258
+ VALUE switchpoint_result = Qnil;
1259
+ op_context_t *ctx = 0;
1260
+ struct io_uring_sqe *sqe = 0;
1261
+
1262
+ rb_io_t *src_fptr;
1263
+ rb_io_t *dest_fptr;
1264
+
1265
+ VALUE underlying_io = rb_ivar_get(src, ID_ivar_io);
1266
+ if (underlying_io != Qnil) src = underlying_io;
1267
+ GetOpenFile(src, src_fptr);
1268
+ io_verify_blocking_mode(src_fptr, src, Qtrue);
1269
+
1270
+ underlying_io = rb_ivar_get(dest, ID_ivar_io);
1271
+ if (underlying_io != Qnil) dest = underlying_io;
1272
+ dest = rb_io_get_write_io(dest);
1273
+ GetOpenFile(dest, dest_fptr);
1274
+ io_verify_blocking_mode(dest_fptr, dest, Qtrue);
1275
+
1276
+ int maxlen = NUM2INT(chunk_size);
1277
+ VALUE str = Qnil;
1278
+ VALUE chunk_len_value = Qnil;
1279
+
1280
+ int pipefd[2] = { -1, -1 };
1281
+ if (pipe(pipefd) == -1) {
1282
+ err = errno;
1283
+ goto syscallerror;
1284
+ }
1285
+
1286
+ if (prefix != Qnil) {
1287
+ splice_chunks_get_sqe(backend, &ctx, &sqe, OP_WRITE);
1288
+ splice_chunks_prep_write(ctx, sqe, dest_fptr->fd, prefix);
1289
+ }
1290
+
1291
+ while (1) {
1292
+ int chunk_len;
1293
+ VALUE chunk_prefix_str = Qnil;
1294
+ VALUE chunk_postfix_str = Qnil;
1295
+
1296
+ splice_chunks_get_sqe(backend, &ctx, &sqe, OP_SPLICE);
1297
+ splice_chunks_prep_splice(ctx, sqe, src_fptr->fd, pipefd[1], maxlen);
1298
+
1299
+ SPLICE_CHUNKS_AWAIT_OPS(backend, &ctx, &chunk_len, &switchpoint_result);
1300
+ if (chunk_len == 0) break;
1301
+
1302
+ total += chunk_len;
1303
+ chunk_len_value = INT2NUM(chunk_len);
1304
+
1305
+
1306
+ if (chunk_prefix != Qnil) {
1307
+ chunk_prefix_str = (TYPE(chunk_prefix) == T_STRING) ? chunk_prefix : rb_funcall(chunk_prefix, ID_call, 1, chunk_len_value);
1308
+ splice_chunks_get_sqe(backend, &ctx, &sqe, OP_WRITE);
1309
+ splice_chunks_prep_write(ctx, sqe, dest_fptr->fd, chunk_prefix_str);
1310
+ }
1311
+
1312
+ splice_chunks_get_sqe(backend, &ctx, &sqe, OP_SPLICE);
1313
+ splice_chunks_prep_splice(ctx, sqe, pipefd[0], dest_fptr->fd, chunk_len);
1314
+
1315
+ if (chunk_postfix != Qnil) {
1316
+ chunk_postfix_str = (TYPE(chunk_postfix) == T_STRING) ? chunk_postfix : rb_funcall(chunk_postfix, ID_call, 1, chunk_len_value);
1317
+ splice_chunks_get_sqe(backend, &ctx, &sqe, OP_WRITE);
1318
+ splice_chunks_prep_write(ctx, sqe, dest_fptr->fd, chunk_postfix_str);
1319
+ }
1320
+
1321
+ RB_GC_GUARD(chunk_prefix_str);
1322
+ RB_GC_GUARD(chunk_postfix_str);
1323
+ }
1324
+
1325
+ if (postfix != Qnil) {
1326
+ splice_chunks_get_sqe(backend, &ctx, &sqe, OP_WRITE);
1327
+ splice_chunks_prep_write(ctx, sqe, dest_fptr->fd, postfix);
1328
+ }
1329
+ if (ctx) {
1330
+ SPLICE_CHUNKS_AWAIT_OPS(backend, &ctx, 0, &switchpoint_result);
1331
+ }
1332
+
1333
+ RB_GC_GUARD(str);
1334
+ RB_GC_GUARD(chunk_len_value);
1335
+ RB_GC_GUARD(switchpoint_result);
1336
+ if (pipefd[0] != -1) close(pipefd[0]);
1337
+ if (pipefd[1] != -1) close(pipefd[1]);
1338
+ return INT2NUM(total);
1339
+ syscallerror:
1340
+ if (pipefd[0] != -1) close(pipefd[0]);
1341
+ if (pipefd[1] != -1) close(pipefd[1]);
1342
+ rb_syserr_fail(err, strerror(err));
1343
+ error:
1344
+ if (pipefd[0] != -1) close(pipefd[0]);
1345
+ if (pipefd[1] != -1) close(pipefd[1]);
1346
+ return RAISE_EXCEPTION(switchpoint_result);
1347
+ }
1348
+
1193
1349
  void Init_Backend() {
1194
1350
  VALUE cBackend = rb_define_class_under(mPolyphony, "Backend", rb_cObject);
1195
1351
  rb_define_alloc_func(cBackend, Backend_allocate);
@@ -1203,6 +1359,7 @@ void Init_Backend() {
1203
1359
  rb_define_method(cBackend, "kind", Backend_kind, 0);
1204
1360
  rb_define_method(cBackend, "chain", Backend_chain, -1);
1205
1361
  rb_define_method(cBackend, "idle_gc_period=", Backend_idle_gc_period_set, 1);
1362
+ rb_define_method(cBackend, "splice_chunks", Backend_splice_chunks, 7);
1206
1363
 
1207
1364
  rb_define_method(cBackend, "accept", Backend_accept, 2);
1208
1365
  rb_define_method(cBackend, "accept_loop", Backend_accept_loop, 2);
@@ -737,10 +737,63 @@ error:
737
737
  return RAISE_EXCEPTION(switchpoint_result);
738
738
  }
739
739
 
740
+ struct libev_rw_ctx {
741
+ int ref_count;
742
+ VALUE fiber;
743
+ };
744
+
745
+ struct libev_ref_count_io {
746
+ struct ev_io io;
747
+ struct libev_rw_ctx *ctx;
748
+ };
749
+
750
+ struct libev_rw_io {
751
+ struct libev_ref_count_io r;
752
+ struct libev_ref_count_io w;
753
+ struct libev_rw_ctx ctx;
754
+ };
755
+
756
+ void Backend_rw_io_callback(EV_P_ ev_io *w, int revents)
757
+ {
758
+ struct libev_ref_count_io *watcher = (struct libev_ref_count_io *)w;
759
+ int ref_count = watcher->ctx->ref_count--;
760
+ if (!ref_count)
761
+ Fiber_make_runnable(watcher->ctx->fiber, Qnil);
762
+ }
763
+
764
+ VALUE libev_wait_rw_fd_with_watcher(Backend_t *backend, int r_fd, int w_fd, struct libev_rw_io *watcher) {
765
+ VALUE switchpoint_result = Qnil;
766
+
767
+ if (watcher->ctx.fiber == Qnil) watcher->ctx.fiber = rb_fiber_current();
768
+ watcher->ctx.ref_count = 0;
769
+ if (r_fd != -1) {
770
+ ev_io_init(&watcher->r.io, Backend_rw_io_callback, r_fd, EV_READ);
771
+ ev_io_start(backend->ev_loop, &watcher->r.io);
772
+ watcher->r.ctx = &watcher->ctx;
773
+ watcher->ctx.ref_count++;
774
+ }
775
+ if (w_fd != -1) {
776
+ ev_io_init(&watcher->w.io, Backend_rw_io_callback, w_fd, EV_WRITE);
777
+ ev_io_start(backend->ev_loop, &watcher->w.io);
778
+ watcher->w.ctx = &watcher->ctx;
779
+ watcher->ctx.ref_count++;
780
+ }
781
+
782
+ switchpoint_result = backend_await((struct Backend_base *)backend);
783
+
784
+ if (r_fd != -1) ev_io_stop(backend->ev_loop, &watcher->r.io);
785
+ if (w_fd != -1) ev_io_stop(backend->ev_loop, &watcher->w.io);
786
+ RB_GC_GUARD(switchpoint_result);
787
+ return switchpoint_result;
788
+ }
789
+
790
+
791
+
792
+
740
793
  #ifdef POLYPHONY_LINUX
741
794
  VALUE Backend_splice(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
742
795
  Backend_t *backend;
743
- struct libev_io watcher;
796
+ struct libev_rw_io watcher;
744
797
  VALUE switchpoint_result = Qnil;
745
798
  VALUE underlying_io;
746
799
  rb_io_t *src_fptr;
@@ -760,17 +813,14 @@ VALUE Backend_splice(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
760
813
  GetOpenFile(dest, dest_fptr);
761
814
  io_verify_blocking_mode(dest_fptr, dest, Qfalse);
762
815
 
763
- watcher.fiber = Qnil;
816
+ watcher.ctx.fiber = Qnil;
764
817
  while (1) {
765
818
  len = splice(src_fptr->fd, 0, dest_fptr->fd, 0, NUM2INT(maxlen), 0);
766
819
  if (len < 0) {
767
820
  int e = errno;
768
821
  if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
769
822
 
770
- switchpoint_result = libev_wait_fd_with_watcher(backend, src_fptr->fd, &watcher, EV_READ);
771
- if (TEST_EXCEPTION(switchpoint_result)) goto error;
772
-
773
- switchpoint_result = libev_wait_fd_with_watcher(backend, dest_fptr->fd, &watcher, EV_WRITE);
823
+ switchpoint_result = libev_wait_rw_fd_with_watcher(backend, src_fptr->fd, dest_fptr->fd, &watcher);
774
824
  if (TEST_EXCEPTION(switchpoint_result)) goto error;
775
825
  }
776
826
  else {
@@ -778,12 +828,12 @@ VALUE Backend_splice(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
778
828
  }
779
829
  }
780
830
 
781
- if (watcher.fiber == Qnil) {
831
+ if (watcher.ctx.fiber == Qnil) {
782
832
  switchpoint_result = backend_snooze();
783
833
  if (TEST_EXCEPTION(switchpoint_result)) goto error;
784
834
  }
785
835
 
786
- RB_GC_GUARD(watcher.fiber);
836
+ RB_GC_GUARD(watcher.ctx.fiber);
787
837
  RB_GC_GUARD(switchpoint_result);
788
838
 
789
839
  return INT2NUM(len);
@@ -793,7 +843,7 @@ error:
793
843
 
794
844
  VALUE Backend_splice_to_eof(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
795
845
  Backend_t *backend;
796
- struct libev_io watcher;
846
+ struct libev_rw_io watcher;
797
847
  VALUE switchpoint_result = Qnil;
798
848
  VALUE underlying_io;
799
849
  rb_io_t *src_fptr;
@@ -814,17 +864,14 @@ VALUE Backend_splice_to_eof(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
814
864
  GetOpenFile(dest, dest_fptr);
815
865
  io_verify_blocking_mode(dest_fptr, dest, Qfalse);
816
866
 
817
- watcher.fiber = Qnil;
867
+ watcher.ctx.fiber = Qnil;
818
868
  while (1) {
819
869
  len = splice(src_fptr->fd, 0, dest_fptr->fd, 0, NUM2INT(maxlen), 0);
820
870
  if (len < 0) {
821
871
  int e = errno;
822
872
  if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
823
873
 
824
- switchpoint_result = libev_wait_fd_with_watcher(backend, src_fptr->fd, &watcher, EV_READ);
825
- if (TEST_EXCEPTION(switchpoint_result)) goto error;
826
-
827
- switchpoint_result = libev_wait_fd_with_watcher(backend, dest_fptr->fd, &watcher, EV_WRITE);
874
+ switchpoint_result = libev_wait_rw_fd_with_watcher(backend, src_fptr->fd, dest_fptr->fd, &watcher);
828
875
  if (TEST_EXCEPTION(switchpoint_result)) goto error;
829
876
  }
830
877
  else if (len == 0) {
@@ -835,12 +882,12 @@ VALUE Backend_splice_to_eof(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
835
882
  }
836
883
  }
837
884
 
838
- if (watcher.fiber == Qnil) {
885
+ if (watcher.ctx.fiber == Qnil) {
839
886
  switchpoint_result = backend_snooze();
840
887
  if (TEST_EXCEPTION(switchpoint_result)) goto error;
841
888
  }
842
889
 
843
- RB_GC_GUARD(watcher.fiber);
890
+ RB_GC_GUARD(watcher.ctx.fiber);
844
891
  RB_GC_GUARD(switchpoint_result);
845
892
 
846
893
  return INT2NUM(total);
@@ -1225,10 +1272,8 @@ VALUE Backend_chain(int argc,VALUE *argv, VALUE self) {
1225
1272
  result = Backend_write(self, RARRAY_AREF(op, 1), RARRAY_AREF(op, 2));
1226
1273
  else if (op_type == SYM_send && op_len == 4)
1227
1274
  result = Backend_send(self, RARRAY_AREF(op, 1), RARRAY_AREF(op, 2), RARRAY_AREF(op, 3));
1228
- #ifdef POLYPHONY_LINUX
1229
1275
  else if (op_type == SYM_splice && op_len == 4)
1230
1276
  result = Backend_splice(self, RARRAY_AREF(op, 1), RARRAY_AREF(op, 2), RARRAY_AREF(op, 3));
1231
- #endif
1232
1277
  else
1233
1278
  rb_raise(rb_eRuntimeError, "Invalid op specified or bad op arity");
1234
1279
  }
@@ -1252,6 +1297,142 @@ inline VALUE Backend_run_idle_tasks(VALUE self) {
1252
1297
  return self;
1253
1298
  }
1254
1299
 
1300
+ inline int splice_chunks_write(Backend_t *backend, int fd, VALUE str, struct libev_rw_io *watcher, VALUE *result) {
1301
+ char *buf = RSTRING_PTR(str);
1302
+ int len = RSTRING_LEN(str);
1303
+ int left = len;
1304
+ while (left > 0) {
1305
+ ssize_t n = write(fd, buf, left);
1306
+ if (n < 0) {
1307
+ int err = errno;
1308
+ if ((err != EWOULDBLOCK && err != EAGAIN)) return err;
1309
+
1310
+ *result = libev_wait_rw_fd_with_watcher(backend, -1, fd, watcher);
1311
+ if (TEST_EXCEPTION(*result)) return -1;
1312
+ }
1313
+ else {
1314
+ buf += n;
1315
+ left -= n;
1316
+ }
1317
+ }
1318
+ return 0;
1319
+ }
1320
+
1321
+ VALUE Backend_splice_chunks(VALUE self, VALUE src, VALUE dest, VALUE prefix, VALUE postfix, VALUE chunk_prefix, VALUE chunk_postfix, VALUE chunk_size) {
1322
+ Backend_t *backend;
1323
+ GetBackend(self, backend);
1324
+ int total = 0;
1325
+ int err = 0;
1326
+ VALUE result = Qnil;
1327
+
1328
+ rb_io_t *src_fptr;
1329
+ rb_io_t *dest_fptr;
1330
+
1331
+ VALUE underlying_io = rb_ivar_get(src, ID_ivar_io);
1332
+ if (underlying_io != Qnil) src = underlying_io;
1333
+ GetOpenFile(src, src_fptr);
1334
+ io_verify_blocking_mode(src_fptr, src, Qfalse);
1335
+
1336
+ underlying_io = rb_ivar_get(dest, ID_ivar_io);
1337
+ if (underlying_io != Qnil) dest = underlying_io;
1338
+ dest = rb_io_get_write_io(dest);
1339
+ GetOpenFile(dest, dest_fptr);
1340
+ io_verify_blocking_mode(dest_fptr, dest, Qfalse);
1341
+
1342
+ struct libev_rw_io watcher;
1343
+ watcher.ctx.fiber = Qnil;
1344
+ int maxlen = NUM2INT(chunk_size);
1345
+ VALUE str = Qnil;
1346
+ VALUE chunk_len_value = Qnil;
1347
+
1348
+ int pipefd[2] = { -1, -1 };
1349
+ if (pipe(pipefd) == -1) {
1350
+ err = errno;
1351
+ goto syscallerror;
1352
+ }
1353
+
1354
+ fcntl(pipefd[0], F_SETFL, O_NONBLOCK);
1355
+ fcntl(pipefd[1], F_SETFL, O_NONBLOCK);
1356
+
1357
+ if (prefix != Qnil) {
1358
+ int err = splice_chunks_write(backend, dest_fptr->fd, prefix, &watcher, &result);
1359
+ if (err == -1) goto error; else if (err) goto syscallerror;
1360
+ }
1361
+ while (1) {
1362
+ int chunk_len;
1363
+ // splice to pipe
1364
+ while (1) {
1365
+ chunk_len = splice(src_fptr->fd, 0, pipefd[1], 0, maxlen, 0);
1366
+ if (chunk_len < 0) {
1367
+ err = errno;
1368
+ if (err != EWOULDBLOCK && err != EAGAIN) goto syscallerror;
1369
+
1370
+ result = libev_wait_rw_fd_with_watcher(backend, src_fptr->fd, pipefd[1], &watcher);
1371
+ if (TEST_EXCEPTION(result)) goto error;
1372
+ }
1373
+ else {
1374
+ break;
1375
+ }
1376
+ }
1377
+ if (chunk_len == 0) break;
1378
+
1379
+ total += chunk_len;
1380
+ chunk_len_value = INT2NUM(chunk_len);
1381
+
1382
+ if (chunk_prefix != Qnil) {
1383
+ VALUE str = (TYPE(chunk_prefix) == T_STRING) ? chunk_prefix : rb_funcall(chunk_prefix, ID_call, 1, chunk_len_value);
1384
+ int err = splice_chunks_write(backend, dest_fptr->fd, str, &watcher, &result);
1385
+ if (err == -1) goto error; else if (err) goto syscallerror;
1386
+ }
1387
+
1388
+ int left = chunk_len;
1389
+ while (1) {
1390
+ int n = splice(pipefd[0], 0, dest_fptr->fd, 0, left, 0);
1391
+ if (n < 0) {
1392
+ err = errno;
1393
+ if (err != EWOULDBLOCK && err != EAGAIN) goto syscallerror;
1394
+
1395
+ result = libev_wait_rw_fd_with_watcher(backend, pipefd[0], dest_fptr->fd, &watcher);
1396
+ if (TEST_EXCEPTION(result)) goto error;
1397
+ }
1398
+ else {
1399
+ left -= n;
1400
+ if (left == 0) break;
1401
+ }
1402
+ }
1403
+
1404
+ if (chunk_postfix != Qnil) {
1405
+ VALUE str = (TYPE(chunk_postfix) == T_STRING) ? chunk_postfix : rb_funcall(chunk_postfix, ID_call, 1, chunk_len_value);
1406
+ int err = splice_chunks_write(backend, dest_fptr->fd, str, &watcher, &result);
1407
+ if (err == -1) goto error; else if (err) goto syscallerror;
1408
+ }
1409
+ }
1410
+
1411
+ if (postfix != Qnil) {
1412
+ int err = splice_chunks_write(backend, dest_fptr->fd, postfix, &watcher, &result);
1413
+ if (err == -1) goto error; else if (err) goto syscallerror;
1414
+ }
1415
+
1416
+ if (watcher.ctx.fiber == Qnil) {
1417
+ result = backend_snooze();
1418
+ if (TEST_EXCEPTION(result)) goto error;
1419
+ }
1420
+ RB_GC_GUARD(str);
1421
+ RB_GC_GUARD(chunk_len_value);
1422
+ RB_GC_GUARD(result);
1423
+ if (pipefd[0] != -1) close(pipefd[0]);
1424
+ if (pipefd[1] != -1) close(pipefd[1]);
1425
+ return INT2NUM(total);
1426
+ syscallerror:
1427
+ if (pipefd[0] != -1) close(pipefd[0]);
1428
+ if (pipefd[1] != -1) close(pipefd[1]);
1429
+ rb_syserr_fail(err, strerror(err));
1430
+ error:
1431
+ if (pipefd[0] != -1) close(pipefd[0]);
1432
+ if (pipefd[1] != -1) close(pipefd[1]);
1433
+ return RAISE_EXCEPTION(result);
1434
+ }
1435
+
1255
1436
  void Init_Backend() {
1256
1437
  ev_set_allocator(xrealloc);
1257
1438
 
@@ -1267,6 +1448,7 @@ void Init_Backend() {
1267
1448
  rb_define_method(cBackend, "kind", Backend_kind, 0);
1268
1449
  rb_define_method(cBackend, "chain", Backend_chain, -1);
1269
1450
  rb_define_method(cBackend, "idle_gc_period=", Backend_idle_gc_period_set, 1);
1451
+ rb_define_method(cBackend, "splice_chunks", Backend_splice_chunks, 7);
1270
1452
 
1271
1453
  rb_define_method(cBackend, "accept", Backend_accept, 2);
1272
1454
  rb_define_method(cBackend, "accept_loop", Backend_accept_loop, 2);
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Polyphony
4
- VERSION = '0.56.0'
4
+ VERSION = '0.57.0'
5
5
  end
data/test/test_backend.rb CHANGED
@@ -282,6 +282,41 @@ class BackendTest < MiniTest::Test
282
282
  end
283
283
  end
284
284
 
285
+
286
+ def test_splice_chunks
287
+ body = 'abcd' * 250
288
+ chunk_size = 750
289
+
290
+ buf = +''
291
+ r, w = IO.pipe
292
+ reader = spin do
293
+ r.read_loop { |data| buf << data }
294
+ end
295
+
296
+ i, o = IO.pipe
297
+ writer = spin do
298
+ o << body
299
+ o.close
300
+ end
301
+ Thread.current.backend.splice_chunks(
302
+ i,
303
+ w,
304
+ "Content-Type: foo\r\n\r\n",
305
+ "0\r\n\r\n",
306
+ ->(len) { "#{len.to_s(16)}\r\n" },
307
+ "\r\n",
308
+ chunk_size
309
+ )
310
+ w.close
311
+ reader.await
312
+
313
+ expected = "Content-Type: foo\r\n\r\n#{750.to_s(16)}\r\n#{body[0..749]}\r\n#{250.to_s(16)}\r\n#{body[750..999]}\r\n0\r\n\r\n"
314
+ assert_equal expected, buf
315
+ ensure
316
+ o.close
317
+ w.close
318
+ end
319
+
285
320
  def test_idle_gc
286
321
  GC.disable
287
322
 
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.56.0
4
+ version: 0.57.0
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-06-22 00:00:00.000000000 Z
11
+ date: 2021-06-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake-compiler
@@ -361,6 +361,7 @@ files:
361
361
  - examples/io/rack_server.rb
362
362
  - examples/io/raw.rb
363
363
  - examples/io/reline.rb
364
+ - examples/io/splice_chunks.rb
364
365
  - examples/io/stdio.rb
365
366
  - examples/io/system.rb
366
367
  - examples/io/tcp_proxy.rb