polyphony 0.62 → 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2cea172818871812ebbafcbee0a4b98d130a351f904d7732a02400ef363eac62
4
- data.tar.gz: f4ef85515436a463d3732f04bbef457426f267fac01f5c5c28619e42334cd745
3
+ metadata.gz: b8e2aa3d011286dda93222164cbf66f1ec4c18b9c212ee7a70dbebd4bc523943
4
+ data.tar.gz: 99b5fb95c682dfad8df33f07d98bfab7f2109b9d4ec0e24720869c2d331f23fb
5
5
  SHA512:
6
- metadata.gz: 489b813f1bb2d7d97f87a60024b0665761571c4227e4fab6c733dcdb62b6ee98760473202c9154eb39a4b544663f759e4da6e70603d483e9f2ec2a64363cd4e1
7
- data.tar.gz: 05b232d7d67120e0983e60855c928beac6e91271fafc87586da7cc7543128fe326be04d126154ac7e0b0c0b249bf41ca90ab902f79f4a5fbf00cad498061f7e7
6
+ metadata.gz: 27c65b6e340d1aad41d38b4b322b98d282f96fe6d136c897ab8de14651b2da3cfc392650600d63983f76b5fb1dc70e4e49116efb289a790a509bec63caa9b9e4
7
+ data.tar.gz: 94e62ac7fbcb6d085af7475d987eca45be191aaf7654c3bacb07c4f7793d450281d3da1b08b12636a49398e97c1043c3a2cd628018925bdc082e02a0859743bc
@@ -7,7 +7,7 @@ jobs:
7
7
  strategy:
8
8
  fail-fast: false
9
9
  matrix:
10
- os: [ubuntu-latest]
10
+ os: [ubuntu-latest, ubuntu-18.04, macos-10.15]
11
11
  ruby: [2.6, 2.7, 3.0]
12
12
 
13
13
  name: >-
data/CHANGELOG.md CHANGED
@@ -1,3 +1,21 @@
1
+ ## 0.66 2021-08-01
2
+
3
+ - Fix all splicing APIs on non-linux OSes (#63)
4
+ - Add GC marking of buffers when cancelling read/write ops in io_uring backend
5
+
6
+ ## 0.65 2021-07-29
7
+
8
+ - Add `#__polyphony_read_method__` method for read method detection
9
+
10
+ ## 0.64 2021-07-26
11
+
12
+ - Add optional raise_on_eof argument to `#readpartial`
13
+
14
+ ## 0.63 2021-07-26
15
+
16
+ - Add support for specifying buf and buf_pos in `IO#read`
17
+ - Fix `Socket#read` to work and conform to `IO#read` interface
18
+
1
19
  ## 0.62 2021-07-21
2
20
 
3
21
  - Add `runqueue_size` to backend stats
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- polyphony (0.62)
4
+ polyphony (0.66)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
data/Rakefile CHANGED
@@ -9,8 +9,8 @@ Rake::ExtensionTask.new("polyphony_ext") do |ext|
9
9
  end
10
10
 
11
11
  task :recompile => [:clean, :compile]
12
-
13
12
  task :default => [:compile, :test]
13
+
14
14
  task :test do
15
15
  exec 'ruby test/run.rb'
16
16
  end
@@ -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
- RAISE_IF_EXCEPTION(resume_value);
353
- if (!completed) return resume_value;
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
- RAISE_IF_EXCEPTION(resume_value);
414
- if (!completed) return resume_value;
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
- RAISE_IF_EXCEPTION(resume_value);
461
- if (!completed) return resume_value;
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
- RAISE_IF_EXCEPTION(resume_value);
504
- if (!completed) return resume_value;
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
- // TODO: raise ArgumentError
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
- RAISE_IF_EXCEPTION(resume_value);
632
- if (!completed) return resume_value;
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
- RAISE_IF_EXCEPTION(resume_value);
679
- if (!completed) return resume_value;
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
- RAISE_IF_EXCEPTION(resume_value);
725
- if (!completed) return resume_value;
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
- RAISE_IF_EXCEPTION(resume_value);
768
- if (!completed) return resume_value;
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
- inline unsigned int OP_CONTEXT_RELEASE(op_context_store_t *store, op_context_t *ctx) {
48
- int completed = !ctx->ref_count;
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
- #endif
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 Backend_fake_splice_to_eof(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
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
- int err = splice_chunks_write(backend, dest_fptr->fd, prefix, &watcher, &result);
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
- // splice to pipe
1430
- while (1) {
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 (1) {
1457
- backend->base.op_count++;
1458
- int n = splice(pipefd[0], 0, dest_fptr->fd, 0, left, 0);
1459
- if (n < 0) {
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
- result = libev_wait_rw_fd_with_watcher(backend, pipefd[0], dest_fptr->fd, &watcher);
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);
@@ -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;
@@ -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 long runqueue_size(runqueue_t *runqueue) {
47
+ inline unsigned int runqueue_size(runqueue_t *runqueue) {
48
48
  return runqueue->entries.size;
49
49
  }
50
50
 
51
- inline long runqueue_len(runqueue_t *runqueue) {
51
+ inline unsigned int runqueue_len(runqueue_t *runqueue) {
52
52
  return runqueue->entries.count;
53
53
  }
54
54
 
55
- inline long runqueue_max_len(runqueue_t *runqueue) {
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;
@@ -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
- long runqueue_size(runqueue_t *runqueue);
23
- long runqueue_len(runqueue_t *runqueue);
24
- long runqueue_max_len(runqueue_t *runqueue);
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 */
@@ -76,6 +76,10 @@ end
76
76
 
77
77
  # IO instance method patches
78
78
  class ::IO
79
+ def __polyphony_read_method__
80
+ :backend_read
81
+ end
82
+
79
83
  # def each(sep = $/, limit = nil, chomp: nil)
80
84
  # sep, limit = $/, sep if sep.is_a?(Integer)
81
85
  # end
@@ -108,7 +112,11 @@ class ::IO
108
112
  end
109
113
 
110
114
  alias_method :orig_read, :read
111
- def read(len = nil)
115
+ def read(len = nil, buf = nil, buf_pos = 0)
116
+ if buf
117
+ return Polyphony.backend_read(self, buf, len, true, buf_pos)
118
+ end
119
+
112
120
  @read_buffer ||= +''
113
121
  result = Polyphony.backend_read(self, @read_buffer, len, true, -1)
114
122
  return nil unless result
@@ -119,9 +127,9 @@ class ::IO
119
127
  end
120
128
 
121
129
  alias_method :orig_readpartial, :read
122
- def readpartial(len, str = +'', buffer_pos = 0)
130
+ def readpartial(len, str = +'', buffer_pos = 0, raise_on_eof = true)
123
131
  result = Polyphony.backend_read(self, str, len, false, buffer_pos)
124
- raise EOFError unless result
132
+ raise EOFError if !result && raise_on_eof
125
133
 
126
134
  result
127
135
  end
@@ -5,6 +5,10 @@ require_relative './socket'
5
5
 
6
6
  # OpenSSL socket helper methods (to make it compatible with Socket API) and overrides
7
7
  class ::OpenSSL::SSL::SSLSocket
8
+ def __polyphony_read_method__
9
+ :readpartial
10
+ end
11
+
8
12
  alias_method :orig_initialize, :initialize
9
13
  def initialize(socket, context = nil)
10
14
  socket = socket.respond_to?(:io) ? socket.io || socket : socket
@@ -64,7 +68,21 @@ class ::OpenSSL::SSL::SSLSocket
64
68
  # @sync = osync
65
69
  end
66
70
 
67
- def readpartial(maxlen, buf = +'', buffer_pos = 0)
71
+ alias_method :orig_read, :read
72
+ def read(maxlen = nil, buf = nil, buf_pos = 0)
73
+ return readpartial(maxlen, buf, buf_pos) if buf
74
+
75
+ buf = +''
76
+ return readpartial(maxlen, buf) if maxlen
77
+
78
+ while true
79
+ readpartial(4096, buf, -1)
80
+ end
81
+ rescue EOFError
82
+ buf
83
+ end
84
+
85
+ def readpartial(maxlen, buf = +'', buffer_pos = 0, raise_on_eof = true)
68
86
  if buffer_pos != 0
69
87
  if (result = sysread(maxlen, +''))
70
88
  if buffer_pos == -1
@@ -76,7 +94,9 @@ class ::OpenSSL::SSL::SSLSocket
76
94
  else
77
95
  result = sysread(maxlen, buf)
78
96
  end
79
- result || (raise EOFError)
97
+
98
+ raise EOFError if !result && raise_on_eof
99
+ result
80
100
  end
81
101
 
82
102
  def read_loop(maxlen = 8192)
@@ -5,6 +5,12 @@ require 'socket'
5
5
  require_relative './io'
6
6
  require_relative '../core/thread_pool'
7
7
 
8
+ class BasicSocket
9
+ def __polyphony_read_method__
10
+ :backend_recv
11
+ end
12
+ end
13
+
8
14
  # Socket overrides (eventually rewritten in C)
9
15
  class ::Socket
10
16
  def accept
@@ -22,6 +28,23 @@ class ::Socket
22
28
  Polyphony.backend_connect(self, addr.ip_address, addr.ip_port)
23
29
  end
24
30
 
31
+ alias_method :orig_read, :read
32
+ def read(maxlen = nil, buf = nil, buf_pos = 0)
33
+ return Polyphony.backend_recv(self, buf, maxlen, buf_pos) if buf
34
+ return Polyphony.backend_recv(self, buf || +'', maxlen, 0) if maxlen
35
+
36
+ buf = +''
37
+ len = buf.bytesize
38
+ while true
39
+ Polyphony.backend_recv(self, buf, maxlen || 4096, -1)
40
+ new_len = buf.bytesize
41
+ break if new_len == len
42
+
43
+ len = new_len
44
+ end
45
+ buf
46
+ end
47
+
25
48
  def recv(maxlen, flags = 0, outbuf = nil)
26
49
  Polyphony.backend_recv(self, outbuf || +'', maxlen, 0)
27
50
  end
@@ -60,8 +83,9 @@ class ::Socket
60
83
  # Polyphony.backend_send(self, mesg, 0)
61
84
  # end
62
85
 
63
- def readpartial(maxlen, str = +'', buffer_pos = 0)
64
- Polyphony.backend_recv(self, str, maxlen, buffer_pos)
86
+ def readpartial(maxlen, str = +'', buffer_pos = 0, raise_on_eof = true)
87
+ result = Polyphony.backend_recv(self, str, maxlen, buffer_pos)
88
+ raise EOFError if !result && raise_on_eof
65
89
  end
66
90
 
67
91
  ZERO_LINGER = [0, 0].pack('ii').freeze
@@ -140,6 +164,23 @@ class ::TCPSocket
140
164
  setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_REUSEPORT, 1)
141
165
  end
142
166
 
167
+ alias_method :orig_read, :read
168
+ def read(maxlen = nil, buf = nil, buf_pos = 0)
169
+ return Polyphony.backend_recv(self, buf, maxlen, buf_pos) if buf
170
+ return Polyphony.backend_recv(self, buf || +'', maxlen, 0) if maxlen
171
+
172
+ buf = +''
173
+ len = buf.bytesize
174
+ while true
175
+ Polyphony.backend_recv(self, buf, maxlen || 4096, -1)
176
+ new_len = buf.bytesize
177
+ break if new_len == len
178
+
179
+ len = new_len
180
+ end
181
+ buf
182
+ end
183
+
143
184
  def recv(maxlen, flags = 0, outbuf = nil)
144
185
  Polyphony.backend_recv(self, outbuf || +'', maxlen, 0)
145
186
  end
@@ -165,11 +206,10 @@ class ::TCPSocket
165
206
  # Polyphony.backend_send(self, mesg, 0)
166
207
  # end
167
208
 
168
- def readpartial(maxlen, str = +'', buffer_pos = 0)
209
+ def readpartial(maxlen, str = +'', buffer_pos = 0, raise_on_eof)
169
210
  result = Polyphony.backend_recv(self, str, maxlen, buffer_pos)
170
- raise EOFError unless result
171
-
172
- str
211
+ raise EOFError if !result && raise_on_eof
212
+ result
173
213
  end
174
214
 
175
215
  def read_nonblock(len, str = nil, exception: true)
@@ -217,6 +257,23 @@ class ::UNIXServer
217
257
  end
218
258
 
219
259
  class ::UNIXSocket
260
+ alias_method :orig_read, :read
261
+ def read(maxlen = nil, buf = nil, buf_pos = 0)
262
+ return Polyphony.backend_recv(self, buf, maxlen, buf_pos) if buf
263
+ return Polyphony.backend_recv(self, buf || +'', maxlen, 0) if maxlen
264
+
265
+ buf = +''
266
+ len = buf.bytesize
267
+ while true
268
+ Polyphony.backend_recv(self, buf, maxlen || 4096, -1)
269
+ new_len = buf.bytesize
270
+ break if new_len == len
271
+
272
+ len = new_len
273
+ end
274
+ buf
275
+ end
276
+
220
277
  def recv(maxlen, flags = 0, outbuf = nil)
221
278
  Polyphony.backend_recv(self, outbuf || +'', maxlen, 0)
222
279
  end
@@ -242,11 +299,10 @@ class ::UNIXSocket
242
299
  Polyphony.backend_send(self, mesg, 0)
243
300
  end
244
301
 
245
- def readpartial(maxlen, str = +'', buffer_pos = 0)
302
+ def readpartial(maxlen, str = +'', buffer_pos = 0, raise_on_eof)
246
303
  result = Polyphony.backend_recv(self, str, maxlen, buffer_pos)
247
- raise EOFError unless result
248
-
249
- str
304
+ raise EOFError if !result && raise_on_eof
305
+ result
250
306
  end
251
307
 
252
308
  def read_nonblock(len, str = nil, exception: true)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Polyphony
4
- VERSION = '0.62'
4
+ VERSION = '0.66'
5
5
  end
data/test/helper.rb CHANGED
@@ -15,6 +15,8 @@ require 'minitest/reporters'
15
15
 
16
16
  ::Exception.__disable_sanitized_backtrace__ = true
17
17
 
18
+ IS_LINUX = RUBY_PLATFORM =~ /linux/
19
+
18
20
  # Minitest::Reporters.use! [
19
21
  # Minitest::Reporters::SpecReporter.new
20
22
  # ]
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.04, Time.now - t0
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
 
@@ -98,6 +98,32 @@ class BackendTest < MiniTest::Test
98
98
  assert_equal return_value, buf
99
99
  end
100
100
 
101
+ def test_read_concat_big
102
+ i, o = IO.pipe
103
+
104
+ body = " " * 4000
105
+
106
+ data = "post /?q=time&blah=blah HTTP/1\r\nHost: dev.realiteq.net\r\n\r\n" +
107
+ "get /?q=time HTTP/1.1\r\nContent-Length: #{body.bytesize}\r\n\r\n#{body}" +
108
+ "get /?q=time HTTP/1.1\r\nCookie: foo\r\nCookie: bar\r\n\r\n"
109
+
110
+ o << data
111
+ o.close
112
+
113
+ buf = +''
114
+
115
+ @backend.read(i, buf, 4096, false, -1)
116
+ assert_equal 4096, buf.bytesize
117
+
118
+ @backend.read(i, buf, 1, false, -1)
119
+ assert_equal 4097, buf.bytesize
120
+
121
+ @backend.read(i, buf, 4096, false, -1)
122
+
123
+ assert_equal data.bytesize, buf.bytesize
124
+ assert_equal data, buf
125
+ end
126
+
101
127
  def test_waitpid
102
128
  pid = fork do
103
129
  @backend.post_fork
@@ -217,6 +243,8 @@ class BackendTest < MiniTest::Test
217
243
  end
218
244
 
219
245
  def test_timer_loop
246
+ skip unless IS_LINUX
247
+
220
248
  i = 0
221
249
  f = spin do
222
250
  @backend.timer_loop(0.01) { i += 1 }
@@ -231,6 +259,8 @@ class BackendTest < MiniTest::Test
231
259
  end
232
260
 
233
261
  def test_timeout
262
+ skip unless IS_LINUX
263
+
234
264
  buffer = []
235
265
  assert_raises(Polyphony::TimeoutException) do
236
266
  @backend.timeout(0.01, Polyphony::TimeoutException) do
@@ -262,6 +292,8 @@ class BackendTest < MiniTest::Test
262
292
  end
263
293
 
264
294
  def test_nested_timeout
295
+ skip unless IS_LINUX
296
+
265
297
  buffer = []
266
298
  assert_raises(MyTimeoutException) do
267
299
  @backend.timeout(0.01, MyTimeoutException) do
@@ -321,8 +353,8 @@ class BackendTest < MiniTest::Test
321
353
 
322
354
 
323
355
  def test_splice_chunks
324
- body = 'abcd' * 250
325
- chunk_size = 750
356
+ body = 'abcd' * 4
357
+ chunk_size = 12
326
358
 
327
359
  buf = +''
328
360
  r, w = IO.pipe
@@ -347,7 +379,7 @@ class BackendTest < MiniTest::Test
347
379
  w.close
348
380
  reader.await
349
381
 
350
- 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"
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"
351
383
  assert_equal expected, buf
352
384
  ensure
353
385
  o.close
@@ -368,6 +400,9 @@ class BackendTest < MiniTest::Test
368
400
  assert_equal count, GC.count
369
401
  sleep 0.05
370
402
  assert_equal count, GC.count
403
+
404
+ return unless IS_LINUX
405
+
371
406
  # The idle tasks are ran at most once per fiber switch, before the backend
372
407
  # is polled. Therefore, the second sleep will not have triggered a GC, since
373
408
  # only 0.05s have passed since the gc period was set.
@@ -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
@@ -73,6 +73,26 @@ class IOTest < MiniTest::Test
73
73
  assert_equal [:wait_readable, 'foo'], results
74
74
  end
75
75
 
76
+ def test_read
77
+ i, o = IO.pipe
78
+
79
+ o << 'hi'
80
+ assert_equal 'hi', i.read(2)
81
+
82
+ o << 'foobarbaz'
83
+ assert_equal 'foo', i.read(3)
84
+ assert_equal 'bar', i.read(3)
85
+
86
+ buf = +'abc'
87
+ assert_equal 'baz', i.read(3, buf)
88
+ assert_equal 'baz', buf
89
+
90
+ buf = +'def'
91
+ o << 'foobar'
92
+ assert_equal 'deffoobar', i.read(6, buf, -1)
93
+ assert_equal 'deffoobar', buf
94
+ end
95
+
76
96
  def test_readpartial
77
97
  i, o = IO.pipe
78
98
 
@@ -331,6 +351,8 @@ class IOClassMethodsTest < MiniTest::Test
331
351
  end
332
352
 
333
353
  def test_popen
354
+ skip unless IS_LINUX
355
+
334
356
  counter = 0
335
357
  timer = spin { throttled_loop(200) { counter += 1 } }
336
358
 
data/test/test_socket.rb CHANGED
@@ -12,7 +12,6 @@ class SocketTest < MiniTest::Test
12
12
  def test_tcp
13
13
  port = rand(1234..5678)
14
14
  server = TCPServer.new('127.0.0.1', port)
15
-
16
15
  server_fiber = spin do
17
16
  while (socket = server.accept)
18
17
  spin do
@@ -34,6 +33,45 @@ class SocketTest < MiniTest::Test
34
33
  server&.close
35
34
  end
36
35
 
36
+ def test_read
37
+ port = rand(1234..5678)
38
+ server = TCPServer.new('127.0.0.1', port)
39
+ server_fiber = spin do
40
+ while (socket = server.accept)
41
+ spin do
42
+ while (data = socket.read(8192))
43
+ socket << data
44
+ end
45
+ end
46
+ end
47
+ end
48
+
49
+ snooze
50
+ client = TCPSocket.new('127.0.0.1', port)
51
+
52
+ client << 'hi'
53
+ assert_equal 'hi', client.read(2)
54
+
55
+ client << 'foobarbaz'
56
+ assert_equal 'foo', client.read(3)
57
+ assert_equal 'bar', client.read(3)
58
+
59
+ buf = +'abc'
60
+ assert_equal 'baz', client.read(3, buf)
61
+ assert_equal 'baz', buf
62
+
63
+ buf = +'def'
64
+ client << 'foobar'
65
+ assert_equal 'deffoobar', client.read(6, buf, -1)
66
+ assert_equal 'deffoobar', buf
67
+
68
+ client.close
69
+ ensure
70
+ server_fiber&.stop
71
+ server_fiber&.await
72
+ server&.close
73
+ end
74
+
37
75
  # sending multiple strings at once
38
76
  def test_sendv
39
77
  port = rand(1234..5678)
@@ -120,19 +158,22 @@ class SocketTest < MiniTest::Test
120
158
  end
121
159
  end
122
160
 
123
- class HTTPClientTest < MiniTest::Test
124
- require 'json'
161
+ if IS_LINUX
162
+ class HTTPClientTest < MiniTest::Test
125
163
 
126
- def test_http
127
- res = HTTParty.get('http://ipinfo.io/')
164
+ require 'json'
128
165
 
129
- response = JSON.load(res.body)
130
- assert_equal 'https://ipinfo.io/missingauth', response['readme']
131
- end
166
+ def test_http
167
+ res = HTTParty.get('http://ipinfo.io/')
132
168
 
133
- def test_https
134
- res = HTTParty.get('https://ipinfo.io/')
135
- response = JSON.load(res.body)
136
- assert_equal 'https://ipinfo.io/missingauth', response['readme']
169
+ response = JSON.load(res.body)
170
+ assert_equal 'https://ipinfo.io/missingauth', response['readme']
171
+ end
172
+
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
137
178
  end
138
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.62'
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-07-21 00:00:00.000000000 Z
11
+ date: 2021-08-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake-compiler