polyphony 0.65 → 0.66
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/test.yml +1 -1
- data/CHANGELOG.md +5 -0
- data/Gemfile.lock +1 -1
- data/ext/polyphony/backend_io_uring.c +69 -23
- data/ext/polyphony/backend_io_uring_context.c +42 -0
- data/ext/polyphony/backend_io_uring_context.h +6 -9
- data/ext/polyphony/backend_libev.c +71 -39
- data/ext/polyphony/queue.c +1 -1
- data/ext/polyphony/runqueue.c +3 -3
- data/ext/polyphony/runqueue.h +3 -3
- data/lib/polyphony/version.rb +1 -1
- data/test/helper.rb +2 -0
- data/test/test_backend.rb +13 -4
- data/test/test_global_api.rb +8 -4
- data/test/test_io.rb +2 -0
- data/test/test_socket.rb +14 -11
- data/test/test_thread.rb +3 -0
- data/test/test_timer.rb +5 -3
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b8e2aa3d011286dda93222164cbf66f1ec4c18b9c212ee7a70dbebd4bc523943
|
4
|
+
data.tar.gz: 99b5fb95c682dfad8df33f07d98bfab7f2109b9d4ec0e24720869c2d331f23fb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 27c65b6e340d1aad41d38b4b322b98d282f96fe6d136c897ab8de14651b2da3cfc392650600d63983f76b5fb1dc70e4e49116efb289a790a509bec63caa9b9e4
|
7
|
+
data.tar.gz: 94e62ac7fbcb6d085af7475d987eca45be191aaf7654c3bacb07c4f7793d450281d3da1b08b12636a49398e97c1043c3a2cd628018925bdc082e02a0859743bc
|
data/.github/workflows/test.yml
CHANGED
data/CHANGELOG.md
CHANGED
data/Gemfile.lock
CHANGED
@@ -25,6 +25,8 @@ VALUE SYM_send;
|
|
25
25
|
VALUE SYM_splice;
|
26
26
|
VALUE SYM_write;
|
27
27
|
|
28
|
+
VALUE eArgumentError;
|
29
|
+
|
28
30
|
#ifdef POLYPHONY_UNSET_NONBLOCK
|
29
31
|
#define io_unset_nonblock(fptr, io) io_verify_blocking_mode(fptr, io, Qtrue)
|
30
32
|
#else
|
@@ -45,6 +47,7 @@ typedef struct Backend_t {
|
|
45
47
|
static void Backend_mark(void *ptr) {
|
46
48
|
Backend_t *backend = ptr;
|
47
49
|
backend_base_mark(&backend->base);
|
50
|
+
context_store_mark_taken_buffers(&backend->store);
|
48
51
|
}
|
49
52
|
|
50
53
|
static void Backend_free(void *ptr) {
|
@@ -349,8 +352,11 @@ VALUE Backend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof,
|
|
349
352
|
|
350
353
|
int result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
|
351
354
|
int completed = context_store_release(&backend->store, ctx);
|
352
|
-
|
353
|
-
|
355
|
+
if (!completed) {
|
356
|
+
context_attach_buffers(ctx, 1, &str);
|
357
|
+
RAISE_IF_EXCEPTION(resume_value);
|
358
|
+
return resume_value;
|
359
|
+
}
|
354
360
|
RB_GC_GUARD(resume_value);
|
355
361
|
|
356
362
|
if (result < 0)
|
@@ -410,8 +416,11 @@ VALUE Backend_read_loop(VALUE self, VALUE io, VALUE maxlen) {
|
|
410
416
|
|
411
417
|
ssize_t result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
|
412
418
|
int completed = context_store_release(&backend->store, ctx);
|
413
|
-
|
414
|
-
|
419
|
+
if (!completed) {
|
420
|
+
context_attach_buffers(ctx, 1, &str);
|
421
|
+
RAISE_IF_EXCEPTION(resume_value);
|
422
|
+
return resume_value;
|
423
|
+
}
|
415
424
|
RB_GC_GUARD(resume_value);
|
416
425
|
|
417
426
|
if (result < 0)
|
@@ -457,8 +466,11 @@ VALUE Backend_feed_loop(VALUE self, VALUE io, VALUE receiver, VALUE method) {
|
|
457
466
|
|
458
467
|
ssize_t result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
|
459
468
|
int completed = context_store_release(&backend->store, ctx);
|
460
|
-
|
461
|
-
|
469
|
+
if (!completed) {
|
470
|
+
context_attach_buffers(ctx, 1, &str);
|
471
|
+
RAISE_IF_EXCEPTION(resume_value);
|
472
|
+
return resume_value;
|
473
|
+
}
|
462
474
|
RB_GC_GUARD(resume_value);
|
463
475
|
|
464
476
|
if (result < 0)
|
@@ -500,8 +512,11 @@ VALUE Backend_write(VALUE self, VALUE io, VALUE str) {
|
|
500
512
|
|
501
513
|
int result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
|
502
514
|
int completed = context_store_release(&backend->store, ctx);
|
503
|
-
|
504
|
-
|
515
|
+
if (!completed) {
|
516
|
+
context_attach_buffers(ctx, 1, &str);
|
517
|
+
RAISE_IF_EXCEPTION(resume_value);
|
518
|
+
return resume_value;
|
519
|
+
}
|
505
520
|
RB_GC_GUARD(resume_value);
|
506
521
|
|
507
522
|
if (result < 0)
|
@@ -549,12 +564,10 @@ VALUE Backend_writev(VALUE self, VALUE io, int argc, VALUE *argv) {
|
|
549
564
|
|
550
565
|
int result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
|
551
566
|
int completed = context_store_release(&backend->store, ctx);
|
552
|
-
if (TEST_EXCEPTION(resume_value)) {
|
553
|
-
free(iov);
|
554
|
-
RAISE_EXCEPTION(resume_value);
|
555
|
-
}
|
556
567
|
if (!completed) {
|
557
568
|
free(iov);
|
569
|
+
context_attach_buffers(ctx, argc, argv);
|
570
|
+
RAISE_IF_EXCEPTION(resume_value);
|
558
571
|
return resume_value;
|
559
572
|
}
|
560
573
|
RB_GC_GUARD(resume_value);
|
@@ -588,8 +601,7 @@ VALUE Backend_writev(VALUE self, VALUE io, int argc, VALUE *argv) {
|
|
588
601
|
|
589
602
|
VALUE Backend_write_m(int argc, VALUE *argv, VALUE self) {
|
590
603
|
if (argc < 2)
|
591
|
-
|
592
|
-
rb_raise(rb_eRuntimeError, "(wrong number of arguments (expected 2 or more))");
|
604
|
+
rb_raise(eArgumentError, "(wrong number of arguments (expected 2 or more))");
|
593
605
|
|
594
606
|
return (argc == 2) ?
|
595
607
|
Backend_write(self, argv[0], argv[1]) :
|
@@ -628,8 +640,11 @@ VALUE Backend_recv(VALUE self, VALUE io, VALUE str, VALUE length, VALUE pos) {
|
|
628
640
|
|
629
641
|
int result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
|
630
642
|
int completed = context_store_release(&backend->store, ctx);
|
631
|
-
|
632
|
-
|
643
|
+
if (!completed) {
|
644
|
+
context_attach_buffers(ctx, 1, &str);
|
645
|
+
RAISE_IF_EXCEPTION(resume_value);
|
646
|
+
return resume_value;
|
647
|
+
}
|
633
648
|
RB_GC_GUARD(resume_value);
|
634
649
|
|
635
650
|
if (result < 0)
|
@@ -675,8 +690,11 @@ VALUE Backend_recv_loop(VALUE self, VALUE io, VALUE maxlen) {
|
|
675
690
|
|
676
691
|
int result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
|
677
692
|
int completed = context_store_release(&backend->store, ctx);
|
678
|
-
|
679
|
-
|
693
|
+
if (!completed) {
|
694
|
+
context_attach_buffers(ctx, 1, &str);
|
695
|
+
RAISE_IF_EXCEPTION(resume_value);
|
696
|
+
return resume_value;
|
697
|
+
}
|
680
698
|
RB_GC_GUARD(resume_value);
|
681
699
|
|
682
700
|
if (result < 0)
|
@@ -721,8 +739,11 @@ VALUE Backend_recv_feed_loop(VALUE self, VALUE io, VALUE receiver, VALUE method)
|
|
721
739
|
|
722
740
|
int result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
|
723
741
|
int completed = context_store_release(&backend->store, ctx);
|
724
|
-
|
725
|
-
|
742
|
+
if (!completed) {
|
743
|
+
context_attach_buffers(ctx, 1, &str);
|
744
|
+
RAISE_IF_EXCEPTION(resume_value);
|
745
|
+
return resume_value;
|
746
|
+
}
|
726
747
|
RB_GC_GUARD(resume_value);
|
727
748
|
|
728
749
|
if (result < 0)
|
@@ -764,8 +785,11 @@ VALUE Backend_send(VALUE self, VALUE io, VALUE str, VALUE flags) {
|
|
764
785
|
|
765
786
|
int result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
|
766
787
|
int completed = context_store_release(&backend->store, ctx);
|
767
|
-
|
768
|
-
|
788
|
+
if (!completed) {
|
789
|
+
context_attach_buffers(ctx, 1, &str);
|
790
|
+
RAISE_IF_EXCEPTION(resume_value);
|
791
|
+
return resume_value;
|
792
|
+
}
|
769
793
|
RB_GC_GUARD(resume_value);
|
770
794
|
|
771
795
|
if (result < 0)
|
@@ -1165,6 +1189,24 @@ struct io_uring_sqe *Backend_chain_prepare_splice(Backend_t *backend, VALUE src,
|
|
1165
1189
|
return sqe;
|
1166
1190
|
}
|
1167
1191
|
|
1192
|
+
void Backend_chain_ctx_attach_buffers(op_context_t *ctx, int argc, VALUE *argv) {
|
1193
|
+
int count = 0;
|
1194
|
+
if (argc > 1) ctx->buffers = malloc(sizeof(VALUE) * (argc - 1));
|
1195
|
+
|
1196
|
+
for (int i = 0; i < argc; i++) {
|
1197
|
+
VALUE op = argv[i];
|
1198
|
+
VALUE op_type = RARRAY_AREF(op, 0);
|
1199
|
+
|
1200
|
+
if (op_type == SYM_write || op_type == SYM_send) {
|
1201
|
+
if (!count) ctx->buffer0 = RARRAY_AREF(op, 2);
|
1202
|
+
else ctx->buffers[count - 1] = RARRAY_AREF(op, 2);
|
1203
|
+
count++;
|
1204
|
+
}
|
1205
|
+
}
|
1206
|
+
ctx->buffer_count = count;
|
1207
|
+
}
|
1208
|
+
|
1209
|
+
|
1168
1210
|
VALUE Backend_chain(int argc,VALUE *argv, VALUE self) {
|
1169
1211
|
VALUE resume_value = Qnil;
|
1170
1212
|
unsigned int sqe_count = 0;
|
@@ -1218,6 +1260,8 @@ VALUE Backend_chain(int argc,VALUE *argv, VALUE self) {
|
|
1218
1260
|
int result = ctx->result;
|
1219
1261
|
int completed = context_store_release(&backend->store, ctx);
|
1220
1262
|
if (!completed) {
|
1263
|
+
Backend_chain_ctx_attach_buffers(ctx, argc, argv);
|
1264
|
+
|
1221
1265
|
// op was not completed (an exception was raised), so we need to cancel it
|
1222
1266
|
ctx->result = -ECANCELED;
|
1223
1267
|
struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
|
@@ -1409,6 +1453,7 @@ syscallerror:
|
|
1409
1453
|
if (pipefd[1] != -1) close(pipefd[1]);
|
1410
1454
|
rb_syserr_fail(err, strerror(err));
|
1411
1455
|
error:
|
1456
|
+
context_attach_buffers_v(ctx, 4, prefix, postfix, chunk_prefix, chunk_postfix);
|
1412
1457
|
if (pipefd[0] != -1) close(pipefd[0]);
|
1413
1458
|
if (pipefd[1] != -1) close(pipefd[1]);
|
1414
1459
|
return RAISE_EXCEPTION(switchpoint_result);
|
@@ -1469,7 +1514,6 @@ void Init_Backend() {
|
|
1469
1514
|
rb_define_method(cBackend, "waitpid", Backend_waitpid, 1);
|
1470
1515
|
rb_define_method(cBackend, "write", Backend_write_m, -1);
|
1471
1516
|
|
1472
|
-
|
1473
1517
|
#ifdef POLYPHONY_UNSET_NONBLOCK
|
1474
1518
|
ID_ivar_is_nonblocking = rb_intern("@is_nonblocking");
|
1475
1519
|
#endif
|
@@ -1480,6 +1524,8 @@ void Init_Backend() {
|
|
1480
1524
|
SYM_write = ID2SYM(rb_intern("write"));
|
1481
1525
|
|
1482
1526
|
backend_setup_stats_symbols();
|
1527
|
+
|
1528
|
+
eArgumentError = rb_const_get(rb_cObject, rb_intern("ArgumentError"));
|
1483
1529
|
}
|
1484
1530
|
|
1485
1531
|
#endif // POLYPHONY_BACKEND_LIBURING
|
@@ -50,6 +50,7 @@ inline op_context_t *context_store_acquire(op_context_store_t *store, enum op_ty
|
|
50
50
|
ctx->resume_value = Qnil;
|
51
51
|
ctx->ref_count = 2;
|
52
52
|
ctx->result = 0;
|
53
|
+
ctx->buffer_count = 0;
|
53
54
|
|
54
55
|
store->taken_count++;
|
55
56
|
|
@@ -67,6 +68,8 @@ inline int context_store_release(op_context_store_t *store, op_context_t *ctx) {
|
|
67
68
|
ctx->ref_count--;
|
68
69
|
if (ctx->ref_count) return 0;
|
69
70
|
|
71
|
+
if (ctx->buffer_count > 1) free(ctx->buffers);
|
72
|
+
|
70
73
|
store->taken_count--;
|
71
74
|
store->available_count++;
|
72
75
|
|
@@ -93,3 +96,42 @@ void context_store_free(op_context_store_t *store) {
|
|
93
96
|
store->taken = next;
|
94
97
|
}
|
95
98
|
}
|
99
|
+
|
100
|
+
inline void context_store_mark_taken_buffers(op_context_store_t *store) {
|
101
|
+
op_context_t *ctx = store->taken;
|
102
|
+
while (ctx) {
|
103
|
+
for (unsigned int i = 0; i < ctx->buffer_count; i++)
|
104
|
+
rb_gc_mark(i == 0 ? ctx->buffer0 : ctx->buffers[i - 1]);
|
105
|
+
ctx = ctx->next;
|
106
|
+
}
|
107
|
+
}
|
108
|
+
|
109
|
+
inline void context_attach_buffers(op_context_t *ctx, unsigned int count, VALUE *buffers) {
|
110
|
+
// attaching buffers to the context is done in order to ensure that any GC
|
111
|
+
// pass done before the context is released will mark those buffers, even if
|
112
|
+
// the fiber has already been resumed and the buffers are not in use anymore.
|
113
|
+
// This is done in order to prevent a possible race condition where on the
|
114
|
+
// kernel side the buffers are still in use, but in userspace they have
|
115
|
+
// effectively been freed after a GC pass.
|
116
|
+
ctx->buffer_count = count;
|
117
|
+
if (count > 1)
|
118
|
+
ctx->buffers = malloc(sizeof(VALUE) * (count - 1));
|
119
|
+
for (unsigned int i = 0; i < count; i++)
|
120
|
+
if (!i) ctx->buffer0 = buffers[0];
|
121
|
+
else ctx->buffers[i - 1] = buffers[i];
|
122
|
+
}
|
123
|
+
|
124
|
+
inline void context_attach_buffers_v(op_context_t *ctx, unsigned int count, ...) {
|
125
|
+
va_list values;
|
126
|
+
|
127
|
+
va_start(values, count);
|
128
|
+
|
129
|
+
ctx->buffer_count = count;
|
130
|
+
if (count > 1)
|
131
|
+
ctx->buffers = malloc(sizeof(VALUE) * (count - 1));
|
132
|
+
for (unsigned int i = 0; i < count; i++)
|
133
|
+
if (!i) ctx->buffer0 = va_arg(values, VALUE);
|
134
|
+
else ctx->buffers[i - 1] = va_arg(values, VALUE);
|
135
|
+
|
136
|
+
va_end(values);
|
137
|
+
}
|
@@ -27,6 +27,9 @@ typedef struct op_context {
|
|
27
27
|
int result;
|
28
28
|
VALUE fiber;
|
29
29
|
VALUE resume_value;
|
30
|
+
unsigned int buffer_count;
|
31
|
+
VALUE buffer0;
|
32
|
+
VALUE *buffers;
|
30
33
|
} op_context_t;
|
31
34
|
|
32
35
|
typedef struct op_context_store {
|
@@ -43,14 +46,8 @@ void context_store_initialize(op_context_store_t *store);
|
|
43
46
|
op_context_t *context_store_acquire(op_context_store_t *store, enum op_type type);
|
44
47
|
int context_store_release(op_context_store_t *store, op_context_t *ctx);
|
45
48
|
void context_store_free(op_context_store_t *store);
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
if (ctx->ref_count)
|
50
|
-
ctx->ref_count -= 1;
|
51
|
-
else
|
52
|
-
context_store_release(store, ctx);
|
53
|
-
return completed;
|
54
|
-
}
|
49
|
+
void context_store_mark_taken_buffers(op_context_store_t *store);
|
50
|
+
void context_attach_buffers(op_context_t *ctx, unsigned int count, VALUE *buffers);
|
51
|
+
void context_attach_buffers_v(op_context_t *ctx, unsigned int count, ...);
|
55
52
|
|
56
53
|
#endif /* BACKEND_IO_URING_CONTEXT_H */
|
@@ -941,9 +941,8 @@ VALUE Backend_splice_to_eof(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
|
|
941
941
|
error:
|
942
942
|
return RAISE_EXCEPTION(switchpoint_result);
|
943
943
|
}
|
944
|
-
#
|
945
|
-
|
946
|
-
VALUE Backend_fake_splice(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
|
944
|
+
#else
|
945
|
+
VALUE Backend_splice(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
|
947
946
|
Backend_t *backend;
|
948
947
|
struct libev_io watcher;
|
949
948
|
VALUE switchpoint_result = Qnil;
|
@@ -1018,7 +1017,7 @@ error:
|
|
1018
1017
|
return RAISE_EXCEPTION(switchpoint_result);
|
1019
1018
|
}
|
1020
1019
|
|
1021
|
-
VALUE
|
1020
|
+
VALUE Backend_splice_to_eof(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
|
1022
1021
|
Backend_t *backend;
|
1023
1022
|
struct libev_io watcher;
|
1024
1023
|
VALUE switchpoint_result = Qnil;
|
@@ -1097,6 +1096,7 @@ done:
|
|
1097
1096
|
error:
|
1098
1097
|
return RAISE_EXCEPTION(switchpoint_result);
|
1099
1098
|
}
|
1099
|
+
#endif
|
1100
1100
|
|
1101
1101
|
VALUE Backend_wait_io(VALUE self, VALUE io, VALUE write) {
|
1102
1102
|
Backend_t *backend;
|
@@ -1384,6 +1384,64 @@ inline int splice_chunks_write(Backend_t *backend, int fd, VALUE str, struct lib
|
|
1384
1384
|
return 0;
|
1385
1385
|
}
|
1386
1386
|
|
1387
|
+
static inline int splice_chunks_splice(Backend_t *backend, int src_fd, int dest_fd, int maxlen,
|
1388
|
+
struct libev_rw_io *watcher, VALUE *result, int *chunk_len) {
|
1389
|
+
#ifdef POLYPHONY_LINUX
|
1390
|
+
backend->base.op_count++;
|
1391
|
+
while (1) {
|
1392
|
+
*chunk_len = splice(src_fd, 0, dest_fd, 0, maxlen, 0);
|
1393
|
+
if (*chunk_len >= 0) return 0;
|
1394
|
+
|
1395
|
+
int err = errno;
|
1396
|
+
if (err != EWOULDBLOCK && err != EAGAIN) return err;
|
1397
|
+
|
1398
|
+
*result = libev_wait_rw_fd_with_watcher(backend, src_fd, dest_fd, watcher);
|
1399
|
+
if (TEST_EXCEPTION(*result)) return -1;
|
1400
|
+
}
|
1401
|
+
#else
|
1402
|
+
char *buf = malloc(maxlen);
|
1403
|
+
int ret;
|
1404
|
+
|
1405
|
+
backend->base.op_count++;
|
1406
|
+
while (1) {
|
1407
|
+
*chunk_len = read(src_fd, buf, maxlen);
|
1408
|
+
if (*chunk_len >= 0) break;
|
1409
|
+
|
1410
|
+
ret = errno;
|
1411
|
+
if ((ret != EWOULDBLOCK && ret != EAGAIN)) goto done;
|
1412
|
+
|
1413
|
+
*result = libev_wait_rw_fd_with_watcher(backend, src_fd, -1, watcher);
|
1414
|
+
if (TEST_EXCEPTION(*result)) goto exception;
|
1415
|
+
}
|
1416
|
+
|
1417
|
+
backend->base.op_count++;
|
1418
|
+
char *ptr = buf;
|
1419
|
+
int left = *chunk_len;
|
1420
|
+
while (left > 0) {
|
1421
|
+
ssize_t n = write(dest_fd, ptr, left);
|
1422
|
+
if (n < 0) {
|
1423
|
+
ret = errno;
|
1424
|
+
if ((ret != EWOULDBLOCK && ret != EAGAIN)) goto done;
|
1425
|
+
|
1426
|
+
*result = libev_wait_rw_fd_with_watcher(backend, -1, dest_fd, watcher);
|
1427
|
+
|
1428
|
+
if (TEST_EXCEPTION(*result)) goto exception;
|
1429
|
+
}
|
1430
|
+
else {
|
1431
|
+
ptr += n;
|
1432
|
+
left -= n;
|
1433
|
+
}
|
1434
|
+
}
|
1435
|
+
ret = 0;
|
1436
|
+
goto done;
|
1437
|
+
exception:
|
1438
|
+
ret = -1;
|
1439
|
+
done:
|
1440
|
+
free(buf);
|
1441
|
+
return ret;
|
1442
|
+
#endif
|
1443
|
+
}
|
1444
|
+
|
1387
1445
|
VALUE Backend_splice_chunks(VALUE self, VALUE src, VALUE dest, VALUE prefix, VALUE postfix, VALUE chunk_prefix, VALUE chunk_postfix, VALUE chunk_size) {
|
1388
1446
|
Backend_t *backend;
|
1389
1447
|
GetBackend(self, backend);
|
@@ -1421,26 +1479,13 @@ VALUE Backend_splice_chunks(VALUE self, VALUE src, VALUE dest, VALUE prefix, VAL
|
|
1421
1479
|
fcntl(pipefd[1], F_SETFL, O_NONBLOCK);
|
1422
1480
|
|
1423
1481
|
if (prefix != Qnil) {
|
1424
|
-
|
1482
|
+
err = splice_chunks_write(backend, dest_fptr->fd, prefix, &watcher, &result);
|
1425
1483
|
if (err == -1) goto error; else if (err) goto syscallerror;
|
1426
1484
|
}
|
1427
1485
|
while (1) {
|
1428
|
-
int chunk_len;
|
1429
|
-
|
1430
|
-
|
1431
|
-
backend->base.op_count++;
|
1432
|
-
chunk_len = splice(src_fptr->fd, 0, pipefd[1], 0, maxlen, 0);
|
1433
|
-
if (chunk_len < 0) {
|
1434
|
-
err = errno;
|
1435
|
-
if (err != EWOULDBLOCK && err != EAGAIN) goto syscallerror;
|
1436
|
-
|
1437
|
-
result = libev_wait_rw_fd_with_watcher(backend, src_fptr->fd, pipefd[1], &watcher);
|
1438
|
-
if (TEST_EXCEPTION(result)) goto error;
|
1439
|
-
}
|
1440
|
-
else {
|
1441
|
-
break;
|
1442
|
-
}
|
1443
|
-
}
|
1486
|
+
int chunk_len = 0;
|
1487
|
+
err = splice_chunks_splice(backend, src_fptr->fd, pipefd[1], maxlen, &watcher, &result, &chunk_len);
|
1488
|
+
if (err == -1) goto error; else if (err) goto syscallerror;
|
1444
1489
|
if (chunk_len == 0) break;
|
1445
1490
|
|
1446
1491
|
total += chunk_len;
|
@@ -1453,20 +1498,12 @@ VALUE Backend_splice_chunks(VALUE self, VALUE src, VALUE dest, VALUE prefix, VAL
|
|
1453
1498
|
}
|
1454
1499
|
|
1455
1500
|
int left = chunk_len;
|
1456
|
-
while (
|
1457
|
-
|
1458
|
-
|
1459
|
-
if (
|
1460
|
-
err = errno;
|
1461
|
-
if (err != EWOULDBLOCK && err != EAGAIN) goto syscallerror;
|
1501
|
+
while (left > 0) {
|
1502
|
+
int len;
|
1503
|
+
err = splice_chunks_splice(backend, pipefd[0], dest_fptr->fd, left, &watcher, &result, &len);
|
1504
|
+
if (err == -1) goto error; else if (err) goto syscallerror;
|
1462
1505
|
|
1463
|
-
|
1464
|
-
if (TEST_EXCEPTION(result)) goto error;
|
1465
|
-
}
|
1466
|
-
else {
|
1467
|
-
left -= n;
|
1468
|
-
if (left == 0) break;
|
1469
|
-
}
|
1506
|
+
left -= len;
|
1470
1507
|
}
|
1471
1508
|
|
1472
1509
|
if (chunk_postfix != Qnil) {
|
@@ -1550,13 +1587,8 @@ void Init_Backend() {
|
|
1550
1587
|
rb_define_method(cBackend, "sendv", Backend_sendv, 3);
|
1551
1588
|
rb_define_method(cBackend, "sleep", Backend_sleep, 1);
|
1552
1589
|
|
1553
|
-
#ifdef POLYPHONY_LINUX
|
1554
1590
|
rb_define_method(cBackend, "splice", Backend_splice, 3);
|
1555
1591
|
rb_define_method(cBackend, "splice_to_eof", Backend_splice_to_eof, 3);
|
1556
|
-
#else
|
1557
|
-
rb_define_method(cBackend, "splice", Backend_fake_splice, 3);
|
1558
|
-
rb_define_method(cBackend, "splice_to_eof", Backend_fake_splice_to_eof, 3);
|
1559
|
-
#endif
|
1560
1592
|
|
1561
1593
|
rb_define_method(cBackend, "timeout", Backend_timeout, -1);
|
1562
1594
|
rb_define_method(cBackend, "timer_loop", Backend_timer_loop, 1);
|
data/ext/polyphony/queue.c
CHANGED
@@ -78,7 +78,7 @@ inline void queue_schedule_blocked_fibers_to_capacity(Queue_t *queue) {
|
|
78
78
|
}
|
79
79
|
}
|
80
80
|
|
81
|
-
inline void capped_queue_block_push(Queue_t *queue) {
|
81
|
+
static inline void capped_queue_block_push(Queue_t *queue) {
|
82
82
|
VALUE fiber = rb_fiber_current();
|
83
83
|
VALUE backend = rb_ivar_get(rb_thread_current(), ID_ivar_backend);
|
84
84
|
VALUE switchpoint_result;
|
data/ext/polyphony/runqueue.c
CHANGED
@@ -44,15 +44,15 @@ inline void runqueue_clear(runqueue_t *runqueue) {
|
|
44
44
|
runqueue_ring_buffer_clear(&runqueue->entries);
|
45
45
|
}
|
46
46
|
|
47
|
-
inline
|
47
|
+
inline unsigned int runqueue_size(runqueue_t *runqueue) {
|
48
48
|
return runqueue->entries.size;
|
49
49
|
}
|
50
50
|
|
51
|
-
inline
|
51
|
+
inline unsigned int runqueue_len(runqueue_t *runqueue) {
|
52
52
|
return runqueue->entries.count;
|
53
53
|
}
|
54
54
|
|
55
|
-
inline
|
55
|
+
inline unsigned int runqueue_max_len(runqueue_t *runqueue) {
|
56
56
|
unsigned int max_len = runqueue->high_watermark;
|
57
57
|
runqueue->high_watermark = 0;
|
58
58
|
return max_len;
|
data/ext/polyphony/runqueue.h
CHANGED
@@ -19,9 +19,9 @@ runqueue_entry runqueue_shift(runqueue_t *runqueue);
|
|
19
19
|
void runqueue_delete(runqueue_t *runqueue, VALUE fiber);
|
20
20
|
int runqueue_index_of(runqueue_t *runqueue, VALUE fiber);
|
21
21
|
void runqueue_clear(runqueue_t *runqueue);
|
22
|
-
|
23
|
-
|
24
|
-
|
22
|
+
unsigned int runqueue_size(runqueue_t *runqueue);
|
23
|
+
unsigned int runqueue_len(runqueue_t *runqueue);
|
24
|
+
unsigned int runqueue_max_len(runqueue_t *runqueue);
|
25
25
|
int runqueue_empty_p(runqueue_t *runqueue);
|
26
26
|
|
27
27
|
#endif /* RUNQUEUE_H */
|
data/lib/polyphony/version.rb
CHANGED
data/test/helper.rb
CHANGED
data/test/test_backend.rb
CHANGED
@@ -26,7 +26,7 @@ class BackendTest < MiniTest::Test
|
|
26
26
|
@backend.sleep 0.01
|
27
27
|
count += 1
|
28
28
|
}.await
|
29
|
-
assert_in_range 0.02..0.
|
29
|
+
assert_in_range 0.02..0.06, Time.now - t0 if IS_LINUX
|
30
30
|
assert_equal 3, count
|
31
31
|
end
|
32
32
|
|
@@ -243,6 +243,8 @@ class BackendTest < MiniTest::Test
|
|
243
243
|
end
|
244
244
|
|
245
245
|
def test_timer_loop
|
246
|
+
skip unless IS_LINUX
|
247
|
+
|
246
248
|
i = 0
|
247
249
|
f = spin do
|
248
250
|
@backend.timer_loop(0.01) { i += 1 }
|
@@ -257,6 +259,8 @@ class BackendTest < MiniTest::Test
|
|
257
259
|
end
|
258
260
|
|
259
261
|
def test_timeout
|
262
|
+
skip unless IS_LINUX
|
263
|
+
|
260
264
|
buffer = []
|
261
265
|
assert_raises(Polyphony::TimeoutException) do
|
262
266
|
@backend.timeout(0.01, Polyphony::TimeoutException) do
|
@@ -288,6 +292,8 @@ class BackendTest < MiniTest::Test
|
|
288
292
|
end
|
289
293
|
|
290
294
|
def test_nested_timeout
|
295
|
+
skip unless IS_LINUX
|
296
|
+
|
291
297
|
buffer = []
|
292
298
|
assert_raises(MyTimeoutException) do
|
293
299
|
@backend.timeout(0.01, MyTimeoutException) do
|
@@ -347,8 +353,8 @@ class BackendTest < MiniTest::Test
|
|
347
353
|
|
348
354
|
|
349
355
|
def test_splice_chunks
|
350
|
-
body = 'abcd' *
|
351
|
-
chunk_size =
|
356
|
+
body = 'abcd' * 4
|
357
|
+
chunk_size = 12
|
352
358
|
|
353
359
|
buf = +''
|
354
360
|
r, w = IO.pipe
|
@@ -373,7 +379,7 @@ class BackendTest < MiniTest::Test
|
|
373
379
|
w.close
|
374
380
|
reader.await
|
375
381
|
|
376
|
-
expected = "Content-Type: foo\r\n\r\n#{
|
382
|
+
expected = "Content-Type: foo\r\n\r\n#{12.to_s(16)}\r\n#{body[0..11]}\r\n#{4.to_s(16)}\r\n#{body[12..15]}\r\n0\r\n\r\n"
|
377
383
|
assert_equal expected, buf
|
378
384
|
ensure
|
379
385
|
o.close
|
@@ -394,6 +400,9 @@ class BackendTest < MiniTest::Test
|
|
394
400
|
assert_equal count, GC.count
|
395
401
|
sleep 0.05
|
396
402
|
assert_equal count, GC.count
|
403
|
+
|
404
|
+
return unless IS_LINUX
|
405
|
+
|
397
406
|
# The idle tasks are ran at most once per fiber switch, before the backend
|
398
407
|
# is polled. Therefore, the second sleep will not have triggered a GC, since
|
399
408
|
# only 0.05s have passed since the gc period was set.
|
data/test/test_global_api.rb
CHANGED
@@ -137,7 +137,7 @@ class MoveOnAfterTest < MiniTest::Test
|
|
137
137
|
t1 = Time.now
|
138
138
|
|
139
139
|
assert_nil v
|
140
|
-
assert_in_range 0.014..0.02, t1 - t0
|
140
|
+
assert_in_range 0.014..0.02, t1 - t0 if IS_LINUX
|
141
141
|
end
|
142
142
|
|
143
143
|
def test_move_on_after_without_block
|
@@ -152,6 +152,8 @@ class MoveOnAfterTest < MiniTest::Test
|
|
152
152
|
end
|
153
153
|
|
154
154
|
def test_nested_move_on_after
|
155
|
+
skip unless IS_LINUX
|
156
|
+
|
155
157
|
t0 = Time.now
|
156
158
|
o = move_on_after(0.01, with_value: 1) do
|
157
159
|
move_on_after(0.02, with_value: 2) do
|
@@ -210,7 +212,7 @@ class CancelAfterTest < MiniTest::Test
|
|
210
212
|
sleep 0.007
|
211
213
|
end
|
212
214
|
t1 = Time.now
|
213
|
-
assert_in_range 0.014..0.024, t1 - t0
|
215
|
+
assert_in_range 0.014..0.024, t1 - t0 if IS_LINUX
|
214
216
|
end
|
215
217
|
|
216
218
|
class CustomException < Exception
|
@@ -399,7 +401,7 @@ class ThrottledLoopTest < MiniTest::Test
|
|
399
401
|
end
|
400
402
|
f.await
|
401
403
|
t1 = Time.now
|
402
|
-
assert_in_range 0.075..0.15, t1 - t0
|
404
|
+
assert_in_range 0.075..0.15, t1 - t0 if IS_LINUX
|
403
405
|
assert_equal [1, 2, 3, 4, 5], buffer
|
404
406
|
end
|
405
407
|
end
|
@@ -415,6 +417,8 @@ class GlobalAPIEtcTest < MiniTest::Test
|
|
415
417
|
end
|
416
418
|
|
417
419
|
def test_every
|
420
|
+
skip unless IS_LINUX
|
421
|
+
|
418
422
|
buffer = []
|
419
423
|
t0 = Time.now
|
420
424
|
f = spin do
|
@@ -429,7 +433,7 @@ class GlobalAPIEtcTest < MiniTest::Test
|
|
429
433
|
t0 = Time.now
|
430
434
|
sleep 0.1
|
431
435
|
elapsed = Time.now - t0
|
432
|
-
assert (0.05..0.15).include? elapsed
|
436
|
+
assert (0.05..0.15).include? elapsed if IS_LINUX
|
433
437
|
|
434
438
|
f = spin { sleep }
|
435
439
|
snooze
|
data/test/test_io.rb
CHANGED
data/test/test_socket.rb
CHANGED
@@ -158,19 +158,22 @@ class SocketTest < MiniTest::Test
|
|
158
158
|
end
|
159
159
|
end
|
160
160
|
|
161
|
-
|
162
|
-
|
161
|
+
if IS_LINUX
|
162
|
+
class HTTPClientTest < MiniTest::Test
|
163
163
|
|
164
|
-
|
165
|
-
res = HTTParty.get('http://ipinfo.io/')
|
164
|
+
require 'json'
|
166
165
|
|
167
|
-
|
168
|
-
|
169
|
-
|
166
|
+
def test_http
|
167
|
+
res = HTTParty.get('http://ipinfo.io/')
|
168
|
+
|
169
|
+
response = JSON.load(res.body)
|
170
|
+
assert_equal 'https://ipinfo.io/missingauth', response['readme']
|
171
|
+
end
|
170
172
|
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
173
|
+
def test_https
|
174
|
+
res = HTTParty.get('https://ipinfo.io/')
|
175
|
+
response = JSON.load(res.body)
|
176
|
+
assert_equal 'https://ipinfo.io/missingauth', response['readme']
|
177
|
+
end
|
175
178
|
end
|
176
179
|
end
|
data/test/test_thread.rb
CHANGED
@@ -180,6 +180,9 @@ class ThreadTest < MiniTest::Test
|
|
180
180
|
assert_equal count, GC.count
|
181
181
|
sleep 0.05
|
182
182
|
assert_equal count, GC.count
|
183
|
+
|
184
|
+
return unless IS_LINUX
|
185
|
+
|
183
186
|
# The idle tasks are ran at most once per fiber switch, before the backend
|
184
187
|
# is polled. Therefore, the second sleep will not have triggered a GC, since
|
185
188
|
# only 0.05s have passed since the gc period was set.
|
data/test/test_timer.rb
CHANGED
@@ -19,7 +19,7 @@ class TimerMoveOnAfterTest < MiniTest::Test
|
|
19
19
|
end
|
20
20
|
t1 = Time.now
|
21
21
|
|
22
|
-
assert_in_range 0.1..0.15, t1 - t0
|
22
|
+
assert_in_range 0.1..0.15, t1 - t0 if IS_LINUX
|
23
23
|
assert_nil v
|
24
24
|
end
|
25
25
|
|
@@ -31,11 +31,13 @@ class TimerMoveOnAfterTest < MiniTest::Test
|
|
31
31
|
end
|
32
32
|
t1 = Time.now
|
33
33
|
|
34
|
-
assert_in_range 0.01..0.05, t1 - t0
|
34
|
+
assert_in_range 0.01..0.05, t1 - t0 if IS_LINUX
|
35
35
|
assert_equal :bar, v
|
36
36
|
end
|
37
37
|
|
38
38
|
def test_timer_move_on_after_with_reset
|
39
|
+
skip unless IS_LINUX
|
40
|
+
|
39
41
|
t0 = Time.now
|
40
42
|
v = @timer.move_on_after(0.01, with_value: :moved_on) do
|
41
43
|
sleep 0.007
|
@@ -71,7 +73,7 @@ class TimerCancelAfterTest < MiniTest::Test
|
|
71
73
|
end
|
72
74
|
end
|
73
75
|
t1 = Time.now
|
74
|
-
assert_in_range 0.01..0.03, t1 - t0
|
76
|
+
assert_in_range 0.01..0.03, t1 - t0 if IS_LINUX
|
75
77
|
end
|
76
78
|
|
77
79
|
def test_timer_cancel_after_with_reset
|
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.66'
|
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-
|
11
|
+
date: 2021-08-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake-compiler
|