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 +4 -4
- data/CHANGELOG.md +6 -0
- data/Gemfile.lock +1 -1
- data/examples/io/splice_chunks.rb +29 -0
- data/ext/polyphony/backend_io_uring.c +161 -4
- data/ext/polyphony/backend_libev.c +200 -18
- data/lib/polyphony/version.rb +1 -1
- data/test/test_backend.rb +35 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e350937221c476548465a2881e64689042137eab59571a0940f63236a249563f
|
4
|
+
data.tar.gz: 14870d9e38e7aa9f300d09cdfa0693929edb59756e0d17e7b3e7ae2d4a9dab0d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
@@ -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
|
-
|
243
|
-
|
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,
|
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,
|
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
|
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 =
|
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
|
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 =
|
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);
|
data/lib/polyphony/version.rb
CHANGED
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.
|
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-
|
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
|