polyphony 0.53.2 → 0.58

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d656c0aad868a468632949355b617821b6a66aa4da69a994ea7afbc4af46e97b
4
- data.tar.gz: edc45b184b0b50940925a1a1db56b0106ded10906ca181513e38450bc320e828
3
+ metadata.gz: e8002b8c0e03afa8e915b5ed67d64c1fc288a5fa220dfb2c32d3966a78046eee
4
+ data.tar.gz: c3a46eeae1f048f4adee9d3130f5dd3593936e843c1874d928f742d1fa83053f
5
5
  SHA512:
6
- metadata.gz: f346672c82eee4055ec6c8f7fc1103850fd622a031ebf1c096e9ab130e3aa0b37c559d2b2f953f6b1bd721e4421199800fc0de54993a0c802ddadfa873f35030
7
- data.tar.gz: 8273d691c2ed19ca04207380ea99392c7f65fefe6600c4a9bf9cc20a802f41e34b471e3af8d5adccf29c783c2f0c17610896fae2c183ac26b601c34dc27a5ce2
6
+ metadata.gz: e6681155761daee35b73c9674e69aa505786e576814db0c909028cb576d8609f99bccc79f80b7d06d90c7658747bc2589c9837d3bdb3419782147e573d39f278
7
+ data.tar.gz: 90197a8db54405ca37492a54a20adf00c85f142af606cad26ad823161825ccf6bd9a414adcbbf3e27eaf93eea1d94a7cbc50a2fb5982ee4ce48969d4378400cd
@@ -26,6 +26,6 @@ jobs:
26
26
  - name: Show Linux kernel version
27
27
  run: uname -r
28
28
  - name: Compile C-extension
29
- run: bundle exec rake compile
29
+ run: POLYPHONY_USE_LIBEV=1 bundle exec rake compile
30
30
  - name: Run tests
31
31
  run: bundle exec rake test
data/.gitignore CHANGED
@@ -56,4 +56,6 @@ lib/*.bundle
56
56
  lib/*.so
57
57
 
58
58
  _site
59
- .sass-cache
59
+ .sass-cache
60
+
61
+ log
data/CHANGELOG.md CHANGED
@@ -1,12 +1,43 @@
1
- ## 0.53.2
1
+ ## 0.58 2021-06-25
2
+
3
+ - Implement `Thread#idle_gc_period`, `#on_idle` (#56)
4
+ - Implement `Backend#idle_block=` (#56)
5
+
6
+ ## 0.57.0 2021-06-23
7
+
8
+ - Implement `Backend#splice_chunks` method for both libev and io_uring backends
9
+ - Improve waiting for readiness in libev `Backend#splice`, `#splice_to_eof`
10
+ - Enable splice op in libev `Backend#chain` for non-Linux OS
11
+
12
+ ## 0.56.0 2021-06-22
13
+
14
+ - Implement fake `Backend#splice`, `Backend#splice_to_eof` methods for non-Linux
15
+ OS
16
+
17
+ ## 0.55.0 2021-06-17
18
+
19
+ - Finish io_uring implementation of Backend#chain
20
+ - Reimplement io_uring op_context acquire/release algorithm (using ref count)
21
+ - Fix #gets on sockets
22
+ - Redesign event anti-starvation mechanism
23
+
24
+ ## 0.54.0 2021-06-14
25
+
26
+ - Implement Mutex#owned?, #locked? (#50)
27
+ - Fix arity for SSLSocket#peeraddr (#55)
28
+ - Add missing SSLServer#accept_loop method (#53)
29
+ - Fix SSLSocket buffering behaviour
30
+ - Add recv_loop alias for SSLSocket (#54)
31
+
32
+ ## 0.53.2 2021-05-10
2
33
 
3
34
  - Remove `splice` methods on libev backend on non-Linux OS (#43)
4
35
 
5
- ## 0.53.0
36
+ ## 0.53.0 2021-04-23
6
37
 
7
38
  - Implement `Backend#splice`, `Backend#splice_to_eof`, along with `IO#splice`, `IO#splice_to_eof`
8
39
 
9
- ## 0.52.0
40
+ ## 0.52.0 2021-02-28
10
41
 
11
42
  - Polyphony is now compatible with Ruby 3.0
12
43
  - Add `Backend#sendv` method for sending multiple strings
@@ -16,19 +47,19 @@
16
47
  - libev backend: Use` pidfd_open` for Linux 5.3+, otherwise use a libev child watcher
17
48
  - Use `:call` as default method in `#feed_loop`
18
49
 
19
- ## 0.51.0
50
+ ## 0.51.0 2021-02-02
20
51
 
21
52
  - Implement `IO#feed_loop`, `Socket#feed_loop`
22
53
  - Fix error handling in `Process.kill_and_await`
23
54
 
24
- ## 0.50.1
55
+ ## 0.50.1 2021-01-31
25
56
 
26
57
  - Set `IOSQE_ASYNC` flag in io_uring backend
27
58
  - Fix error handling in `Backend#waitpid`
28
59
  - Reimplement libev backend's `#waitpid` by using pidfd_open (in similar manner
29
60
  to the io_uring backend)
30
61
 
31
- ## 0.50.0
62
+ ## 0.50.0 2021-01-28
32
63
 
33
64
  - Use `Process::CLOCK_MONOTONIC` in Timer
34
65
  - Add `Timer#sleep`, `Timer#after`, `Timer#every`
@@ -36,50 +67,50 @@
36
67
  - Add `Thread#fiber_index_of` method
37
68
  - Use `Backend#wait_event` in `Fiber#await`
38
69
 
39
- ## 0.49.2
70
+ ## 0.49.2 2021-01-19
40
71
 
41
72
  - Fix hang with 100s or more child fibers when terminating
42
73
  - Fix double pending_count increment in io_uring backend
43
74
 
44
- ## 0.49.1
75
+ ## 0.49.1 2021-01-13
45
76
 
46
77
  - Use `TCPSocket` instead of `Socket` in `Net.tcp_connect`
47
78
  - Catch `Errno::ERSCH` in `Process.kill_and_await`
48
79
  - Set io_uring queue size to 2048
49
80
 
50
- ## 0.49.0
81
+ ## 0.49.0 2021-01-11
51
82
 
52
83
  - Implement `Polyphony::Timer` for performant timeouts
53
84
 
54
- ## 0.48.0
85
+ ## 0.48.0 2021-01-05
55
86
 
56
87
  - Implement graceful shutdown
57
88
  - Add support for `break` / `StopIteration` in `spin_loop`
58
89
  - Fix `IO#gets`, `IO#readpartial`
59
90
 
60
- ## 0.47.5.1
91
+ ## 0.47.5.1 2020-11-20
61
92
 
62
93
  - Add missing `Socket#accept_loop` method
63
94
 
64
- ## 0.47.5
95
+ ## 0.47.5 2020-11-20
65
96
 
66
97
  - Add `socket_class` argument to `Backend#accept`, `Backend#accept_loop`
67
98
  - Fix `#supervise` to stop when all children fibers are done
68
99
 
69
- ## 0.47.4
100
+ ## 0.47.4 2020-11-14
70
101
 
71
102
  - Add support for Unix sockets
72
103
 
73
- ## 0.47.3
104
+ ## 0.47.3 2020-11-12
74
105
 
75
106
  - Enable I/O in signal handlers (#45)
76
107
  - Accept `:interval` argument in `#spin_loop`
77
108
 
78
- ## 0.47.2
109
+ ## 0.47.2 2020-11-10
79
110
 
80
111
  - Fix API compatibility between TCPSocket and IO
81
112
 
82
- ## 0.47.0
113
+ ## 0.47.0 2020-11-10
83
114
 
84
115
  - Implement `#spin_scope` used for creating blocking fiber scopes
85
116
  - Reimplement `move_on_after`, `cancel_after`, `Timeout.timeout` using
@@ -87,18 +118,18 @@
87
118
  - Implement `Backend#timeout` API
88
119
  - Implemented capped queues
89
120
 
90
- ## 0.46.1
121
+ ## 0.46.1 2020-11-04
91
122
 
92
123
  - Add `TCPServer#accept_loop`, `OpenSSL::SSL::SSLSocket#accept_loop` method
93
124
  - Fix compilation error on MacOS (#43)
94
125
  - Fix backtrace for `Timeout.timeout`
95
126
  - Add `Backend#timer_loop`
96
127
 
97
- ## 0.46.0
128
+ ## 0.46.0 2020-10-08
98
129
 
99
130
  - Implement [io_uring backend](https://github.com/digital-fabric/polyphony/pull/44)
100
131
 
101
- ## 0.45.5
132
+ ## 0.45.5 2020-10-04
102
133
 
103
134
  - Fix compilation error (#43)
104
135
  - Add support for resetting move_on_after, cancel_after timeouts
@@ -107,22 +138,22 @@
107
138
  - Schedule parent with priority on uncaught exception
108
139
  - Fix race condition in `Mutex#synchronize` (#41)
109
140
 
110
- ## 0.45.4
141
+ ## 0.45.4 2020-09-06
111
142
 
112
143
  - Improve signal trapping mechanism
113
144
 
114
- ## 0.45.3
145
+ ## 0.45.3 2020-09-02
115
146
 
116
147
  - Don't swallow error in `Process#kill_and_await`
117
148
  - Add `Fiber#mailbox` attribute reader
118
149
  - Fix bug in `Fiber.await`
119
150
  - Implement `IO#getc`, `IO#getbyte`
120
151
 
121
- ## 0.45.2
152
+ ## 0.45.2 2020-08-03
122
153
 
123
154
  - Rewrite `Fiber#<<`, `Fiber#await`, `Fiber#receive` in C
124
155
 
125
- ## 0.45.1
156
+ ## 0.45.1 2020-08-01
126
157
 
127
158
  - Fix Net::HTTP compatibility
128
159
  - Fix fs adapter
@@ -132,7 +163,7 @@
132
163
  - Cleanup code
133
164
  - Improve support for Ruby 3 keyword args
134
165
 
135
- ## 0.45.0
166
+ ## 0.45.0 2020-07-29
136
167
 
137
168
  - Cleanup code
138
169
  - Rename `Agent` to `Backend`
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- polyphony (0.53.2)
4
+ polyphony (0.58)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
@@ -17,6 +17,7 @@ GEM
17
17
  mime-types (~> 3.0)
18
18
  multi_xml (>= 0.5.2)
19
19
  json (2.3.0)
20
+ localhost (1.1.8)
20
21
  method_source (1.0.0)
21
22
  mime-types (3.3.1)
22
23
  mime-types-data (~> 3.2015)
@@ -72,6 +73,7 @@ DEPENDENCIES
72
73
  hiredis (= 0.6.3)
73
74
  http_parser.rb (~> 0.6.0)
74
75
  httparty (= 0.17.1)
76
+ localhost (~> 1.1.4)
75
77
  minitest (= 5.14.4)
76
78
  minitest-reporters (= 1.4.2)
77
79
  msgpack (= 1.4.2)
data/TODO.md CHANGED
@@ -10,9 +10,6 @@
10
10
 
11
11
  - Add support for `break` and `StopIteration` in all loops (with tests)
12
12
 
13
- - Change `IO#gets` to use `String#split` to cut into lines, much faster (see
14
- examples/performance/line_splitting.rb)
15
-
16
13
  - More tight loops
17
14
  - `IO#gets_loop`, `Socket#gets_loop`, `OpenSSL::Socket#gets_loop` (medium effort)
18
15
  - `Fiber#receive_loop` (very little effort, should be implemented in C)
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+
5
+ require 'polyphony'
6
+
7
+ GC.disable
8
+
9
+ p count: GC.count
10
+ snooze
11
+ p count_after_snooze: GC.count
12
+ sleep 0.1
13
+ p count_after_sleep: GC.count
14
+
15
+ Thread.current.backend.idle_gc_period = 60
16
+
17
+ p count: GC.count
18
+ snooze
19
+ p count_after_snooze: GC.count
20
+ sleep 0.1
21
+ p count_after_sleep: GC.count
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+ require 'polyphony'
5
+
6
+ queue = Queue.new
7
+
8
+ 4.times { |i|
9
+ spin_loop {
10
+ job = queue.pop
11
+ puts("worker %d job %s" % [i, job.inspect])
12
+ }
13
+ }
14
+
15
+ (1..10).each do |i|
16
+ queue << "job#{i}"
17
+ end
18
+
19
+ sleep 0.1 until queue.empty?
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+ require 'polyphony'
5
+ require 'localhost/authority'
6
+
7
+ authority = Localhost::Authority.fetch
8
+ opts = {
9
+ reuse_addr: true,
10
+ dont_linger: true,
11
+ secure_context: authority.server_context
12
+ }
13
+
14
+ server = Polyphony::Net.tcp_listen('localhost', 1234, opts)
15
+
16
+ puts 'Serving HTTPS on port 1234'
17
+
18
+ spin_loop(interval: 1) { STDOUT << '.' }
19
+
20
+ # server.accept_loop do |socket|
21
+ while (socket = server.accept)
22
+ spin do
23
+ while (data = socket.gets("\n", 8192))
24
+ if data.chomp.empty?
25
+ socket << "HTTP/1.1 200 OK\nConnection: close\nContent-Length: 4\n\nfoo\n"
26
+ break
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+ require 'polyphony'
5
+
6
+ i, o = IO.pipe
7
+ f = spin { p i.read }
8
+
9
+ o << 'hello'
10
+ o.close
11
+ f.await
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+ require 'polyphony'
5
+
6
+ i, o = IO.pipe
7
+
8
+ f = spin do
9
+ i.read_loop { |data| STDOUT << data }
10
+ end
11
+
12
+ result = nil
13
+ # File.open(__FILE__, 'r') do |f|
14
+ File.open('../tipi/log', 'r') do |f|
15
+ result = Thread.current.backend.splice_chunks(
16
+ f,
17
+ o,
18
+ "Content-Type: ruby\n\n",
19
+ "0\r\n\r\n",
20
+ ->(len) { "#{len.to_s(16)}\r\n" },
21
+ "\r\n",
22
+ 16384
23
+ )
24
+ end
25
+
26
+
27
+ o.close
28
+ f.await
29
+ p result: result
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+ require 'polyphony'
5
+
6
+ puts 'Please enter your name:'
7
+ name = gets.chomp
8
+ puts "Hello, #{name}!"
@@ -0,0 +1,197 @@
1
+ #include <time.h>
2
+ #include <fcntl.h>
3
+ #include "ruby.h"
4
+ #include "ruby/io.h"
5
+ #include "polyphony.h"
6
+ #include "backend_common.h"
7
+
8
+ inline void initialize_backend_base(struct Backend_base *base) {
9
+ base->currently_polling = 0;
10
+ base->pending_count = 0;
11
+ base->idle_gc_period = 0;
12
+ base->idle_gc_last_time = 0;
13
+ base->idle_block = Qnil;
14
+ }
15
+
16
+ #ifdef POLYPHONY_USE_PIDFD_OPEN
17
+ #ifndef __NR_pidfd_open
18
+ #define __NR_pidfd_open 434 /* System call # on most architectures */
19
+ #endif
20
+
21
+ inline int pidfd_open(pid_t pid, unsigned int flags) {
22
+ return syscall(__NR_pidfd_open, pid, flags);
23
+ }
24
+ #endif
25
+
26
+ //////////////////////////////////////////////////////////////////////
27
+ //////////////////////////////////////////////////////////////////////
28
+ // the following is copied verbatim from the Ruby source code (io.c)
29
+
30
+ inline int io_setstrbuf(VALUE *str, long len) {
31
+ #ifdef _WIN32
32
+ len = (len + 1) & ~1L; /* round up for wide char */
33
+ #endif
34
+ if (*str == Qnil) {
35
+ *str = rb_str_new(0, len);
36
+ return 1;
37
+ }
38
+ else {
39
+ VALUE s = StringValue(*str);
40
+ long clen = RSTRING_LEN(s);
41
+ if (clen >= len) {
42
+ rb_str_modify(s);
43
+ return 0;
44
+ }
45
+ len -= clen;
46
+ }
47
+ rb_str_modify_expand(*str, len);
48
+ return 0;
49
+ }
50
+
51
+ #define MAX_REALLOC_GAP 4096
52
+
53
+ inline void io_shrink_read_string(VALUE str, long n) {
54
+ if (rb_str_capacity(str) - n > MAX_REALLOC_GAP) {
55
+ rb_str_resize(str, n);
56
+ }
57
+ }
58
+
59
+ inline void io_set_read_length(VALUE str, long n, int shrinkable) {
60
+ if (RSTRING_LEN(str) != n) {
61
+ rb_str_modify(str);
62
+ rb_str_set_len(str, n);
63
+ if (shrinkable) io_shrink_read_string(str, n);
64
+ }
65
+ }
66
+
67
+ inline rb_encoding* io_read_encoding(rb_io_t *fptr) {
68
+ if (fptr->encs.enc) {
69
+ return fptr->encs.enc;
70
+ }
71
+ return rb_default_external_encoding();
72
+ }
73
+
74
+ inline VALUE io_enc_str(VALUE str, rb_io_t *fptr) {
75
+ OBJ_TAINT(str);
76
+ rb_enc_associate(str, io_read_encoding(fptr));
77
+ return str;
78
+ }
79
+
80
+ //////////////////////////////////////////////////////////////////////
81
+ //////////////////////////////////////////////////////////////////////
82
+
83
+ VALUE backend_await(struct Backend_base *backend) {
84
+ VALUE ret;
85
+ backend->pending_count++;
86
+ ret = Thread_switch_fiber(rb_thread_current());
87
+ backend->pending_count--;
88
+ RB_GC_GUARD(ret);
89
+ return ret;
90
+ }
91
+
92
+ VALUE backend_snooze() {
93
+ Fiber_make_runnable(rb_fiber_current(), Qnil);
94
+ VALUE ret = Thread_switch_fiber(rb_thread_current());
95
+ return ret;
96
+ }
97
+
98
+ inline void rectify_io_file_pos(rb_io_t *fptr) {
99
+ // Apparently after reopening a closed file, the file position is not reset,
100
+ // which causes the read to fail. Fortunately we can use fptr->rbuf.len to
101
+ // find out if that's the case.
102
+ // See: https://github.com/digital-fabric/polyphony/issues/30
103
+ if (fptr->rbuf.len > 0) {
104
+ lseek(fptr->fd, -fptr->rbuf.len, SEEK_CUR);
105
+ fptr->rbuf.len = 0;
106
+ }
107
+ }
108
+
109
+ inline double current_time() {
110
+ struct timespec ts;
111
+ clock_gettime(CLOCK_MONOTONIC, &ts);
112
+ long long ns = ts.tv_sec;
113
+ ns = ns * 1e9 + ts.tv_nsec;
114
+ double t = ns;
115
+ return t / 1e9;
116
+ }
117
+
118
+ inline VALUE backend_timeout_exception(VALUE exception) {
119
+ if (rb_obj_is_kind_of(exception, rb_cArray) == Qtrue)
120
+ return rb_funcall(rb_ary_entry(exception, 0), ID_new, 1, rb_ary_entry(exception, 1));
121
+ else if (rb_obj_is_kind_of(exception, rb_cClass) == Qtrue)
122
+ return rb_funcall(exception, ID_new, 0);
123
+ else
124
+ return rb_funcall(rb_eRuntimeError, ID_new, 1, exception);
125
+ }
126
+
127
+ VALUE Backend_timeout_safe(VALUE arg) {
128
+ return rb_yield(arg);
129
+ }
130
+
131
+ VALUE Backend_timeout_rescue(VALUE arg, VALUE exception) {
132
+ return exception;
133
+ }
134
+
135
+ VALUE Backend_timeout_ensure_safe(VALUE arg) {
136
+ return rb_rescue2(Backend_timeout_safe, Qnil, Backend_timeout_rescue, Qnil, rb_eException, (VALUE)0);
137
+ }
138
+
139
+ static VALUE empty_string = Qnil;
140
+
141
+ VALUE Backend_sendv(VALUE self, VALUE io, VALUE ary, VALUE flags) {
142
+ switch (RARRAY_LEN(ary)) {
143
+ case 0:
144
+ return Qnil;
145
+ case 1:
146
+ return Backend_send(self, io, RARRAY_AREF(ary, 0), flags);
147
+ default:
148
+ if (empty_string == Qnil) {
149
+ empty_string = rb_str_new_literal("");
150
+ rb_global_variable(&empty_string);
151
+ }
152
+ VALUE joined = rb_ary_join(ary, empty_string);
153
+ VALUE result = Backend_send(self, io, joined, flags);
154
+ RB_GC_GUARD(joined);
155
+ return result;
156
+ }
157
+ }
158
+
159
+ inline void io_verify_blocking_mode(rb_io_t *fptr, VALUE io, VALUE blocking) {
160
+ VALUE blocking_mode = rb_ivar_get(io, ID_ivar_blocking_mode);
161
+ if (blocking == blocking_mode) return;
162
+
163
+ rb_ivar_set(io, ID_ivar_blocking_mode, blocking);
164
+
165
+ #ifdef _WIN32
166
+ if (blocking != Qtrue)
167
+ rb_w32_set_nonblock(fptr->fd);
168
+ #elif defined(F_GETFL)
169
+ int flags = fcntl(fptr->fd, F_GETFL);
170
+ if (flags == -1) return;
171
+ int is_nonblocking = flags & O_NONBLOCK;
172
+
173
+ if (blocking == Qtrue) {
174
+ if (!is_nonblocking) return;
175
+ flags &= ~O_NONBLOCK;
176
+ } else {
177
+ if (is_nonblocking) return;
178
+ flags |= O_NONBLOCK;
179
+ }
180
+ fcntl(fptr->fd, F_SETFL, flags);
181
+ #endif
182
+ }
183
+
184
+ inline void backend_run_idle_tasks(struct Backend_base *base) {
185
+ if (base->idle_block != Qnil)
186
+ rb_funcall(base->idle_block, ID_call, 0);
187
+
188
+ if (base->idle_gc_period == 0) return;
189
+
190
+ double now = current_time();
191
+ if (now - base->idle_gc_last_time < base->idle_gc_period) return;
192
+
193
+ base->idle_gc_last_time = now;
194
+ rb_gc_enable();
195
+ rb_gc_start();
196
+ rb_gc_disable();
197
+ }