polyphony 1.2.1 → 1.4

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: 5017e969ae57d3ea62041d65eb12879fd60e8a997ed52d9ff43514c934df6cbe
4
- data.tar.gz: 5e1ae4fee29f44b1396464346fca163f972ba164ac78437b76e2fb63add9a26a
3
+ metadata.gz: 91cce4703d1b930aa9b242373492a373abd6d49b5ce200f8828683538c15562b
4
+ data.tar.gz: c5c8af2ab5f4961b6379015b8d68f10c56dc5c466a44c0a0a4d6be4a93ab26b9
5
5
  SHA512:
6
- metadata.gz: 9153d20a009fa2b0a0c1f1e03a0609693db7edd9e7e2fd99309fcd90314de2bf9bde1d57c414b3a7c76288c7805ca1992efc1c786295d690fd9033634e21e46f
7
- data.tar.gz: f98fea4bbfaa2c993b51ea09e1b89a276552abb6ae3bdf5ed0557c8c62999bfd45770ad007f447d2427d6564f2d3dd35362291241a6751848dd0150a9c3a957e
6
+ metadata.gz: 5a9cb0b512efaae4b0454f3b5e99807657275a8d4c80dd8f527235c842c3131f4a38f4e4eefc174aeae1c2e7c6f879e9ccf8064e49b42dd69cb27d7780ff1780
7
+ data.tar.gz: 19977c5db508a5a5bdf1fa1f4b0db061436849c5d75f59c62764ae3bfd5426014e4e119e7306b68edf25c82cea9193f259c2e1403b1def825847614c003026dd
data/.rubocop.yml CHANGED
@@ -7,6 +7,7 @@ AllCops:
7
7
  - 'test/**/*.rb'
8
8
  - 'examples/**/*.rb'
9
9
  - 'Gemfile*'
10
+ - 'ext/**/*.rb'
10
11
  - lib/polyphony/adapters/irb.rb
11
12
 
12
13
  Style/LambdaCall:
data/.yardopts CHANGED
@@ -18,6 +18,7 @@
18
18
  ./ext/polyphony
19
19
  -
20
20
  docs/readme.md
21
+ docs/installation.md
21
22
  docs/overview.md
22
23
  docs/tutorial.md
23
24
  docs/cancellation.md
data/CHANGELOG.md CHANGED
@@ -1,3 +1,16 @@
1
+ ## 1.4 2023-07-01
2
+
3
+ - Implement concurrent `IO#close`
4
+ - Improve docs
5
+ - Use only positional arguments in `IO#read` and `IO#readpartial` (#109 @floriandejonckheere)
6
+
7
+ ## 1.3 2023-06-23
8
+
9
+ - Improve cancellation doc page
10
+ - Fix linking to liburing under certain conditions (#107)
11
+ - Fix reference to `Socket::ZERO_LINGER` (#106 @floriandejonckheere)
12
+ - Handle error when calling `pidfd_open`
13
+
1
14
  ## 1.2 2023-06-17
2
15
 
3
16
  - Require Ruby 3.1 or newer
data/README.md CHANGED
@@ -23,58 +23,24 @@
23
23
  ## What is Polyphony?
24
24
 
25
25
  Polyphony is a library for building concurrent applications in Ruby. Polyphony
26
- harnesses the power of [Ruby fibers](https://ruby-doc.org/core-2.5.1/Fiber.html)
27
- to provide a cooperative, sequential coroutine-based concurrency model. Under
28
- the hood, Polyphony uses
29
- [io_uring](https://unixism.net/loti/what_is_io_uring.html) or
26
+ harnesses the power of [Ruby fibers](https://rubyapi.org/3.2/o/fiber) to provide
27
+ a cooperative, sequential coroutine-based concurrency model. Under the hood,
28
+ Polyphony uses [io_uring](https://unixism.net/loti/what_is_io_uring.html) or
30
29
  [libev](https://github.com/enki/libev) to maximize I/O performance.
31
30
 
32
31
  ## Features
33
32
 
34
- * Co-operative scheduling of concurrent tasks using Ruby fibers.
35
- * High-performance event reactor for handling I/O events and timers.
36
- * Natural, sequential programming style that makes it easy to reason about
37
- concurrent code.
38
- * Abstractions and constructs for controlling the execution of concurrent code:
39
- supervisors, cancel scopes, throttling, resource pools etc.
40
- * Code can use native networking classes and libraries, growing support for
41
- third-party gems such as `pg` and `redis`.
42
- * Use stdlib classes such as `TCPServer`, `TCPSocket` and
43
- `OpenSSL::SSL::SSLSocket`.
44
- * Competitive performance and scalability characteristics, in terms of both
45
- throughput and memory consumption.
46
-
47
- ## Installing
48
-
49
- ### System Requirements
50
-
51
- In order to use Polyphony you need to have:
52
-
53
- - Linux or MacOS (support for Windows will come at a later stage)
54
- - Ruby (MRI) 3.1 or newer
55
-
56
- ### Installing the Polyphony Gem
57
-
58
- Add this line to your application's Gemfile:
59
-
60
- ```ruby
61
- gem 'polyphony'
62
- ```
63
-
64
- And then execute:
65
-
66
- ```bash
67
- $ bundle
68
- ```
69
-
70
- Or install it yourself as:
71
-
72
- ```bash
73
- $ gem install polyphony
74
- ```
33
+ * Ruby fibers as the main unit of concurrency.
34
+ * [Structured concurrency](https://en.wikipedia.org/wiki/Structured_concurrency)
35
+ coupled with robust exception handling.
36
+ * Message passing between fibers, even across threads!
37
+ * High-performance I/O using the core Ruby I/O classes and
38
+ [io_uring](https://unixism.net/loti/what_is_io_uring.html) with support for
39
+ [advanced I/O patterns](docs/advanced-io.md).
75
40
 
76
41
  ## Usage
77
42
 
43
+ - [Installation](docs/installation.md)
78
44
  - [Overview](docs/overview.md)
79
45
  - [Tutorial](docs/tutorial.md)
80
46
  - [All About Cancellation: How to Stop Concurrent Operations](docs/cancellation.md)
data/docs/cancellation.md CHANGED
@@ -101,10 +101,11 @@ with_timeout(5) { sleep 10; :bar } #=> MyTimeoutError raised!
101
101
  In the code above, we create a `with_timeout` method that takes a duration
102
102
  argument. It starts by spinning up a fiber that will sleep for the given
103
103
  duration, then raise a custom exception. It then runs the given block by calling
104
- `yield`. If the given block stops running before the timeout, it exists
105
- normally, not before making sure to stop the timeout fiber. If the given block
106
- runs longer than the timeout, the exception raised by the timeout fiber will be
107
- propagated to the fiber running the block, causing it to be stopped.
104
+ `yield`. If the given block returns before the timeout, its return value is
105
+ returned from the call to `with_timeout`, not before making sure to stop the
106
+ timeout fiber. If the given block runs longer than the timeout, the exception
107
+ raised by the timeout will interrupt the fiber running the block, and will
108
+ propagate to the call site.
108
109
 
109
110
  Now that we have an idea of how we can construct timeouts, let's look at the
110
111
  different timeout APIs included in Polyphony:
@@ -0,0 +1,30 @@
1
+ # @title Installation
2
+
3
+ # Installation
4
+
5
+ ## System Requirements
6
+
7
+ In order to use Polyphony you need to have:
8
+
9
+ - Linux or MacOS (support for Windows will come at a later stage)
10
+ - Ruby (MRI) 3.1 or newer
11
+
12
+ ## Installing the Polyphony Gem
13
+
14
+ Add this line to your application's Gemfile:
15
+
16
+ ```ruby
17
+ gem 'polyphony'
18
+ ```
19
+
20
+ And then execute:
21
+
22
+ ```bash
23
+ $ bundle
24
+ ```
25
+
26
+ Or install it yourself as:
27
+
28
+ ```bash
29
+ $ gem install polyphony
30
+ ```
data/docs/readme.md CHANGED
@@ -25,58 +25,24 @@
25
25
  ## What is Polyphony?
26
26
 
27
27
  Polyphony is a library for building concurrent applications in Ruby. Polyphony
28
- harnesses the power of [Ruby fibers](https://ruby-doc.org/core-2.5.1/Fiber.html)
29
- to provide a cooperative, sequential coroutine-based concurrency model. Under
30
- the hood, Polyphony uses
31
- [io_uring](https://unixism.net/loti/what_is_io_uring.html) or
28
+ harnesses the power of [Ruby fibers](https://rubyapi.org/3.2/o/fiber) to provide
29
+ a cooperative, sequential coroutine-based concurrency model. Under the hood,
30
+ Polyphony uses [io_uring](https://unixism.net/loti/what_is_io_uring.html) or
32
31
  [libev](https://github.com/enki/libev) to maximize I/O performance.
33
32
 
34
33
  ## Features
35
34
 
36
- * Co-operative scheduling of concurrent tasks using Ruby fibers.
37
- * High-performance event reactor for handling I/O events and timers.
38
- * Natural, sequential programming style that makes it easy to reason about
39
- concurrent code.
40
- * Abstractions and constructs for controlling the execution of concurrent code:
41
- supervisors, cancel scopes, throttling, resource pools etc.
42
- * Code can use native networking classes and libraries, growing support for
43
- third-party gems such as `pg` and `redis`.
44
- * Use stdlib classes such as `TCPServer`, `TCPSocket` and
45
- `OpenSSL::SSL::SSLSocket`.
46
- * Competitive performance and scalability characteristics, in terms of both
47
- throughput and memory consumption.
48
-
49
- ## Installing
50
-
51
- ### System Requirements
52
-
53
- In order to use Polyphony you need to have:
54
-
55
- - Linux or MacOS (support for Windows will come at a later stage)
56
- - Ruby (MRI) 3.1 or newer
57
-
58
- ### Installing the Polyphony Gem
59
-
60
- Add this line to your application's Gemfile:
61
-
62
- ```ruby
63
- gem 'polyphony'
64
- ```
65
-
66
- And then execute:
67
-
68
- ```bash
69
- $ bundle
70
- ```
71
-
72
- Or install it yourself as:
73
-
74
- ```bash
75
- $ gem install polyphony
76
- ```
35
+ * Ruby fibers as the main unit of concurrency.
36
+ * [Structured concurrency](https://en.wikipedia.org/wiki/Structured_concurrency)
37
+ coupled with robust exception handling.
38
+ * Message passing between fibers, even across threads!
39
+ * High-performance I/O using the core Ruby I/O classes and
40
+ [io_uring](https://unixism.net/loti/what_is_io_uring.html) with support for
41
+ {file:/docs/advanced-io.md advanced I/O patterns}.
77
42
 
78
43
  ## Usage
79
44
 
45
+ - {file:/docs/installation.md Installation}
80
46
  - {file:/docs/overview.md Overview}
81
47
  - {file:/docs/tutorial.md Tutorial}
82
48
  - {file:/docs/cancellation.md All About Cancellation: How to Stop Concurrent Operations}
@@ -0,0 +1,99 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+ require 'polyphony'
5
+
6
+ # Based on the design of Elixir's GenStage
7
+
8
+ class Producer
9
+ def initialize(mod, *a, **b)
10
+ extend(mod)
11
+ setup(*a, **b)
12
+ @_fiber = spin do
13
+ receive_loop do |msg|
14
+ case msg[:kind]
15
+ when :demand
16
+ items = handle_demand(msg[:limit])
17
+ msg[:peer] << items
18
+ end
19
+ end
20
+ end
21
+ end
22
+
23
+ def <<(msg)
24
+ @_fiber << msg
25
+ end
26
+ end
27
+
28
+ module Counter
29
+ def setup(counter = 0)
30
+ @counter = counter
31
+ end
32
+
33
+ def handle_demand(demand)
34
+ events = (@counter...@counter + demand).to_a
35
+ @counter += demand
36
+ events
37
+ end
38
+ end
39
+
40
+ counter = Producer.new(Counter, 0)
41
+
42
+ class Consumer
43
+ def initialize(mod, *a, **b)
44
+ extend(mod)
45
+ setup(*a, **b) if respond_to?(:setup)
46
+ @_fiber = spin do
47
+ while true
48
+ items = get_items
49
+ handle_items(items)
50
+ end
51
+ end
52
+
53
+ @max_demand = 10
54
+ @min_demand = 5
55
+ end
56
+
57
+ def subscribe(upstream)
58
+ @upstream = upstream
59
+ end
60
+
61
+ private
62
+
63
+ def get_items
64
+ send_demand(@max_demand) if !@sent_demand
65
+ items = receive
66
+ send_demand(@min_demand)
67
+ items
68
+ end
69
+
70
+ def send_demand(demand)
71
+ if @upstream
72
+ @upstream << { peer: Fiber.current, kind: :demand, limit: demand }
73
+ @sent_demand = true
74
+ else
75
+ sleep 0.1
76
+ end
77
+ end
78
+ end
79
+
80
+ module Printer
81
+ def handle_items(items)
82
+ sleep 1
83
+ puts "got: #{items.join(' ')}"
84
+ end
85
+ end
86
+
87
+ # counter << { peer: Fiber.current, kind: :demand, limit: 10 }
88
+ # r = receive
89
+
90
+ # p r: r
91
+
92
+ # counter << { peer: Fiber.current, kind: :demand, limit: 10 }
93
+ # r = receive
94
+ # p r: r
95
+
96
+ printer = Consumer.new(Printer)
97
+ printer.subscribe(counter)
98
+
99
+ sleep
@@ -458,7 +458,7 @@ VALUE Backend_stats(VALUE self) {
458
458
 
459
459
  VALUE Backend_verify_blocking_mode(VALUE self, VALUE io, VALUE blocking) {
460
460
  io_verify_blocking_mode(io, rb_io_descriptor(io), blocking);
461
- return self;
461
+ return io;
462
462
  }
463
463
 
464
464
  void backend_setup_stats_symbols(void) {
@@ -14,7 +14,7 @@
14
14
  #include <errno.h>
15
15
 
16
16
  #include "polyphony.h"
17
- #include "liburing.h"
17
+ #include <liburing.h>
18
18
  #include "backend_io_uring_context.h"
19
19
  #include "ruby/thread.h"
20
20
  #include "ruby/io.h"
@@ -141,8 +141,6 @@ typedef struct poll_context {
141
141
  int result;
142
142
  } poll_context_t;
143
143
 
144
- extern int __sys_io_uring_enter(int fd, unsigned to_submit, unsigned min_complete, unsigned flags, sigset_t *sig);
145
-
146
144
  void *io_uring_backend_poll_without_gvl(void *ptr) {
147
145
  poll_context_t *ctx = (poll_context_t *)ptr;
148
146
  ctx->result = io_uring_wait_cqe(ctx->ring, &ctx->cqe);
@@ -215,7 +213,7 @@ again:
215
213
  if (overflow_checked) goto done;
216
214
 
217
215
  if (cq_ring_needs_flush(ring)) {
218
- __sys_io_uring_enter(ring->ring_fd, 0, 0, IORING_ENTER_GETEVENTS, NULL);
216
+ io_uring_enter(ring->ring_fd, 0, 0, IORING_ENTER_GETEVENTS, NULL);
219
217
  overflow_checked = true;
220
218
  goto again;
221
219
  }
@@ -1365,34 +1363,33 @@ VALUE Backend_wait_io(VALUE self, VALUE io, VALUE write) {
1365
1363
  return self;
1366
1364
  }
1367
1365
 
1368
- // VALUE Backend_close(VALUE self, VALUE io) {
1369
- // Backend_t *backend;
1370
- // rb_io_t *fptr;
1371
- // VALUE resume_value = Qnil;
1372
- // op_context_t *ctx;
1373
- // struct io_uring_sqe *sqe;
1374
- // int result;
1375
- // int completed;
1376
-
1377
- // if (fd < 0) return Qnil;
1378
-
1379
- // io_unset_nonblock(io, fd);
1366
+ VALUE Backend_close(VALUE self, VALUE io) {
1367
+ Backend_t *backend;
1368
+ rb_io_t *fptr;
1369
+ VALUE resume_value = Qnil;
1370
+ op_context_t *ctx;
1371
+ struct io_uring_sqe *sqe;
1372
+ int result;
1373
+ int completed;
1374
+ int fd = fd_from_io(io, &fptr, 0, 0);
1375
+ if (fd < 0) return Qnil;
1380
1376
 
1381
- // ctx = context_store_acquire(&backend->store, OP_CLOSE);
1382
- // sqe = io_uring_backend_get_sqe(backend);
1383
- // io_uring_prep_close(sqe, fd);
1384
- // result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
1385
- // completed = context_store_release(&backend->store, ctx);
1386
- // RAISE_IF_EXCEPTION(resume_value);
1387
- // if (!completed) return resume_value;
1388
- // RB_GC_GUARD(resume_value);
1377
+ GetBackend(self, backend);
1378
+ ctx = context_store_acquire(&backend->store, OP_CLOSE);
1379
+ sqe = io_uring_backend_get_sqe(backend);
1380
+ io_uring_prep_close(sqe, fd);
1381
+ result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
1382
+ completed = context_store_release(&backend->store, ctx);
1383
+ RAISE_IF_EXCEPTION(resume_value);
1384
+ if (!completed) return resume_value;
1385
+ RB_GC_GUARD(resume_value);
1389
1386
 
1390
- // if (result < 0) rb_syserr_fail(-result, strerror(-result));
1387
+ if (result < 0) rb_syserr_fail(-result, strerror(-result));
1391
1388
 
1392
- // fptr_finalize(fptr);
1393
- // // fd = -1;
1394
- // return io;
1395
- // }
1389
+ fptr_finalize(fptr);
1390
+ // fd = -1;
1391
+ return io;
1392
+ }
1396
1393
 
1397
1394
  inline struct __kernel_timespec double_to_timespec(double duration) {
1398
1395
  double duration_integral;
@@ -1539,6 +1536,10 @@ VALUE Backend_waitpid(VALUE self, VALUE pid) {
1539
1536
  RAISE_IF_EXCEPTION(resume_value);
1540
1537
  RB_GC_GUARD(resume_value);
1541
1538
  }
1539
+ else {
1540
+ int e = errno;
1541
+ rb_syserr_fail(e, strerror(e));
1542
+ }
1542
1543
 
1543
1544
  ret = waitpid(pid_int, &status, WNOHANG);
1544
1545
  if (ret < 0) {
@@ -2013,7 +2014,7 @@ void Init_Backend(void) {
2013
2014
  rb_define_method(cBackend, "wait_io", Backend_wait_io, 2);
2014
2015
  rb_define_method(cBackend, "waitpid", Backend_waitpid, 1);
2015
2016
  rb_define_method(cBackend, "write", Backend_write_m, -1);
2016
- // rb_define_method(cBackend, "close", Backend_close, 1);
2017
+ rb_define_method(cBackend, "close", Backend_close, 1);
2017
2018
 
2018
2019
  SYM_io_uring = ID2SYM(rb_intern("io_uring"));
2019
2020
  SYM_send = ID2SYM(rb_intern("send"));
@@ -1135,6 +1135,31 @@ VALUE Backend_wait_io(VALUE self, VALUE io, VALUE write) {
1135
1135
  return libev_wait_fd(backend, fd, events, 1);
1136
1136
  }
1137
1137
 
1138
+ VALUE Backend_close(VALUE self, VALUE io) {
1139
+ Backend_t *backend;
1140
+ rb_io_t *fptr;
1141
+ VALUE resume_value = Qnil;
1142
+ int result;
1143
+ int fd = fd_from_io(io, &fptr, 0, 0);
1144
+ if (fd < 0) return Qnil;
1145
+
1146
+ GetBackend(self, backend);
1147
+
1148
+ result = close(fd);
1149
+ if (result == -1) {
1150
+ int err = errno;
1151
+ rb_syserr_fail(err, strerror(err));
1152
+ }
1153
+
1154
+ resume_value = backend_snooze(&backend->base);
1155
+ RAISE_IF_EXCEPTION(resume_value);
1156
+ RB_GC_GUARD(resume_value);
1157
+
1158
+ fptr_finalize(fptr);
1159
+ // fd = -1;
1160
+ return io;
1161
+ }
1162
+
1138
1163
  struct libev_timer {
1139
1164
  struct ev_timer timer;
1140
1165
  VALUE fiber;
@@ -1654,6 +1679,7 @@ void Init_Backend(void) {
1654
1679
  rb_define_method(cBackend, "wait_io", Backend_wait_io, 2);
1655
1680
  rb_define_method(cBackend, "waitpid", Backend_waitpid, 1);
1656
1681
  rb_define_method(cBackend, "write", Backend_write_m, -1);
1682
+ rb_define_method(cBackend, "close", Backend_close, 1);
1657
1683
 
1658
1684
  SYM_libev = ID2SYM(rb_intern("libev"));
1659
1685
 
@@ -44,11 +44,13 @@ if config[:io_uring]
44
44
  end
45
45
  end
46
46
 
47
- if !find_header 'liburing.h', File.expand_path('../../vendor/liburing/src/include', __dir__)
47
+ if !find_header 'liburing.h', File.join(liburing_path, 'src/include')
48
48
  raise "Couldn't find liburing.h"
49
49
  end
50
50
 
51
- $LDFLAGS << " -L#{File.expand_path('../../vendor/liburing/src', __dir__)} -l uring"
51
+ if !find_library('uring', nil, File.join(liburing_path, 'src'))
52
+ raise "Couldn't find liburing.a"
53
+ end
52
54
  end
53
55
 
54
56
  def define_bool(name, value)
@@ -92,12 +94,8 @@ $defs << '-DPOLYPHONY_PLAYGROUND' if ENV['POLYPHONY_PLAYGROUND']
92
94
 
93
95
  CONFIG['optflags'] << ' -fno-strict-aliasing' unless RUBY_PLATFORM =~ /mswin/
94
96
 
95
- if RUBY_VERSION >= '3.1'
96
- have_func('rb_fiber_transfer', 'ruby.h')
97
- end
98
-
99
97
  have_header('ruby/io/buffer.h')
100
-
98
+ have_func('rb_fiber_transfer')
101
99
  have_func('rb_io_path')
102
100
  have_func('rb_io_descriptor')
103
101
  have_func('rb_io_get_write_io')
@@ -404,9 +404,26 @@ VALUE Polyphony_raw_buffer_size(VALUE self, VALUE buffer) {
404
404
  return INT2FIX(buffer_spec->len);
405
405
  }
406
406
 
407
- // VALUE Polyphony_backend_close(VALUE self, VALUE io) {
408
- // return Backend_close(BACKEND(), io);
409
- // }
407
+ /* Closes the given IO.
408
+ *
409
+ * @param io [IO, Polyphony::Pipe] IO instance
410
+ * @return [IO, Polyphony::Pipe] given IO
411
+ */
412
+
413
+ VALUE Polyphony_backend_close(VALUE self, VALUE io) {
414
+ return Backend_close(BACKEND(), io);
415
+ }
416
+
417
+ /* Ensures the given IO is in blocking/non-blocking mode.
418
+ *
419
+ * @param io [IO, Polyphony::Pipe] IO instance
420
+ * @param blocking [boolean] true for blocking, false for non-blocking mode
421
+ * @return [IO, Polyphony::Pipe] given IO
422
+ */
423
+
424
+ VALUE Polyphony_backend_verify_blocking_mode(VALUE self, VALUE io, VALUE blocking) {
425
+ return Backend_verify_blocking_mode(BACKEND(), io, blocking);
426
+ }
410
427
 
411
428
  void Init_Polyphony(void) {
412
429
  mPolyphony = rb_define_module("Polyphony");
@@ -448,8 +465,8 @@ void Init_Polyphony(void) {
448
465
  rb_define_singleton_method(mPolyphony, "backend_wait_io", Polyphony_backend_wait_io, 2);
449
466
  rb_define_singleton_method(mPolyphony, "backend_waitpid", Polyphony_backend_waitpid, 1);
450
467
  rb_define_singleton_method(mPolyphony, "backend_write", Polyphony_backend_write, -1);
451
- // rb_define_singleton_method(mPolyphony, "backend_close", Polyphony_backend_close, 1);
452
- rb_define_singleton_method(mPolyphony, "backend_verify_blocking_mode", Backend_verify_blocking_mode, 2);
468
+ rb_define_singleton_method(mPolyphony, "backend_close", Polyphony_backend_close, 1);
469
+ rb_define_singleton_method(mPolyphony, "backend_verify_blocking_mode", Polyphony_backend_verify_blocking_mode, 2);
453
470
 
454
471
  rb_define_singleton_method(mPolyphony, "__with_raw_buffer__", Polyphony_with_raw_buffer, 1);
455
472
  rb_define_singleton_method(mPolyphony, "__raw_buffer_get__", Polyphony_raw_buffer_get, -1);
@@ -133,7 +133,7 @@ VALUE Backend_wait_io(VALUE self, VALUE io, VALUE write);
133
133
  VALUE Backend_waitpid(VALUE self, VALUE pid);
134
134
  VALUE Backend_write(VALUE self, VALUE io, VALUE str);
135
135
  VALUE Backend_write_m(int argc, VALUE *argv, VALUE self);
136
- // VALUE Backend_close(VALUE self, VALUE io);
136
+ VALUE Backend_close(VALUE self, VALUE io);
137
137
 
138
138
  VALUE Backend_poll(VALUE self, VALUE blocking);
139
139
  VALUE Backend_wait_event(VALUE self, VALUE raise_on_exception);
@@ -52,7 +52,7 @@ module Polyphony
52
52
 
53
53
  # Converts an event (expressed as an array) to a hash.
54
54
  #
55
- # @param e [Array] event as emitted by the backend
55
+ # @param event [Array] event as emitted by the backend
56
56
  # @return [Hash] event hash
57
57
  def trace_event_info(event)
58
58
  {
@@ -65,7 +65,7 @@ module Polyphony
65
65
 
66
66
  # Returns an event hash for a `:block` event.
67
67
  #
68
- # @param e [Array] event array
68
+ # @param event [Array] event array
69
69
  # @return [Hash] event hash
70
70
  def event_props_block(event)
71
71
  {
@@ -76,7 +76,7 @@ module Polyphony
76
76
 
77
77
  # Returns an event hash for a `:enter_poll` event.
78
78
  #
79
- # @param e [Array] event array
79
+ # @param _event [Array] event array
80
80
  # @return [Hash] event hash
81
81
  def event_props_enter_poll(_event)
82
82
  {}
@@ -84,7 +84,7 @@ module Polyphony
84
84
 
85
85
  # Returns an event hash for a `:leave_poll` event.
86
86
  #
87
- # @param e [Array] event array
87
+ # @param _event [Array] event array
88
88
  # @return [Hash] event hash
89
89
  def event_props_leave_poll(_event)
90
90
  {}
@@ -92,7 +92,7 @@ module Polyphony
92
92
 
93
93
  # Returns an event hash for a `:schedule` event.
94
94
  #
95
- # @param e [Array] event array
95
+ # @param event [Array] event array
96
96
  # @return [Hash] event hash
97
97
  def event_props_schedule(event)
98
98
  {
@@ -105,7 +105,7 @@ module Polyphony
105
105
 
106
106
  # Returns an event hash for a `:spin` event.
107
107
  #
108
- # @param e [Array] event array
108
+ # @param event [Array] event array
109
109
  # @return [Hash] event hash
110
110
  def event_props_spin(event)
111
111
  {
@@ -117,7 +117,7 @@ module Polyphony
117
117
 
118
118
  # Returns an event hash for a `:terminate` event.
119
119
  #
120
- # @param e [Array] event array
120
+ # @param event [Array] event array
121
121
  # @return [Hash] event hash
122
122
  def event_props_terminate(event)
123
123
  {
@@ -128,7 +128,7 @@ module Polyphony
128
128
 
129
129
  # Returns an event hash for a `:unblock` event.
130
130
  #
131
- # @param e [Array] event array
131
+ # @param event [Array] event array
132
132
  # @return [Hash] event hash
133
133
  def event_props_unblock(event)
134
134
  {
@@ -197,7 +197,7 @@ class ::IO
197
197
  alias_method :orig_read, :read
198
198
 
199
199
  # @!visibility private
200
- def read(len = nil, buf = nil, buffer_pos: 0)
200
+ def read(len = nil, buf = nil, buffer_pos = 0)
201
201
  return '' if len == 0
202
202
  return Polyphony.backend_read(self, buf, len, true, buffer_pos) if buf
203
203
 
@@ -214,7 +214,7 @@ class ::IO
214
214
  alias_method :orig_readpartial, :read
215
215
 
216
216
  # @!visibility private
217
- def readpartial(len, str = +'', buffer_pos: 0, raise_on_eof: true)
217
+ def readpartial(len, str = +'', buffer_pos = 0, raise_on_eof = true)
218
218
  result = Polyphony.backend_read(self, str, len, false, buffer_pos)
219
219
  raise EOFError if !result && raise_on_eof
220
220
 
@@ -255,7 +255,7 @@ class ::IO
255
255
  idx = @read_buffer.index(sep)
256
256
  return @read_buffer.slice!(0, idx + sep_size) if idx
257
257
 
258
- result = readpartial(8192, @read_buffer, buffer_pos: -1)
258
+ result = readpartial(8192, @read_buffer, -1)
259
259
  return nil unless result
260
260
  end
261
261
  rescue EOFError
@@ -280,7 +280,7 @@ class ::IO
280
280
  yield line
281
281
  end
282
282
 
283
- result = readpartial(8192, @read_buffer, buffer_pos: -1)
283
+ result = readpartial(8192, @read_buffer, -1)
284
284
  return self if !result
285
285
  end
286
286
  rescue EOFError
@@ -427,6 +427,19 @@ class ::IO
427
427
  Polyphony.backend_splice(src, self, maxlen)
428
428
  end
429
429
 
430
+ # @!visibility private
431
+ alias_method :orig_close, :close
432
+
433
+ # Closes the IO instance
434
+ #
435
+ # @return [void]
436
+ def close
437
+ return if closed?
438
+
439
+ Polyphony.backend_close(self)
440
+ nil
441
+ end
442
+
430
443
  if RUBY_PLATFORM =~ /linux/
431
444
  # Tees data from the given IO.
432
445
  #
@@ -5,6 +5,22 @@ require 'open3'
5
5
  module Polyphony
6
6
  # Intercepts calls to #trap
7
7
  module TrapInterceptor
8
+ # Installs a signal handler. If a block is given (or the command parameter
9
+ # is a Proc or a callable), it is executed inside an out-of-band,
10
+ # prioritized fiber.
11
+ #
12
+ # If the command is the string “IGNORE” or “SIG_IGN”, the signal will be
13
+ # ignored. If the command is “DEFAULT” or “SIG_DFL”, the Ruby’s default
14
+ # handler will be invoked. If the command is “EXIT”, the script will be
15
+ # terminated by the signal. If the command is “SYSTEM_DEFAULT”, the
16
+ # operating system’s default handler will be invoked. Otherwise, the given
17
+ # command or block will be run. The special signal name “EXIT” or signal
18
+ # number zero will be invoked just prior to program termination.
19
+ #
20
+ # trap returns the previous handler for the given signal.
21
+ #
22
+ # @param sig [String, Symbol, Integer] signal name or number
23
+ # @param command [String, Proc] command to perform
8
24
  def trap(sig, command = nil, &block)
9
25
  return super(sig, command) if command.is_a? String
10
26
 
@@ -90,7 +90,7 @@ class ::OpenSSL::SSL::SSLSocket
90
90
 
91
91
  # Reads from the socket. If `maxlen` is given, reads up to `maxlen` bytes from
92
92
  # the socket, otherwise reads to `EOF`. If `buf` is given, it is used as the
93
- # buffer to read into, otherwise a new string is allocated. If `buf_pos` is
93
+ # buffer to read into, otherwise a new string is allocated. If `buffer_pos` is
94
94
  # given, reads into the given offset (in bytes) in the given buffer. If the
95
95
  # given buffer offset is negative, it is calculated from the current end of
96
96
  # the buffer (`-1` means the read data will be appended to the end of the
@@ -101,21 +101,21 @@ class ::OpenSSL::SSL::SSLSocket
101
101
  #
102
102
  # @param maxlen [Integer, nil] maximum bytes to read from socket
103
103
  # @param buf [String, nil] buffer to read into
104
- # @param buf_pos [Number] buffer position to read into
104
+ # @param buffer_pos [Number] buffer position to read into
105
105
  # @return [String] buffer used for reading
106
- def read(maxlen = nil, buf = nil, buffer_pos: 0)
107
- return readpartial(maxlen, buf, buffer_pos:) if buf
106
+ def read(maxlen = nil, buf = nil, buffer_pos = 0)
107
+ return readpartial(maxlen, buf, buffer_pos) if buf
108
108
 
109
109
  buf = +''
110
110
  return readpartial(maxlen, buf) if maxlen
111
111
 
112
- readpartial(4096, buf, buffer_pos: -1) while true
112
+ readpartial(4096, buf, -1) while true
113
113
  rescue EOFError
114
114
  buf
115
115
  end
116
116
 
117
117
  # Reads up to `maxlen` from the socket. If `buf` is given, it is used as the
118
- # buffer to read into, otherwise a new string is allocated. If `buf_pos` is
118
+ # buffer to read into, otherwise a new string is allocated. If `buffer_pos` is
119
119
  # given, reads into the given offset (in bytes) in the given buffer. If the
120
120
  # given buffer offset is negative, it is calculated from the current end of
121
121
  # the buffer (`-1` means the read data will be appended to the end of the
@@ -127,16 +127,16 @@ class ::OpenSSL::SSL::SSLSocket
127
127
  #
128
128
  # @param maxlen [Integer, nil] maximum bytes to read from socket
129
129
  # @param buf [String, nil] buffer to read into
130
- # @param buf_pos [Number] buffer position to read into
130
+ # @param buffer_pos [Number] buffer position to read into
131
131
  # @param raise_on_eof [bool] whether to raise an exception on `EOF`
132
132
  # @return [String, nil] buffer used for reading or nil on `EOF`
133
- def readpartial(maxlen, buf = +'', buffer_pos: 0, raise_on_eof: true)
134
- if buf_pos != 0
133
+ def readpartial(maxlen, buf = +'', buffer_pos = 0, raise_on_eof = true)
134
+ if buffer_pos != 0
135
135
  if (result = sysread(maxlen, +''))
136
- if buf_pos == -1
136
+ if buffer_pos == -1
137
137
  result = buf + result
138
138
  else
139
- result = buf[0...buf_pos] + result
139
+ result = buf[0...buffer_pos] + result
140
140
  end
141
141
  end
142
142
  else
@@ -41,9 +41,9 @@ class Polyphony::Pipe
41
41
  #
42
42
  # @param len [Integer, nil] maximum bytes to read
43
43
  # @param buf [String, nil] buffer to read into
44
- # @param buf_pos [Integer] buffer position to read into
44
+ # @param buffer_pos [Integer] buffer position to read into
45
45
  # @return [String] read data
46
- def read(len = nil, buf = nil, buffer_pos: 0)
46
+ def read(len = nil, buf = nil, buffer_pos = 0)
47
47
  return Polyphony.backend_read(self, buf, len, true, buffer_pos) if buf
48
48
 
49
49
  @read_buffer ||= +''
@@ -59,10 +59,10 @@ class Polyphony::Pipe
59
59
  #
60
60
  # @param len [Integer, nil] maximum bytes to read
61
61
  # @param buf [String, nil] buffer to read into
62
- # @param buf_pos [Integer] buffer position to read into
62
+ # @param buffer_pos [Integer] buffer position to read into
63
63
  # @param raise_on_eof [boolean] whether to raise an error if EOF is detected
64
64
  # @return [String] read data
65
- def readpartial(len, buf = +'', buffer_pos: 0, raise_on_eof: true)
65
+ def readpartial(len, buf = +'', buffer_pos = 0, raise_on_eof = true)
66
66
  result = Polyphony.backend_read(self, buf, len, false, buffer_pos)
67
67
  raise EOFError if !result && raise_on_eof
68
68
 
@@ -104,7 +104,7 @@ class Polyphony::Pipe
104
104
  idx = @read_buffer.index(sep)
105
105
  return @read_buffer.slice!(0, idx + sep_size) if idx
106
106
 
107
- result = readpartial(8192, @read_buffer, buffer_pos: -1)
107
+ result = readpartial(8192, @read_buffer, -1)
108
108
  return nil unless result
109
109
  end
110
110
  rescue EOFError
@@ -59,7 +59,7 @@ class ::Socket < ::BasicSocket
59
59
 
60
60
  # Reads from the socket. If `maxlen` is given, reads up to `maxlen` bytes from
61
61
  # the socket, otherwise reads to `EOF`. If `buf` is given, it is used as the
62
- # buffer to read into, otherwise a new string is allocated. If `buf_pos` is
62
+ # buffer to read into, otherwise a new string is allocated. If `buffer_pos` is
63
63
  # given, reads into the given offset (in bytes) in the given buffer. If the
64
64
  # given buffer offset is negative, it is calculated from the current end of
65
65
  # the buffer (`-1` means the read data will be appended to the end of the
@@ -70,9 +70,9 @@ class ::Socket < ::BasicSocket
70
70
  #
71
71
  # @param len [Integer, nil] maximum bytes to read from socket
72
72
  # @param buf [String, nil] buffer to read into
73
- # @param buf_pos [Number] buffer position to read into
73
+ # @param buffer_pos [Number] buffer position to read into
74
74
  # @return [String] buffer used for reading
75
- def read(len = nil, buf = nil, buffer_pos: 0)
75
+ def read(len = nil, buf = nil, buffer_pos = 0)
76
76
  return '' if len == 0
77
77
  return Polyphony.backend_read(self, buf, len, true, buffer_pos) if buf
78
78
 
@@ -148,7 +148,7 @@ class ::Socket < ::BasicSocket
148
148
  end
149
149
 
150
150
  # Reads up to `maxlen` from the socket. If `buf` is given, it is used as the
151
- # buffer to read into, otherwise a new string is allocated. If `buf_pos` is
151
+ # buffer to read into, otherwise a new string is allocated. If `buffer_pos` is
152
152
  # given, reads into the given offset (in bytes) in the given buffer. If the
153
153
  # given buffer offset is negative, it is calculated from the current end of
154
154
  # the buffer (`-1` means the read data will be appended to the end of the
@@ -160,10 +160,10 @@ class ::Socket < ::BasicSocket
160
160
  #
161
161
  # @param maxlen [Integer, nil] maximum bytes to read from socket
162
162
  # @param buf [String, nil] buffer to read into
163
- # @param buf_pos [Number] buffer position to read into
163
+ # @param buffer_pos [Number] buffer position to read into
164
164
  # @param raise_on_eof [bool] whether to raise an exception on `EOF`
165
165
  # @return [String, nil] buffer used for reading or nil on `EOF`
166
- def readpartial(maxlen, buf = +'', buffer_pos: 0, raise_on_eof: true)
166
+ def readpartial(maxlen, buf = +'', buffer_pos = 0, raise_on_eof = true)
167
167
  result = Polyphony.backend_recv(self, buf, maxlen, buffer_pos)
168
168
  raise EOFError if !result && raise_on_eof
169
169
 
@@ -177,7 +177,7 @@ class ::Socket < ::BasicSocket
177
177
  #
178
178
  # @return [::Socket] self
179
179
  def dont_linger
180
- setsockopt(Socket::SOL_SOCKET, Socket::SO_LINGER, ZERO_LINGER)
180
+ setsockopt(Socket::SOL_SOCKET, Socket::SO_LINGER, Socket::ZERO_LINGER)
181
181
  self
182
182
  end
183
183
 
@@ -287,7 +287,7 @@ class ::TCPSocket < ::IPSocket
287
287
  #
288
288
  # @return [::Socket] self
289
289
  def dont_linger
290
- setsockopt(Socket::SOL_SOCKET, Socket::SO_LINGER, ZERO_LINGER)
290
+ setsockopt(Socket::SOL_SOCKET, Socket::SO_LINGER, Socket::ZERO_LINGER)
291
291
  self
292
292
  end
293
293
 
@@ -320,7 +320,7 @@ class ::TCPSocket < ::IPSocket
320
320
 
321
321
  # Reads from the socket. If `maxlen` is given, reads up to `maxlen` bytes from
322
322
  # the socket, otherwise reads to `EOF`. If `buf` is given, it is used as the
323
- # buffer to read into, otherwise a new string is allocated. If `buf_pos` is
323
+ # buffer to read into, otherwise a new string is allocated. If `buffer_pos` is
324
324
  # given, reads into the given offset (in bytes) in the given buffer. If the
325
325
  # given buffer offset is negative, it is calculated from the current end of
326
326
  # the buffer (`-1` means the read data will be appended to the end of the
@@ -331,9 +331,9 @@ class ::TCPSocket < ::IPSocket
331
331
  #
332
332
  # @param len [Integer, nil] maximum bytes to read from socket
333
333
  # @param buf [String, nil] buffer to read into
334
- # @param buf_pos [Number] buffer position to read into
334
+ # @param buffer_pos [Number] buffer position to read into
335
335
  # @return [String] buffer used for reading
336
- def read(len = nil, buf = nil, buffer_pos: 0)
336
+ def read(len = nil, buf = nil, buffer_pos = 0)
337
337
  return '' if len == 0
338
338
  return Polyphony.backend_read(self, buf, len, true, buffer_pos) if buf
339
339
 
@@ -391,7 +391,7 @@ class ::TCPSocket < ::IPSocket
391
391
  end
392
392
 
393
393
  # Reads up to `maxlen` from the socket. If `buf` is given, it is used as the
394
- # buffer to read into, otherwise a new string is allocated. If `buf_pos` is
394
+ # buffer to read into, otherwise a new string is allocated. If `buffer_pos` is
395
395
  # given, reads into the given offset (in bytes) in the given buffer. If the
396
396
  # given buffer offset is negative, it is calculated from the current end of
397
397
  # the buffer (`-1` means the read data will be appended to the end of the
@@ -403,10 +403,10 @@ class ::TCPSocket < ::IPSocket
403
403
  #
404
404
  # @param maxlen [Integer, nil] maximum bytes to read from socket
405
405
  # @param buf [String, nil] buffer to read into
406
- # @param buf_pos [Number] buffer position to read into
406
+ # @param buffer_pos [Number] buffer position to read into
407
407
  # @param raise_on_eof [bool] whether to raise an exception on `EOF`
408
408
  # @return [String, nil] buffer used for reading or nil on `EOF`
409
- def readpartial(maxlen, buf = +'', buffer_pos: 0, raise_on_eof: true)
409
+ def readpartial(maxlen, buf = +'', buffer_pos = 0, raise_on_eof = true)
410
410
  result = Polyphony.backend_recv(self, buf, maxlen, buffer_pos)
411
411
  raise EOFError if !result && raise_on_eof
412
412
 
@@ -526,7 +526,7 @@ class ::UNIXSocket < ::BasicSocket
526
526
 
527
527
  # Reads from the socket. If `maxlen` is given, reads up to `maxlen` bytes from
528
528
  # the socket, otherwise reads to `EOF`. If `buf` is given, it is used as the
529
- # buffer to read into, otherwise a new string is allocated. If `buf_pos` is
529
+ # buffer to read into, otherwise a new string is allocated. If `buffer_pos` is
530
530
  # given, reads into the given offset (in bytes) in the given buffer. If the
531
531
  # given buffer offset is negative, it is calculated from the current end of
532
532
  # the buffer (`-1` means the read data will be appended to the end of the
@@ -537,9 +537,9 @@ class ::UNIXSocket < ::BasicSocket
537
537
  #
538
538
  # @param len [Integer, nil] maximum bytes to read from socket
539
539
  # @param buf [String, nil] buffer to read into
540
- # @param buf_pos [Number] buffer position to read into
540
+ # @param buffer_pos [Number] buffer position to read into
541
541
  # @return [String] buffer used for reading
542
- def read(len = nil, buf = nil, buffer_pos: 0)
542
+ def read(len = nil, buf = nil, buffer_pos = 0)
543
543
  return '' if len == 0
544
544
  return Polyphony.backend_read(self, buf, len, true, buffer_pos) if buf
545
545
 
@@ -623,7 +623,7 @@ class ::UNIXSocket < ::BasicSocket
623
623
  end
624
624
 
625
625
  # Reads up to `maxlen` from the socket. If `buf` is given, it is used as the
626
- # buffer to read into, otherwise a new string is allocated. If `buf_pos` is
626
+ # buffer to read into, otherwise a new string is allocated. If `buffer_pos` is
627
627
  # given, reads into the given offset (in bytes) in the given buffer. If the
628
628
  # given buffer offset is negative, it is calculated from the current end of
629
629
  # the buffer (`-1` means the read data will be appended to the end of the
@@ -635,10 +635,10 @@ class ::UNIXSocket < ::BasicSocket
635
635
  #
636
636
  # @param maxlen [Integer, nil] maximum bytes to read from socket
637
637
  # @param buf [String, nil] buffer to read into
638
- # @param buf_pos [Number] buffer position to read into
638
+ # @param buffer_pos [Number] buffer position to read into
639
639
  # @param raise_on_eof [bool] whether to raise an exception on `EOF`
640
640
  # @return [String, nil] buffer used for reading or nil on `EOF`
641
- def readpartial(maxlen, buf = +'', buffer_pos: 0, raise_on_eof: true)
641
+ def readpartial(maxlen, buf = +'', buffer_pos = 0, raise_on_eof = true)
642
642
  result = Polyphony.backend_recv(self, buf, maxlen, buffer_pos)
643
643
  raise EOFError if !result && raise_on_eof
644
644
 
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Polyphony
4
4
  # @!visibility private
5
- VERSION = '1.2.1'
5
+ VERSION = '1.4'
6
6
  end
data/test/stress.rb CHANGED
@@ -3,11 +3,16 @@
3
3
  count = ARGV[0] ? ARGV[0].to_i : 100
4
4
  test_name = ARGV[1]
5
5
 
6
- $test_cmd = +'ruby test/test_scenarios.rb'
6
+ $test_cmd = +'ruby test/run.rb'
7
7
  if test_name
8
8
  $test_cmd << " --name #{test_name}"
9
9
  end
10
10
 
11
+ puts '*' * 40
12
+ puts
13
+ puts $test_cmd
14
+ puts
15
+
11
16
  def run_test(count)
12
17
  puts "#{count}: running tests..."
13
18
  # sleep 1
data/test/test_backend.rb CHANGED
@@ -544,4 +544,11 @@ class BackendChainTest < MiniTest::Test
544
544
  # released properly before raising the error (for the time being this has
545
545
  # been verified manually).
546
546
  end
547
+
548
+ def test_backend_close
549
+ r, w = IO.pipe
550
+ w << 'abc'
551
+ Thread.backend.close(w)
552
+ assert w.closed?
553
+ end
547
554
  end
@@ -137,7 +137,7 @@ class MoveOnAfterTest < MiniTest::Test
137
137
  t1 = monotonic_clock
138
138
 
139
139
  assert_nil v
140
- assert_in_range 0.012..0.025, t1 - t0 if IS_LINUX
140
+ assert_in_range 0.012..0.030, t1 - t0 if IS_LINUX
141
141
  end
142
142
 
143
143
  def test_nested_move_on_after
@@ -161,7 +161,7 @@ class MoveOnAfterTest < MiniTest::Test
161
161
  end
162
162
  t1 = monotonic_clock
163
163
  assert_equal 2, o
164
- assert_in_range 0.008..0.025, t1 - t0 if IS_LINUX
164
+ assert_in_range 0.008..0.035, t1 - t0 if IS_LINUX
165
165
  end
166
166
  end
167
167
 
data/test/test_io.rb CHANGED
@@ -89,7 +89,7 @@ class IOTest < MiniTest::Test
89
89
 
90
90
  buf = +'def'
91
91
  o << 'foobar'
92
- assert_equal 'deffoobar', i.read(6, buf, buffer_pos: -1)
92
+ assert_equal 'deffoobar', i.read(6, buf, -1)
93
93
  assert_equal 'deffoobar', buf
94
94
  end
95
95
 
data/test/test_signal.rb CHANGED
@@ -4,6 +4,10 @@ require_relative 'helper'
4
4
 
5
5
  class SignalTrapTest < Minitest::Test
6
6
  def test_signal_handler_trace
7
+ if Thread.current.backend.kind != :io_uring
8
+ skip "Skipping signal handler trace because Backend_close on libev behaves differently"
9
+ end
10
+
7
11
  i1, o1 = IO.pipe
8
12
  i2, o2 = IO.pipe
9
13
  pid = Process.pid
@@ -39,16 +43,21 @@ class SignalTrapTest < Minitest::Test
39
43
  end
40
44
 
41
45
  expected = [
42
- [:block, :main],
46
+ [:block, :main],
47
+ [:enter_poll, :main],
48
+ [:schedule, :main],
49
+ [:leave_poll, :main],
50
+ [:unblock, :main],
51
+ [:block, :main],
43
52
  [:enter_poll, :main],
44
53
  [:leave_poll, :main],
45
- [:unblock, :oob],
46
- [:terminate, :oob],
47
- [:block, :oob],
54
+ [:unblock, :oob],
55
+ [:terminate, :oob],
56
+ [:block, :oob],
48
57
  [:enter_poll, :oob],
49
- [:schedule, :main],
58
+ [:schedule, :main],
50
59
  [:leave_poll, :oob],
51
- [:unblock, :main]
60
+ [:unblock, :main]
52
61
  ]
53
62
  if Thread.backend.kind == :libev
54
63
  expected += [
data/test/test_socket.rb CHANGED
@@ -73,12 +73,8 @@ class TCPSocketTest < MiniTest::Test
73
73
  def test_read
74
74
  port, server = start_tcp_server_on_random_port
75
75
  server_fiber = spin do
76
- while (socket = server.accept)
77
- spin do
78
- while (data = socket.readpartial(8192))
79
- socket << data
80
- end
81
- end
76
+ server.accept_loop do |socket|
77
+ spin { socket.recv_loop { |data| socket << data } }
82
78
  end
83
79
  end
84
80
 
@@ -98,7 +94,7 @@ class TCPSocketTest < MiniTest::Test
98
94
 
99
95
  buf = +'def'
100
96
  client << 'foobar'
101
- assert_equal 'deffoobar', client.read(6, buf, buffer_pos: -1)
97
+ assert_equal 'deffoobar', client.read(6, buf, -1)
102
98
  assert_equal 'deffoobar', buf
103
99
 
104
100
  client.close
@@ -163,6 +159,17 @@ class TCPSocketTest < MiniTest::Test
163
159
  server_fiber&.await
164
160
  server&.close
165
161
  end
162
+
163
+ def test_sockopt
164
+ client = TCPSocket.open('ipinfo.io', 80)
165
+
166
+ client.dont_linger
167
+ client.no_delay
168
+ client.reuse_addr
169
+ client.reuse_port
170
+ ensure
171
+ client.close
172
+ end
166
173
  end
167
174
 
168
175
  class UNIXSocketTest < MiniTest::Test
data/test/test_timer.rb CHANGED
@@ -73,7 +73,7 @@ class TimerCancelAfterTest < MiniTest::Test
73
73
  end
74
74
  end
75
75
  t1 = Time.now
76
- assert_in_range 0.01..0.03, t1 - t0 if IS_LINUX
76
+ assert_in_range 0.01..0.04, t1 - t0 if IS_LINUX
77
77
  end
78
78
 
79
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: 1.2.1
4
+ version: '1.4'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sharon Rosner
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-06-17 00:00:00.000000000 Z
11
+ date: 2023-07-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake-compiler
@@ -191,6 +191,7 @@ files:
191
191
  - docs/extending.md
192
192
  - docs/faq.md
193
193
  - docs/fiber-scheduling.md
194
+ - docs/installation.md
194
195
  - docs/overview.md
195
196
  - docs/readme.md
196
197
  - docs/tutorial.md
@@ -234,6 +235,7 @@ files:
234
235
  - examples/core/shutdown_all_children.rb
235
236
  - examples/core/spin.rb
236
237
  - examples/core/spin_error_backtrace.rb
238
+ - examples/core/stages.rb
237
239
  - examples/core/stream_mockup.rb
238
240
  - examples/core/supervise-process.rb
239
241
  - examples/core/supervisor.rb