polyphony 1.3 → 1.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.yardopts +1 -0
- data/CHANGELOG.md +6 -0
- data/README.md +11 -45
- data/docs/installation.md +30 -0
- data/docs/readme.md +11 -45
- data/examples/core/stages.rb +99 -0
- data/ext/polyphony/backend_common.c +1 -1
- data/ext/polyphony/backend_io_uring.c +28 -29
- data/ext/polyphony/backend_libev.c +26 -0
- data/ext/polyphony/polyphony.c +22 -5
- data/ext/polyphony/polyphony.h +1 -1
- data/lib/polyphony/core/debug.rb +8 -8
- data/lib/polyphony/extensions/io.rb +17 -4
- data/lib/polyphony/extensions/kernel.rb +16 -0
- data/lib/polyphony/extensions/openssl.rb +11 -11
- data/lib/polyphony/extensions/pipe.rb +5 -5
- data/lib/polyphony/extensions/socket.rb +18 -18
- data/lib/polyphony/version.rb +1 -1
- data/test/stress.rb +6 -1
- data/test/test_backend.rb +7 -0
- data/test/test_global_api.rb +2 -2
- data/test/test_io.rb +1 -1
- data/test/test_signal.rb +15 -6
- data/test/test_socket.rb +3 -7
- data/test/test_timer.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 91cce4703d1b930aa9b242373492a373abd6d49b5ce200f8828683538c15562b
|
4
|
+
data.tar.gz: c5c8af2ab5f4961b6379015b8d68f10c56dc5c466a44c0a0a4d6be4a93ab26b9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5a9cb0b512efaae4b0454f3b5e99807657275a8d4c80dd8f527235c842c3131f4a38f4e4eefc174aeae1c2e7c6f879e9ccf8064e49b42dd69cb27d7780ff1780
|
7
|
+
data.tar.gz: 19977c5db508a5a5bdf1fa1f4b0db061436849c5d75f59c62764ae3bfd5426014e4e119e7306b68edf25c82cea9193f259c2e1403b1def825847614c003026dd
|
data/.yardopts
CHANGED
data/CHANGELOG.md
CHANGED
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://
|
27
|
-
|
28
|
-
|
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
|
-
*
|
35
|
-
*
|
36
|
-
|
37
|
-
|
38
|
-
*
|
39
|
-
|
40
|
-
|
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)
|
@@ -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://
|
29
|
-
|
30
|
-
|
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
|
-
*
|
37
|
-
*
|
38
|
-
|
39
|
-
|
40
|
-
*
|
41
|
-
|
42
|
-
|
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
|
461
|
+
return io;
|
462
462
|
}
|
463
463
|
|
464
464
|
void backend_setup_stats_symbols(void) {
|
@@ -1363,34 +1363,33 @@ VALUE Backend_wait_io(VALUE self, VALUE io, VALUE write) {
|
|
1363
1363
|
return self;
|
1364
1364
|
}
|
1365
1365
|
|
1366
|
-
|
1367
|
-
|
1368
|
-
|
1369
|
-
|
1370
|
-
|
1371
|
-
|
1372
|
-
|
1373
|
-
|
1374
|
-
|
1375
|
-
|
1376
|
-
|
1377
|
-
|
1378
|
-
|
1379
|
-
|
1380
|
-
|
1381
|
-
|
1382
|
-
|
1383
|
-
|
1384
|
-
|
1385
|
-
|
1386
|
-
|
1387
|
-
|
1388
|
-
|
1389
|
-
|
1390
|
-
//
|
1391
|
-
|
1392
|
-
|
1393
|
-
// }
|
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;
|
1376
|
+
|
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);
|
1386
|
+
|
1387
|
+
if (result < 0) rb_syserr_fail(-result, strerror(-result));
|
1388
|
+
|
1389
|
+
fptr_finalize(fptr);
|
1390
|
+
// fd = -1;
|
1391
|
+
return io;
|
1392
|
+
}
|
1394
1393
|
|
1395
1394
|
inline struct __kernel_timespec double_to_timespec(double duration) {
|
1396
1395
|
double duration_integral;
|
@@ -2015,7 +2014,7 @@ void Init_Backend(void) {
|
|
2015
2014
|
rb_define_method(cBackend, "wait_io", Backend_wait_io, 2);
|
2016
2015
|
rb_define_method(cBackend, "waitpid", Backend_waitpid, 1);
|
2017
2016
|
rb_define_method(cBackend, "write", Backend_write_m, -1);
|
2018
|
-
|
2017
|
+
rb_define_method(cBackend, "close", Backend_close, 1);
|
2019
2018
|
|
2020
2019
|
SYM_io_uring = ID2SYM(rb_intern("io_uring"));
|
2021
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
|
|
data/ext/polyphony/polyphony.c
CHANGED
@@ -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
|
-
|
408
|
-
|
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
|
-
|
452
|
-
rb_define_singleton_method(mPolyphony, "backend_verify_blocking_mode",
|
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);
|
data/ext/polyphony/polyphony.h
CHANGED
@@ -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
|
-
|
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);
|
data/lib/polyphony/core/debug.rb
CHANGED
@@ -52,7 +52,7 @@ module Polyphony
|
|
52
52
|
|
53
53
|
# Converts an event (expressed as an array) to a hash.
|
54
54
|
#
|
55
|
-
# @param
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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,
|
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,
|
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 `
|
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
|
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
|
107
|
-
return readpartial(maxlen, buf, buffer_pos
|
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,
|
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 `
|
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
|
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
|
134
|
-
if
|
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
|
136
|
+
if buffer_pos == -1
|
137
137
|
result = buf + result
|
138
138
|
else
|
139
|
-
result = buf[0...
|
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
|
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
|
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
|
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
|
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,
|
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 `
|
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
|
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
|
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 `
|
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
|
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
|
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
|
|
@@ -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 `
|
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
|
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
|
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 `
|
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
|
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
|
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 `
|
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
|
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
|
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 `
|
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
|
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
|
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
|
|
data/lib/polyphony/version.rb
CHANGED
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/
|
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
|
data/test/test_global_api.rb
CHANGED
@@ -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.
|
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.
|
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
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,
|
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,
|
46
|
-
[:terminate,
|
47
|
-
[:block,
|
54
|
+
[:unblock, :oob],
|
55
|
+
[:terminate, :oob],
|
56
|
+
[:block, :oob],
|
48
57
|
[:enter_poll, :oob],
|
49
|
-
[:schedule,
|
58
|
+
[:schedule, :main],
|
50
59
|
[:leave_poll, :oob],
|
51
|
-
[:unblock,
|
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
|
-
|
77
|
-
spin
|
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,
|
97
|
+
assert_equal 'deffoobar', client.read(6, buf, -1)
|
102
98
|
assert_equal 'deffoobar', buf
|
103
99
|
|
104
100
|
client.close
|
data/test/test_timer.rb
CHANGED
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.
|
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-
|
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
|