polyphony 0.53.2 → 0.58

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: 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
+ }