polyphony 0.56.0 → 0.57.0

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: 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