polyphony 0.49.2 → 0.53.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/test.yml +1 -1
- data/CHANGELOG.md +34 -0
- data/Gemfile.lock +7 -68
- data/TODO.md +37 -3
- data/examples/core/forking.rb +2 -2
- data/examples/core/nested.rb +21 -0
- data/examples/core/suspend.rb +13 -0
- data/examples/core/terminate_main_fiber.rb +12 -0
- data/examples/io/echo_server.rb +1 -0
- data/examples/io/tcp_proxy.rb +2 -2
- data/ext/polyphony/backend_common.h +58 -8
- data/ext/polyphony/backend_io_uring.c +223 -41
- data/ext/polyphony/backend_io_uring_context.c +1 -0
- data/ext/polyphony/backend_io_uring_context.h +1 -0
- data/ext/polyphony/backend_libev.c +322 -34
- data/ext/polyphony/event.c +1 -1
- data/ext/polyphony/extconf.rb +9 -2
- data/ext/polyphony/fiber.c +2 -1
- data/ext/polyphony/polyphony.c +102 -0
- data/ext/polyphony/polyphony.h +33 -2
- data/ext/polyphony/polyphony_ext.c +3 -0
- data/ext/polyphony/queue.c +1 -1
- data/ext/polyphony/runqueue.c +7 -1
- data/ext/polyphony/runqueue_ring_buffer.c +9 -0
- data/ext/polyphony/runqueue_ring_buffer.h +1 -0
- data/ext/polyphony/socket_extensions.c +33 -0
- data/ext/polyphony/thread.c +14 -0
- data/lib/polyphony/adapters/irb.rb +1 -1
- data/lib/polyphony/adapters/mysql2.rb +1 -1
- data/lib/polyphony/adapters/postgres.rb +5 -5
- data/lib/polyphony/adapters/process.rb +4 -4
- data/lib/polyphony/core/exceptions.rb +1 -0
- data/lib/polyphony/core/global_api.rb +6 -6
- data/lib/polyphony/core/sync.rb +1 -1
- data/lib/polyphony/core/throttler.rb +1 -1
- data/lib/polyphony/core/timer.rb +63 -20
- data/lib/polyphony/extensions/core.rb +5 -5
- data/lib/polyphony/extensions/fiber.rb +2 -0
- data/lib/polyphony/extensions/io.rb +21 -22
- data/lib/polyphony/extensions/openssl.rb +6 -6
- data/lib/polyphony/extensions/socket.rb +56 -47
- data/lib/polyphony/version.rb +1 -1
- data/polyphony.gemspec +6 -5
- data/test/helper.rb +1 -1
- data/test/stress.rb +2 -0
- data/test/test_backend.rb +69 -5
- data/test/test_fiber.rb +16 -0
- data/test/test_global_api.rb +2 -2
- data/test/test_io.rb +84 -1
- data/test/test_kernel.rb +1 -1
- data/test/test_signal.rb +1 -1
- data/test/test_socket.rb +61 -0
- data/test/test_timer.rb +41 -8
- metadata +22 -60
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '0682736e3fdca0ada986f4210f1bb25d682c75a7b9d297451c186b90c8025e13'
|
4
|
+
data.tar.gz: 5ff46620651c056983a6f73c879ca1c49acea22c0c7abcefb1a119cbe248cb77
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cfbb8141b21cc9e56cdcd9a34fd55cd3b48573dcc3011f127ece5049b13ca2c7fa8a17c8525ba5996ab97afcbfc8e46064a3d68c79d71d44d40c274ff9051d2d
|
7
|
+
data.tar.gz: f31d711c88fe354b8ffd63e37f30887b1bfb5c3709d3760412ee4ec4ea06d14c0a17e668ddd750958295dc12307b96cf64874b1be4ed46b8564d04236114ee67
|
data/.github/workflows/test.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,37 @@
|
|
1
|
+
## 0.53.0
|
2
|
+
|
3
|
+
- Implement `Backend#splice`, `Backend#splice_to_eof`, along with `IO#splice`, `IO#splice_to_eof`
|
4
|
+
|
5
|
+
## 0.52.0
|
6
|
+
|
7
|
+
- Polyphony is now compatible with Ruby 3.0
|
8
|
+
- Add `Backend#sendv` method for sending multiple strings
|
9
|
+
- Accept flags argument in `Backend#send` (#48)
|
10
|
+
- Fix io_uring backend on Ruby 3.0 (#47)
|
11
|
+
- Implement C-based public backend API: `Polyphony.backend_XXXX` methods
|
12
|
+
- libev backend: Use` pidfd_open` for Linux 5.3+, otherwise use a libev child watcher
|
13
|
+
- Use `:call` as default method in `#feed_loop`
|
14
|
+
|
15
|
+
## 0.51.0
|
16
|
+
|
17
|
+
- Implement `IO#feed_loop`, `Socket#feed_loop`
|
18
|
+
- Fix error handling in `Process.kill_and_await`
|
19
|
+
|
20
|
+
## 0.50.1
|
21
|
+
|
22
|
+
- Set `IOSQE_ASYNC` flag in io_uring backend
|
23
|
+
- Fix error handling in `Backend#waitpid`
|
24
|
+
- Reimplement libev backend's `#waitpid` by using pidfd_open (in similar manner
|
25
|
+
to the io_uring backend)
|
26
|
+
|
27
|
+
## 0.50.0
|
28
|
+
|
29
|
+
- Use `Process::CLOCK_MONOTONIC` in Timer
|
30
|
+
- Add `Timer#sleep`, `Timer#after`, `Timer#every`
|
31
|
+
- Prevent fiber from being resumed after terminating
|
32
|
+
- Add `Thread#fiber_index_of` method
|
33
|
+
- Use `Backend#wait_event` in `Fiber#await`
|
34
|
+
|
1
35
|
## 0.49.2
|
2
36
|
|
3
37
|
- Fix hang with 100s or more child fibers when terminating
|
data/Gemfile.lock
CHANGED
@@ -1,101 +1,50 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
polyphony (0.
|
4
|
+
polyphony (0.53.0)
|
5
5
|
|
6
6
|
GEM
|
7
7
|
remote: https://rubygems.org/
|
8
8
|
specs:
|
9
|
-
addressable (2.7.0)
|
10
|
-
public_suffix (>= 2.0.2, < 5.0)
|
11
9
|
ansi (1.5.0)
|
12
10
|
ast (2.4.0)
|
13
11
|
builder (3.2.4)
|
14
12
|
coderay (1.1.3)
|
15
|
-
colorator (1.1.0)
|
16
|
-
concurrent-ruby (1.1.6)
|
17
13
|
docile (1.3.2)
|
18
|
-
em-websocket (0.5.1)
|
19
|
-
eventmachine (>= 0.12.9)
|
20
|
-
http_parser.rb (~> 0.6.0)
|
21
|
-
eventmachine (1.2.7)
|
22
|
-
ffi (1.12.1)
|
23
|
-
forwardable-extended (2.6.0)
|
24
14
|
hiredis (0.6.3)
|
25
15
|
http_parser.rb (0.6.0)
|
26
16
|
httparty (0.17.1)
|
27
17
|
mime-types (~> 3.0)
|
28
18
|
multi_xml (>= 0.5.2)
|
29
|
-
i18n (0.9.5)
|
30
|
-
concurrent-ruby (~> 1.0)
|
31
|
-
jekyll (3.8.6)
|
32
|
-
addressable (~> 2.4)
|
33
|
-
colorator (~> 1.0)
|
34
|
-
em-websocket (~> 0.5)
|
35
|
-
i18n (~> 0.7)
|
36
|
-
jekyll-sass-converter (~> 1.0)
|
37
|
-
jekyll-watch (~> 2.0)
|
38
|
-
kramdown (~> 1.14)
|
39
|
-
liquid (~> 4.0)
|
40
|
-
mercenary (~> 0.3.3)
|
41
|
-
pathutil (~> 0.9)
|
42
|
-
rouge (>= 1.7, < 4)
|
43
|
-
safe_yaml (~> 1.0)
|
44
|
-
jekyll-remote-theme (0.4.1)
|
45
|
-
addressable (~> 2.0)
|
46
|
-
jekyll (>= 3.5, < 5.0)
|
47
|
-
rubyzip (>= 1.3.0)
|
48
|
-
jekyll-sass-converter (1.5.2)
|
49
|
-
sass (~> 3.4)
|
50
|
-
jekyll-seo-tag (2.6.1)
|
51
|
-
jekyll (>= 3.3, < 5.0)
|
52
|
-
jekyll-watch (2.2.1)
|
53
|
-
listen (~> 3.0)
|
54
19
|
json (2.3.0)
|
55
|
-
just-the-docs (0.3.0)
|
56
|
-
jekyll (>= 3.8.5)
|
57
|
-
jekyll-seo-tag (~> 2.0)
|
58
|
-
rake (>= 12.3.1, < 13.1.0)
|
59
|
-
kramdown (1.17.0)
|
60
|
-
liquid (4.0.3)
|
61
|
-
listen (3.2.1)
|
62
|
-
rb-fsevent (~> 0.10, >= 0.10.3)
|
63
|
-
rb-inotify (~> 0.9, >= 0.9.10)
|
64
|
-
mercenary (0.3.6)
|
65
20
|
method_source (1.0.0)
|
66
21
|
mime-types (3.3.1)
|
67
22
|
mime-types-data (~> 3.2015)
|
68
23
|
mime-types-data (3.2020.0512)
|
69
|
-
minitest (5.
|
24
|
+
minitest (5.14.4)
|
70
25
|
minitest-reporters (1.4.2)
|
71
26
|
ansi
|
72
27
|
builder
|
73
28
|
minitest (>= 5.0)
|
74
29
|
ruby-progressbar
|
30
|
+
msgpack (1.4.2)
|
75
31
|
multi_xml (0.6.0)
|
76
32
|
mysql2 (0.5.3)
|
77
33
|
parallel (1.19.1)
|
78
34
|
parser (2.7.0.2)
|
79
35
|
ast (~> 2.4.0)
|
80
|
-
pathutil (0.16.2)
|
81
|
-
forwardable-extended (~> 2.6)
|
82
36
|
pg (1.1.4)
|
83
37
|
pry (0.13.1)
|
84
38
|
coderay (~> 1.1)
|
85
39
|
method_source (~> 1.0)
|
86
|
-
public_suffix (4.0.3)
|
87
40
|
rack (2.2.3)
|
88
41
|
rainbow (3.0.0)
|
89
|
-
rake (
|
42
|
+
rake (13.0.3)
|
90
43
|
rake-compiler (1.1.1)
|
91
44
|
rake
|
92
|
-
rb-fsevent (0.10.3)
|
93
|
-
rb-inotify (0.10.1)
|
94
|
-
ffi (~> 1.0)
|
95
45
|
redis (4.1.0)
|
96
46
|
regexp_parser (1.7.1)
|
97
47
|
rexml (3.2.4)
|
98
|
-
rouge (3.15.0)
|
99
48
|
rubocop (0.85.1)
|
100
49
|
parallel (~> 1.10)
|
101
50
|
parser (>= 2.7.0.1)
|
@@ -108,13 +57,6 @@ GEM
|
|
108
57
|
rubocop-ast (0.0.3)
|
109
58
|
parser (>= 2.7.0.1)
|
110
59
|
ruby-progressbar (1.10.1)
|
111
|
-
rubyzip (2.0.0)
|
112
|
-
safe_yaml (1.0.5)
|
113
|
-
sass (3.7.4)
|
114
|
-
sass-listen (~> 4.0.0)
|
115
|
-
sass-listen (4.0.0)
|
116
|
-
rb-fsevent (~> 0.9, >= 0.9.4)
|
117
|
-
rb-inotify (~> 0.9, >= 0.9.7)
|
118
60
|
sequel (5.34.0)
|
119
61
|
simplecov (0.17.1)
|
120
62
|
docile (~> 1.1)
|
@@ -130,12 +72,9 @@ DEPENDENCIES
|
|
130
72
|
hiredis (= 0.6.3)
|
131
73
|
http_parser.rb (~> 0.6.0)
|
132
74
|
httparty (= 0.17.1)
|
133
|
-
|
134
|
-
jekyll-remote-theme (~> 0.4.1)
|
135
|
-
jekyll-seo-tag (~> 2.6.1)
|
136
|
-
just-the-docs (~> 0.3.0)
|
137
|
-
minitest (= 5.13.0)
|
75
|
+
minitest (= 5.14.4)
|
138
76
|
minitest-reporters (= 1.4.2)
|
77
|
+
msgpack (= 1.4.2)
|
139
78
|
mysql2 (= 0.5.3)
|
140
79
|
pg (= 1.1.4)
|
141
80
|
polyphony!
|
@@ -148,4 +87,4 @@ DEPENDENCIES
|
|
148
87
|
simplecov (= 0.17.1)
|
149
88
|
|
150
89
|
BUNDLED WITH
|
151
|
-
2.
|
90
|
+
2.2.3
|
data/TODO.md
CHANGED
@@ -1,3 +1,8 @@
|
|
1
|
+
- Add support for IPv6:
|
2
|
+
https://www.reddit.com/r/ruby/comments/lyen23/understanding_ipv6_and_why_its_important_to_you/
|
3
|
+
|
4
|
+
- Add support for UDP sockets
|
5
|
+
|
1
6
|
- Check segfault when resetting a `cancel_after` timeout lots of times at very high rate
|
2
7
|
- Check why `throttled_loop` inside of `move_on_after` fails to stop
|
3
8
|
|
@@ -13,17 +18,46 @@
|
|
13
18
|
- `Fiber#receive_loop` (very little effort, should be implemented in C)
|
14
19
|
|
15
20
|
|
16
|
-
- Add `Backend#splice`, `Backend#
|
21
|
+
- Add `Backend#splice`, `Backend#splice_to_eof` for implementing stuff like proxying:
|
17
22
|
|
18
23
|
```ruby
|
19
24
|
def two_way_proxy(socket1, socket2)
|
20
25
|
backend = Thread.current.backend
|
21
|
-
f1 = spin { backend.
|
22
|
-
f2 = spin { backend.
|
26
|
+
f1 = spin { backend.splice_to_eof(socket1, socket2) }
|
27
|
+
f2 = spin { backend.splice_to_eof(socket2, socket1) }
|
23
28
|
Fiber.await(f1, f2)
|
24
29
|
end
|
25
30
|
```
|
26
31
|
|
32
|
+
- Add support for `close` to io_uring backend
|
33
|
+
|
34
|
+
- Add support for submission of multiple requests to io_uring backend:
|
35
|
+
|
36
|
+
```ruby
|
37
|
+
Thread.current.backend.submit(
|
38
|
+
[sock, :<<, chunk_header(len)],
|
39
|
+
[sock, :splice, file, len]
|
40
|
+
)
|
41
|
+
```
|
42
|
+
|
43
|
+
Full example (for writing chunks from a file to an HTTP response):
|
44
|
+
|
45
|
+
```ruby
|
46
|
+
def serve_io(io)
|
47
|
+
i, o = IO.pipe
|
48
|
+
backend = Thread.current.backend
|
49
|
+
while true
|
50
|
+
len = o.splice(io, 8192)
|
51
|
+
break if len == 0
|
52
|
+
|
53
|
+
backend.submit(
|
54
|
+
[sock, :<<, chunk_header(len)],
|
55
|
+
[sock, :splice, file, len]
|
56
|
+
)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
```
|
60
|
+
|
27
61
|
- Graceful shutdown again:
|
28
62
|
- What happens to children when doing a graceful shutdown?
|
29
63
|
- What are the implications of passing graceful shutdown flag to children?
|
data/examples/core/forking.rb
CHANGED
@@ -5,7 +5,7 @@ require 'polyphony'
|
|
5
5
|
|
6
6
|
puts "parent pid: #{Process.pid}"
|
7
7
|
|
8
|
-
pid =
|
8
|
+
pid = fork do
|
9
9
|
puts "child pid: #{Process.pid}"
|
10
10
|
|
11
11
|
spin do
|
@@ -20,5 +20,5 @@ end
|
|
20
20
|
puts "got child pid #{pid}"
|
21
21
|
|
22
22
|
puts 'parent waiting for child'
|
23
|
-
|
23
|
+
Polyphony.backend_waitpid(pid)
|
24
24
|
puts 'parent done waiting'
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'polyphony'
|
5
|
+
|
6
|
+
def process
|
7
|
+
p :b_start
|
8
|
+
sleep 1
|
9
|
+
p :b_stop
|
10
|
+
end
|
11
|
+
|
12
|
+
spin do
|
13
|
+
p :a_start
|
14
|
+
spin { process }
|
15
|
+
sleep 60
|
16
|
+
p :a_stop
|
17
|
+
end
|
18
|
+
|
19
|
+
p :main_start
|
20
|
+
sleep 120
|
21
|
+
p :main_stop
|
data/examples/io/echo_server.rb
CHANGED
data/examples/io/tcp_proxy.rb
CHANGED
@@ -15,7 +15,7 @@ f1 = spin {
|
|
15
15
|
client1 = server1.accept
|
16
16
|
loop do
|
17
17
|
if client2
|
18
|
-
Thread.current.backend.
|
18
|
+
Thread.current.backend.splice_to_eof(client1, client2)
|
19
19
|
end
|
20
20
|
end
|
21
21
|
}
|
@@ -24,7 +24,7 @@ f2 = spin {
|
|
24
24
|
client2 = server2.accept
|
25
25
|
loop do
|
26
26
|
if client1
|
27
|
-
Thread.current.backend.
|
27
|
+
Thread.current.backend.splice_to_eof(client2, client1)
|
28
28
|
end
|
29
29
|
end
|
30
30
|
}
|
@@ -3,6 +3,17 @@
|
|
3
3
|
#include "ruby.h"
|
4
4
|
#include "ruby/io.h"
|
5
5
|
|
6
|
+
|
7
|
+
#ifdef POLYPHONY_USE_PIDFD_OPEN
|
8
|
+
#ifndef __NR_pidfd_open
|
9
|
+
#define __NR_pidfd_open 434 /* System call # on most architectures */
|
10
|
+
#endif
|
11
|
+
|
12
|
+
static int pidfd_open(pid_t pid, unsigned int flags) {
|
13
|
+
return syscall(__NR_pidfd_open, pid, flags);
|
14
|
+
}
|
15
|
+
#endif
|
16
|
+
|
6
17
|
//////////////////////////////////////////////////////////////////////
|
7
18
|
//////////////////////////////////////////////////////////////////////
|
8
19
|
// the following is copied verbatim from the Ruby source code (io.c)
|
@@ -15,11 +26,11 @@ struct io_internal_read_struct {
|
|
15
26
|
|
16
27
|
#define StringValue(v) rb_string_value(&(v))
|
17
28
|
|
18
|
-
|
29
|
+
int io_setstrbuf(VALUE *str, long len) {
|
19
30
|
#ifdef _WIN32
|
20
31
|
len = (len + 1) & ~1L; /* round up for wide char */
|
21
32
|
#endif
|
22
|
-
if (
|
33
|
+
if (*str == Qnil) {
|
23
34
|
*str = rb_str_new(0, len);
|
24
35
|
return 1;
|
25
36
|
}
|
@@ -44,7 +55,7 @@ inline void io_shrink_read_string(VALUE str, long n) {
|
|
44
55
|
}
|
45
56
|
}
|
46
57
|
|
47
|
-
|
58
|
+
void io_set_read_length(VALUE str, long n, int shrinkable) {
|
48
59
|
if (RSTRING_LEN(str) != n) {
|
49
60
|
rb_str_modify(str);
|
50
61
|
rb_str_set_len(str, n);
|
@@ -59,7 +70,7 @@ inline rb_encoding* io_read_encoding(rb_io_t *fptr) {
|
|
59
70
|
return rb_default_external_encoding();
|
60
71
|
}
|
61
72
|
|
62
|
-
|
73
|
+
VALUE io_enc_str(VALUE str, rb_io_t *fptr) {
|
63
74
|
OBJ_TAINT(str);
|
64
75
|
rb_enc_associate(str, io_read_encoding(fptr));
|
65
76
|
return str;
|
@@ -99,6 +110,13 @@ inline VALUE backend_snooze() {
|
|
99
110
|
READ_LOOP_PREPARE_STR(); \
|
100
111
|
}
|
101
112
|
|
113
|
+
#define READ_LOOP_PASS_STR_TO_RECEIVER(receiver, method_id) { \
|
114
|
+
io_set_read_length(str, total, shrinkable); \
|
115
|
+
io_enc_str(str, fptr); \
|
116
|
+
rb_funcall_passing_block(receiver, method_id, 1, &str); \
|
117
|
+
READ_LOOP_PREPARE_STR(); \
|
118
|
+
}
|
119
|
+
|
102
120
|
inline void rectify_io_file_pos(rb_io_t *fptr) {
|
103
121
|
// Apparently after reopening a closed file, the file position is not reset,
|
104
122
|
// which causes the read to fail. Fortunately we can use fptr->rbuf.len to
|
@@ -114,16 +132,48 @@ inline double current_time() {
|
|
114
132
|
struct timespec ts;
|
115
133
|
clock_gettime(CLOCK_MONOTONIC, &ts);
|
116
134
|
long long ns = ts.tv_sec;
|
117
|
-
ns = ns *
|
135
|
+
ns = ns * 1e9 + ts.tv_nsec;
|
118
136
|
double t = ns;
|
119
137
|
return t / 1e9;
|
120
138
|
}
|
121
139
|
|
122
140
|
inline VALUE backend_timeout_exception(VALUE exception) {
|
123
|
-
if (
|
141
|
+
if (rb_obj_is_kind_of(exception, rb_cArray) == Qtrue)
|
124
142
|
return rb_funcall(rb_ary_entry(exception, 0), ID_new, 1, rb_ary_entry(exception, 1));
|
125
|
-
else if (
|
143
|
+
else if (rb_obj_is_kind_of(exception, rb_cClass) == Qtrue)
|
126
144
|
return rb_funcall(exception, ID_new, 0);
|
127
145
|
else
|
128
146
|
return rb_funcall(rb_eRuntimeError, ID_new, 1, exception);
|
129
|
-
}
|
147
|
+
}
|
148
|
+
|
149
|
+
VALUE Backend_timeout_safe(VALUE arg) {
|
150
|
+
return rb_yield(arg);
|
151
|
+
}
|
152
|
+
|
153
|
+
VALUE Backend_timeout_rescue(VALUE arg, VALUE exception) {
|
154
|
+
return exception;
|
155
|
+
}
|
156
|
+
|
157
|
+
VALUE Backend_timeout_ensure_safe(VALUE arg) {
|
158
|
+
return rb_rescue2(Backend_timeout_safe, Qnil, Backend_timeout_rescue, Qnil, rb_eException, (VALUE)0);
|
159
|
+
}
|
160
|
+
|
161
|
+
static VALUE empty_string = Qnil;
|
162
|
+
|
163
|
+
VALUE Backend_sendv(VALUE self, VALUE io, VALUE ary, VALUE flags) {
|
164
|
+
switch (RARRAY_LEN(ary)) {
|
165
|
+
case 0:
|
166
|
+
return Qnil;
|
167
|
+
case 1:
|
168
|
+
return Backend_send(self, io, RARRAY_AREF(ary, 0), flags);
|
169
|
+
default:
|
170
|
+
if (empty_string == Qnil) {
|
171
|
+
empty_string = rb_str_new_literal("");
|
172
|
+
rb_global_variable(&empty_string);
|
173
|
+
}
|
174
|
+
VALUE joined = rb_ary_join(ary, empty_string);
|
175
|
+
VALUE result = Backend_send(self, io, joined, flags);
|
176
|
+
RB_GC_GUARD(joined);
|
177
|
+
return result;
|
178
|
+
}
|
179
|
+
}
|