polyphony 0.45.2 → 0.45.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 +4 -4
- data/CHANGELOG.md +11 -0
- data/Gemfile.lock +3 -3
- data/TODO.md +9 -3
- data/examples/io/raw.rb +14 -0
- data/examples/io/reline.rb +18 -0
- data/examples/performance/thread-vs-fiber/polyphony_server.rb +1 -1
- data/lib/polyphony.rb +2 -2
- data/lib/polyphony/adapters/process.rb +0 -3
- data/lib/polyphony/extensions/core.rb +5 -18
- data/lib/polyphony/extensions/fiber.rb +20 -6
- data/lib/polyphony/extensions/io.rb +15 -4
- data/lib/polyphony/version.rb +1 -1
- data/polyphony.gemspec +1 -1
- data/test/test_io.rb +42 -0
- data/test/test_signal.rb +11 -8
- metadata +6 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 52f8ebf1104d921c9e3b0afa0b4605ee8f912eabe8b6264898f23905d2cb6c6f
|
4
|
+
data.tar.gz: 38f0f7cd62997ba5681185c3c4859f57d86de875d8292faf67fffa53c7160181
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2429259a2e79757ec879c4c00db8fd8b87302b9e0a61c6ae2ddd5fdb6e1a3a06ac63d551ed33a4c77480e156eb1247a0fab4a263179a60fd2e9a2d3173831a58
|
7
|
+
data.tar.gz: 04f58dafb5faf1e21b08fd85d508ceb6e9869b0a69d63c133661a834f169eb750b599639ffdbff474abf7f92bc6bfee3a43b9442c2885a6dd88328657b0e1d36
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,14 @@
|
|
1
|
+
## 0.45.4
|
2
|
+
|
3
|
+
* Improve signal trapping mechanism
|
4
|
+
|
5
|
+
## 0.45.3
|
6
|
+
|
7
|
+
* Don't swallow error in `Process#kill_and_await`
|
8
|
+
* Add `Fiber#mailbox` attribute reader
|
9
|
+
* Fix bug in `Fiber.await`
|
10
|
+
* Implement `IO#getc`, `IO#getbyte`
|
11
|
+
|
1
12
|
## 0.45.2
|
2
13
|
|
3
14
|
* Rewrite `Fiber#<<`, `Fiber#await`, `Fiber#receive` in C
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
polyphony (0.45.
|
4
|
+
polyphony (0.45.4)
|
5
5
|
|
6
6
|
GEM
|
7
7
|
remote: https://rubygems.org/
|
@@ -87,7 +87,7 @@ GEM
|
|
87
87
|
rack (2.2.3)
|
88
88
|
rainbow (3.0.0)
|
89
89
|
rake (12.3.3)
|
90
|
-
rake-compiler (1.
|
90
|
+
rake-compiler (1.1.1)
|
91
91
|
rake
|
92
92
|
rb-fsevent (0.10.3)
|
93
93
|
rb-inotify (0.10.1)
|
@@ -141,7 +141,7 @@ DEPENDENCIES
|
|
141
141
|
polyphony!
|
142
142
|
pry (= 0.13.1)
|
143
143
|
rack (>= 2.0.8, < 2.3.0)
|
144
|
-
rake-compiler (= 1.
|
144
|
+
rake-compiler (= 1.1.1)
|
145
145
|
redis (= 4.1.0)
|
146
146
|
rubocop (= 0.85.1)
|
147
147
|
sequel (= 5.34.0)
|
data/TODO.md
CHANGED
@@ -1,8 +1,14 @@
|
|
1
|
-
|
1
|
+
(
|
2
|
+
io_uring: some work has been done on an io_uring based scheduler here:
|
3
|
+
https://github.com/dsh0416/evt
|
4
|
+
|
5
|
+
This can serve as a starting point for doing stuff with io_uring
|
6
|
+
)
|
2
7
|
|
8
|
+
0.45.4
|
9
|
+
|
10
|
+
- Adapter for io/console (what does `IO#raw` do?)
|
3
11
|
- Adapter for Pry and IRB (Which fixes #5 and #6)
|
4
|
-
- Redesign signal handling - the current mechanism is problematic in that it
|
5
|
-
does not address signals that do not kill, for instance HUP or USR1.
|
6
12
|
- Improve `#supervise`. It does not work as advertised, and seems to exhibit an
|
7
13
|
inconsistent behaviour (see supervisor example).
|
8
14
|
- Fix backtrace for `Timeout.timeout` API (see timeout example).
|
data/examples/io/raw.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'polyphony'
|
5
|
+
require 'polyphony/adapters/readline'
|
6
|
+
require 'pry'
|
7
|
+
|
8
|
+
$counter = 0
|
9
|
+
timer = spin do
|
10
|
+
throttled_loop(5) do
|
11
|
+
$counter += 1
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
at_exit { timer.stop }
|
16
|
+
|
17
|
+
puts 'try typing $counter to see the counter incremented in the background'
|
18
|
+
binding.pry
|
data/lib/polyphony.rb
CHANGED
@@ -76,10 +76,10 @@ module Polyphony
|
|
76
76
|
end
|
77
77
|
|
78
78
|
def install_terminating_signal_handlers
|
79
|
-
trap('SIGTERM'
|
79
|
+
trap('SIGTERM') { raise SystemExit }
|
80
80
|
orig_trap('SIGINT') do
|
81
81
|
orig_trap('SIGINT') { exit! }
|
82
|
-
|
82
|
+
Fiber.schedule_priority_oob_fiber { raise Interrupt }
|
83
83
|
end
|
84
84
|
end
|
85
85
|
|
@@ -149,32 +149,19 @@ module ::Kernel
|
|
149
149
|
return orig_trap(sig, command) if command.is_a? String
|
150
150
|
|
151
151
|
block = command if !block && command.respond_to?(:call)
|
152
|
-
exception = signal_exception(block, command)
|
153
152
|
|
154
153
|
# The signal trap can be invoked at any time, including while the system
|
155
154
|
# backend is blocking while polling for events. In order to deal with this
|
156
|
-
# correctly, we
|
157
|
-
#
|
158
|
-
#
|
159
|
-
#
|
160
|
-
# If the command argument is an exception class however, it will be raised
|
161
|
-
# directly in the context of the main fiber.
|
155
|
+
# correctly, we run the signal handler code in an out-of-band, priority
|
156
|
+
# scheduled fiber, that will pass any uncaught exception (including
|
157
|
+
# SystemExit and Interrupt) to the main thread's main fiber. See also
|
158
|
+
# `Fiber#schedule_priority_oob_fiber`.
|
162
159
|
orig_trap(sig) do
|
163
|
-
|
160
|
+
Fiber.schedule_priority_oob_fiber(&block)
|
164
161
|
end
|
165
162
|
end
|
166
163
|
end
|
167
164
|
|
168
|
-
def signal_exception(block, command)
|
169
|
-
if block
|
170
|
-
Polyphony::Interjection.new(block)
|
171
|
-
elsif command.is_a?(Class)
|
172
|
-
command.new
|
173
|
-
else
|
174
|
-
raise ArgumentError, 'Must supply block or exception or callable object'
|
175
|
-
end
|
176
|
-
end
|
177
|
-
|
178
165
|
# Override Timeout to use cancel scope
|
179
166
|
module ::Timeout
|
180
167
|
def self.timeout(sec, klass = nil, message = nil, &block)
|
@@ -103,7 +103,7 @@ module Polyphony
|
|
103
103
|
suspend
|
104
104
|
fibers.map(&:result)
|
105
105
|
ensure
|
106
|
-
await_select_cleanup(state)
|
106
|
+
await_select_cleanup(state) if state
|
107
107
|
end
|
108
108
|
alias_method :join, :await
|
109
109
|
|
@@ -165,6 +165,20 @@ module Polyphony
|
|
165
165
|
state[:selected] = true
|
166
166
|
end
|
167
167
|
end
|
168
|
+
|
169
|
+
# Creates and schedules with priority an out-of-band fiber that runs the
|
170
|
+
# supplied block. If any uncaught exception is raised while the fiber is
|
171
|
+
# running, it will bubble up to the main thread's main fiber, which will
|
172
|
+
# also be scheduled with priority. This method is mainly used trapping
|
173
|
+
# signals (see also the patched `Kernel#trap`)
|
174
|
+
def schedule_priority_oob_fiber(&block)
|
175
|
+
f = Fiber.new do
|
176
|
+
block.call
|
177
|
+
rescue Exception => e
|
178
|
+
Thread.current.break_out_of_ev_loop(Thread.main.main_fiber, e)
|
179
|
+
end
|
180
|
+
Thread.current.break_out_of_ev_loop(f, nil)
|
181
|
+
end
|
168
182
|
end
|
169
183
|
|
170
184
|
# Methods for controlling child fibers
|
@@ -173,9 +187,9 @@ module Polyphony
|
|
173
187
|
(@children ||= {}).keys
|
174
188
|
end
|
175
189
|
|
176
|
-
def spin(tag = nil, orig_caller = Kernel.caller, &block)
|
190
|
+
def spin(tag = nil, orig_caller = Kernel.caller, do_schedule: true, &block)
|
177
191
|
f = Fiber.new { |v| f.run(v) }
|
178
|
-
f.prepare(tag, block, orig_caller, self)
|
192
|
+
f.prepare(tag, block, orig_caller, self, do_schedule: do_schedule)
|
179
193
|
(@children ||= {})[f] = true
|
180
194
|
f
|
181
195
|
end
|
@@ -213,14 +227,14 @@ module Polyphony
|
|
213
227
|
|
214
228
|
# Fiber life cycle methods
|
215
229
|
module FiberLifeCycle
|
216
|
-
def prepare(tag, block, caller, parent)
|
230
|
+
def prepare(tag, block, caller, parent, do_schedule: true)
|
217
231
|
@thread = Thread.current
|
218
232
|
@tag = tag
|
219
233
|
@parent = parent
|
220
234
|
@caller = caller
|
221
235
|
@block = block
|
222
236
|
__fiber_trace__(:fiber_create, self)
|
223
|
-
schedule
|
237
|
+
schedule if do_schedule
|
224
238
|
end
|
225
239
|
|
226
240
|
def run(first_value)
|
@@ -314,7 +328,7 @@ class ::Fiber
|
|
314
328
|
extend Polyphony::FiberControlClassMethods
|
315
329
|
|
316
330
|
attr_accessor :tag, :thread, :parent
|
317
|
-
attr_reader :result
|
331
|
+
attr_reader :result, :mailbox
|
318
332
|
|
319
333
|
def running?
|
320
334
|
@running
|
@@ -90,11 +90,22 @@ class ::IO
|
|
90
90
|
# def each_codepoint
|
91
91
|
# end
|
92
92
|
|
93
|
-
|
94
|
-
|
93
|
+
alias_method :orig_getbyte, :getbyte
|
94
|
+
def getbyte
|
95
|
+
char = getc
|
96
|
+
char ? char.getbyte(0) : nil
|
97
|
+
end
|
95
98
|
|
96
|
-
|
97
|
-
|
99
|
+
alias_method :orig_getc, :getc
|
100
|
+
def getc
|
101
|
+
return @read_buffer.slice!(0) if @read_buffer && !@read_buffer.empty?
|
102
|
+
|
103
|
+
@read_buffer ||= +''
|
104
|
+
Thread.current.backend.read(self, @read_buffer, 8192, false)
|
105
|
+
return @read_buffer.slice!(0) if !@read_buffer.empty?
|
106
|
+
|
107
|
+
nil
|
108
|
+
end
|
98
109
|
|
99
110
|
alias_method :orig_read, :read
|
100
111
|
def read(len = nil)
|
data/lib/polyphony/version.rb
CHANGED
data/polyphony.gemspec
CHANGED
@@ -21,7 +21,7 @@ Gem::Specification.new do |s|
|
|
21
21
|
s.require_paths = ["lib"]
|
22
22
|
s.required_ruby_version = '>= 2.6'
|
23
23
|
|
24
|
-
s.add_development_dependency 'rake-compiler', '1.
|
24
|
+
s.add_development_dependency 'rake-compiler', '1.1.1'
|
25
25
|
s.add_development_dependency 'minitest', '5.13.0'
|
26
26
|
s.add_development_dependency 'minitest-reporters', '1.4.2'
|
27
27
|
s.add_development_dependency 'simplecov', '0.17.1'
|
data/test/test_io.rb
CHANGED
@@ -92,6 +92,48 @@ class IOTest < MiniTest::Test
|
|
92
92
|
assert_raises(EOFError) { i.readpartial(1) }
|
93
93
|
end
|
94
94
|
|
95
|
+
def test_getc
|
96
|
+
i, o = IO.pipe
|
97
|
+
|
98
|
+
buf = []
|
99
|
+
f = spin do
|
100
|
+
while (c = i.getc)
|
101
|
+
buf << c
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
snooze
|
106
|
+
assert_equal [], buf
|
107
|
+
|
108
|
+
o << 'f'
|
109
|
+
snooze
|
110
|
+
o << 'g'
|
111
|
+
o.close
|
112
|
+
f.await
|
113
|
+
assert_equal ['f', 'g'], buf
|
114
|
+
end
|
115
|
+
|
116
|
+
def test_getbyte
|
117
|
+
i, o = IO.pipe
|
118
|
+
|
119
|
+
buf = []
|
120
|
+
f = spin do
|
121
|
+
while (b = i.getbyte)
|
122
|
+
buf << b
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
snooze
|
127
|
+
assert_equal [], buf
|
128
|
+
|
129
|
+
o << 'f'
|
130
|
+
snooze
|
131
|
+
o << 'g'
|
132
|
+
o.close
|
133
|
+
f.await
|
134
|
+
assert_equal [102, 103], buf
|
135
|
+
end
|
136
|
+
|
95
137
|
# see https://github.com/digital-fabric/polyphony/issues/30
|
96
138
|
def test_reopened_tempfile
|
97
139
|
file = Tempfile.new
|
data/test/test_signal.rb
CHANGED
@@ -3,18 +3,21 @@
|
|
3
3
|
require_relative 'helper'
|
4
4
|
|
5
5
|
class SignalTrapTest < Minitest::Test
|
6
|
+
def test_int_signal
|
7
|
+
Thread.new { sleep 0.001; Process.kill('INT', Process.pid) }
|
8
|
+
assert_raises(Interrupt) { sleep 5 }
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_term_signal
|
12
|
+
Thread.new { sleep 0.001; Process.kill('TERM', Process.pid) }
|
13
|
+
assert_raises(SystemExit) { sleep 5 }
|
14
|
+
end
|
15
|
+
|
6
16
|
def test_signal_exception_handling
|
7
17
|
i, o = IO.pipe
|
8
18
|
pid = Polyphony.fork do
|
9
19
|
i.close
|
10
|
-
|
11
|
-
spin do
|
12
|
-
sleep 5
|
13
|
-
rescue ::Interrupt => e
|
14
|
-
# the signal should be raised only in the main fiber
|
15
|
-
o.puts "1-interrupt"
|
16
|
-
end.await
|
17
|
-
end.await
|
20
|
+
sleep 5
|
18
21
|
rescue ::Interrupt => e
|
19
22
|
o.puts "3-interrupt"
|
20
23
|
ensure
|
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: 0.45.
|
4
|
+
version: 0.45.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: 2020-
|
11
|
+
date: 2020-09-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake-compiler
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - '='
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 1.
|
19
|
+
version: 1.1.1
|
20
20
|
type: :development
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - '='
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 1.
|
26
|
+
version: 1.1.1
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: minitest
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -377,6 +377,8 @@ files:
|
|
377
377
|
- examples/io/open.rb
|
378
378
|
- examples/io/pry.rb
|
379
379
|
- examples/io/rack_server.rb
|
380
|
+
- examples/io/raw.rb
|
381
|
+
- examples/io/reline.rb
|
380
382
|
- examples/io/system.rb
|
381
383
|
- examples/io/tcpserver.rb
|
382
384
|
- examples/io/tcpsocket.rb
|