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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a2fa4fa731ff52272d3aed456f7a26751e76c63b4335366919bffaf089bf96dd
4
- data.tar.gz: 1afe67258c5c73d9cc1082ca238eb358dac0f5b7a975f90a77b4f6de6a23b2a4
3
+ metadata.gz: 52f8ebf1104d921c9e3b0afa0b4605ee8f912eabe8b6264898f23905d2cb6c6f
4
+ data.tar.gz: 38f0f7cd62997ba5681185c3c4859f57d86de875d8292faf67fffa53c7160181
5
5
  SHA512:
6
- metadata.gz: 175edf315d759f85d2c0d934be36b9fcacd68342206d984ad64f800120a8b65668e9ea81595f47cbbb439402c256b47fde78f0cdad4b42e0c3b1d6ee321135b9
7
- data.tar.gz: 01cfe1d8b51d60a736adb80062eb22e7983a1879a5491cb7a2e47ec4ad5734e0c8bd8664653870a4393276b42fa912953721658ecff634521a672419c8d3cab6
6
+ metadata.gz: 2429259a2e79757ec879c4c00db8fd8b87302b9e0a61c6ae2ddd5fdb6e1a3a06ac63d551ed33a4c77480e156eb1247a0fab4a263179a60fd2e9a2d3173831a58
7
+ data.tar.gz: 04f58dafb5faf1e21b08fd85d508ceb6e9869b0a69d63c133661a834f169eb750b599639ffdbff474abf7f92bc6bfee3a43b9442c2885a6dd88328657b0e1d36
@@ -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
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- polyphony (0.45.2)
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.0.5)
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.0.5)
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
- 0.45.2
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).
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+ require 'polyphony'
5
+ require 'io/console'
6
+
7
+ c = STDIN.raw(min: 1, tim: 0, &:getbyte)
8
+ p result: c
9
+ exit
10
+
11
+ puts '?' * 40
12
+ c = STDIN.getbyte
13
+ puts '*' * 40
14
+ p c
@@ -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
@@ -13,7 +13,7 @@ def handle_client(socket)
13
13
  parser.on_message_complete = proc do |env|
14
14
  reqs << Object.new # parser
15
15
  end
16
- socket.read_loop |data|
16
+ socket.read_loop do |data|
17
17
  parser << data
18
18
  while (req = reqs.shift)
19
19
  handle_request(socket, req)
@@ -76,10 +76,10 @@ module Polyphony
76
76
  end
77
77
 
78
78
  def install_terminating_signal_handlers
79
- trap('SIGTERM', SystemExit)
79
+ trap('SIGTERM') { raise SystemExit }
80
80
  orig_trap('SIGINT') do
81
81
  orig_trap('SIGINT') { exit! }
82
- Thread.current.break_out_of_ev_loop(Thread.main.main_fiber, Interrupt.new)
82
+ Fiber.schedule_priority_oob_fiber { raise Interrupt }
83
83
  end
84
84
  end
85
85
 
@@ -24,9 +24,6 @@ module Polyphony
24
24
  def kill_and_await(sig, pid)
25
25
  ::Process.kill(sig, pid)
26
26
  Thread.current.backend.waitpid(pid)
27
- rescue SystemCallError
28
- # ignore
29
- puts 'SystemCallError in kill_and_await'
30
27
  end
31
28
  end
32
29
  end
@@ -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 spin a fiber that will run the signal handler code, then
157
- # call break_out_of_ev_loop, which will put the fiber at the front of the
158
- # run queue, then wake up the backend.
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
- Thread.current.break_out_of_ev_loop(Thread.main.main_fiber, exception)
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
- # def getbyte
94
- # end
93
+ alias_method :orig_getbyte, :getbyte
94
+ def getbyte
95
+ char = getc
96
+ char ? char.getbyte(0) : nil
97
+ end
95
98
 
96
- # def getc
97
- # end
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)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Polyphony
4
- VERSION = '0.45.2'
4
+ VERSION = '0.45.4'
5
5
  end
@@ -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.0.5'
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'
@@ -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
@@ -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
- spin do
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.2
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-08-03 00:00:00.000000000 Z
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.0.5
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.0.5
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