polyphony 0.70 → 0.73.1

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.
Files changed (78) hide show
  1. checksums.yaml +4 -4
  2. data/.github/FUNDING.yml +1 -0
  3. data/.github/workflows/test.yml +3 -2
  4. data/.gitignore +3 -1
  5. data/CHANGELOG.md +32 -4
  6. data/Gemfile.lock +11 -11
  7. data/TODO.md +1 -1
  8. data/bin/pdbg +1 -1
  9. data/bin/polyphony-debug +0 -0
  10. data/bin/stress.rb +0 -0
  11. data/bin/test +0 -0
  12. data/docs/_user-guide/all-about-timers.md +1 -1
  13. data/docs/api-reference/exception.md +5 -1
  14. data/docs/api-reference/fiber.md +2 -2
  15. data/docs/faq.md +1 -1
  16. data/docs/getting-started/overview.md +8 -8
  17. data/docs/getting-started/tutorial.md +3 -3
  18. data/docs/main-concepts/concurrency.md +1 -1
  19. data/docs/main-concepts/extending.md +3 -3
  20. data/docs/main-concepts/fiber-scheduling.md +1 -1
  21. data/examples/core/calc.rb +37 -0
  22. data/examples/core/calc_with_restart.rb +40 -0
  23. data/examples/core/calc_with_supervise.rb +37 -0
  24. data/examples/core/message_based_supervision.rb +1 -1
  25. data/examples/core/ring.rb +29 -0
  26. data/examples/io/rack_server.rb +1 -1
  27. data/examples/io/tunnel.rb +1 -1
  28. data/examples/performance/fiber_transfer.rb +1 -1
  29. data/examples/performance/line_splitting.rb +1 -1
  30. data/examples/performance/thread-vs-fiber/compare.rb +1 -1
  31. data/ext/polyphony/backend_common.c +15 -9
  32. data/ext/polyphony/backend_common.h +1 -1
  33. data/ext/polyphony/backend_io_uring.c +56 -64
  34. data/ext/polyphony/backend_io_uring_context.c +1 -1
  35. data/ext/polyphony/backend_io_uring_context.h +1 -1
  36. data/ext/polyphony/backend_libev.c +36 -30
  37. data/ext/polyphony/extconf.rb +25 -13
  38. data/ext/polyphony/polyphony.h +5 -1
  39. data/ext/polyphony/queue.c +2 -2
  40. data/ext/polyphony/runqueue_ring_buffer.c +3 -2
  41. data/ext/polyphony/thread.c +1 -1
  42. data/lib/polyphony/adapters/irb.rb +11 -1
  43. data/lib/polyphony/{extensions → core}/debug.rb +0 -0
  44. data/lib/polyphony/core/global_api.rb +3 -6
  45. data/lib/polyphony/core/timer.rb +2 -2
  46. data/lib/polyphony/debugger.rb +3 -3
  47. data/lib/polyphony/extensions/exception.rb +45 -0
  48. data/lib/polyphony/extensions/fiber.rb +27 -9
  49. data/lib/polyphony/extensions/io.rb +2 -2
  50. data/lib/polyphony/extensions/{core.rb → kernel.rb} +0 -73
  51. data/lib/polyphony/extensions/openssl.rb +20 -5
  52. data/lib/polyphony/extensions/process.rb +19 -0
  53. data/lib/polyphony/extensions/socket.rb +3 -4
  54. data/lib/polyphony/extensions/timeout.rb +10 -0
  55. data/lib/polyphony/extensions.rb +9 -0
  56. data/lib/polyphony/version.rb +1 -1
  57. data/lib/polyphony.rb +2 -5
  58. data/polyphony.gemspec +1 -1
  59. data/test/coverage.rb +2 -2
  60. data/test/stress.rb +1 -1
  61. data/test/test_backend.rb +12 -12
  62. data/test/test_event.rb +1 -1
  63. data/test/test_ext.rb +1 -1
  64. data/test/test_fiber.rb +52 -7
  65. data/test/test_global_api.rb +16 -3
  66. data/test/test_io.rb +3 -3
  67. data/test/test_process_supervision.rb +1 -1
  68. data/test/test_queue.rb +6 -6
  69. data/test/test_signal.rb +20 -1
  70. data/test/test_socket.rb +12 -10
  71. data/test/test_supervise.rb +85 -0
  72. data/test/test_sync.rb +2 -2
  73. data/test/test_thread.rb +22 -2
  74. data/test/test_thread_pool.rb +1 -1
  75. data/test/test_throttler.rb +1 -1
  76. data/test/test_timer.rb +2 -2
  77. data/test/test_trace.rb +1 -1
  78. metadata +13 -4
@@ -3,32 +3,43 @@
3
3
  require 'rubygems'
4
4
  require 'mkmf'
5
5
 
6
- use_liburing = false
7
- use_pidfd_open = false
8
- force_use_libev = ENV['POLYPHONY_USE_LIBEV'] != nil
9
- linux = RUBY_PLATFORM =~ /linux/
10
-
11
- if linux && `uname -sr` =~ /Linux 5\.(\d+)/
12
- kernel_minor_version = $1.to_i
13
- use_liburing = !force_use_libev && kernel_minor_version >= 6
14
- use_pidfd_open = kernel_minor_version >= 3
6
+
7
+ KERNEL_INFO_RE = /Linux (\d)\.(\d+)\.(?:\d+)\-(?:\d+\-)?(\w+)/
8
+ def get_config
9
+ config = { linux: !!(RUBY_PLATFORM =~ /linux/) }
10
+ return config if !config[:linux]
11
+
12
+ kernel_info = `uname -sr`
13
+ m = kernel_info.match(KERNEL_INFO_RE)
14
+ raise "Could not parse Linux kernel information (#{kernel_info.inspect})" if !m
15
+
16
+ version, major_revision, distribution = m[1].to_i, m[2].to_i, m[3]
17
+ config[:pidfd_open] = (version == 5) && (major_revision >= 3)
18
+
19
+ force_libev = ENV['POLYPHONY_USE_LIBEV'] != nil
20
+ config[:io_uring] = !force_libev &&
21
+ (version == 5) && (major_revision >= 6) && (distribution != 'linuxkit')
22
+ config
15
23
  end
16
24
 
17
- $defs << '-DPOLYPHONY_USE_PIDFD_OPEN' if use_pidfd_open
18
- if use_liburing
25
+ config = get_config
26
+ puts "Building Polyphony... (#{config.inspect})"
27
+
28
+ $defs << '-DPOLYPHONY_USE_PIDFD_OPEN' if config[:pidfd_open]
29
+ if config[:io_uring]
19
30
  $defs << "-DPOLYPHONY_BACKEND_LIBURING"
20
31
  $defs << "-DPOLYPHONY_UNSET_NONBLOCK" if RUBY_VERSION =~ /^3/
21
32
  $CFLAGS << " -Wno-pointer-arith"
22
33
  else
23
34
  $defs << "-DPOLYPHONY_BACKEND_LIBEV"
24
- $defs << "-DPOLYPHONY_LINUX" if linux
35
+ $defs << "-DPOLYPHONY_LINUX" if config[:linux]
25
36
  $defs << '-DEV_USE_LINUXAIO' if have_header('linux/aio_abi.h')
26
37
  $defs << '-DEV_USE_SELECT' if have_header('sys/select.h')
27
38
  $defs << '-DEV_USE_POLL' if have_type('port_event_t', 'poll.h')
28
39
  $defs << '-DEV_USE_EPOLL' if have_header('sys/epoll.h')
29
40
  $defs << '-DEV_USE_KQUEUE' if have_header('sys/event.h') && have_header('sys/queue.h')
30
41
  $defs << '-DEV_USE_PORT' if have_type('port_event_t', 'port.h')
31
- $defs << '-DHAVE_SYS_RESOURCE_H' if have_header('sys/resource.h')
42
+ $defs << '-DHAVE_SYS_RESOURCE_H' if have_header('sys/resource.h')
32
43
 
33
44
  $CFLAGS << " -Wno-comment"
34
45
  $CFLAGS << " -Wno-unused-result"
@@ -40,6 +51,7 @@ $defs << '-DPOLYPHONY_PLAYGROUND' if ENV['POLYPHONY_PLAYGROUND']
40
51
 
41
52
  CONFIG['optflags'] << ' -fno-strict-aliasing' unless RUBY_PLATFORM =~ /mswin/
42
53
 
54
+ have_func('rb_fiber_transfer', 'ruby.h')
43
55
 
44
56
  dir_config 'polyphony_ext'
45
57
  create_makefile 'polyphony_ext'
@@ -26,7 +26,11 @@
26
26
  #define RAISE_IF_NOT_NIL(ret) if (ret != Qnil) { RAISE_EXCEPTION(ret); }
27
27
 
28
28
  // Fiber#transfer
29
- #define FIBER_TRANSFER(fiber, value) rb_funcall(fiber, ID_transfer, 1, value)
29
+ #if HAVE_RB_FIBER_TRANSFER
30
+ #define FIBER_TRANSFER(fiber, value) rb_fiber_transfer(fiber, 1, &value)
31
+ #else
32
+ #define FIBER_TRANSFER(fiber, value) rb_funcall(fiber, ID_transfer, 1, value)
33
+ #endif
30
34
 
31
35
  #define BACKEND() (rb_ivar_get(rb_thread_current(), ID_ivar_backend))
32
36
 
@@ -162,12 +162,12 @@ VALUE Queue_cap(VALUE self, VALUE cap) {
162
162
  Queue_t *queue;
163
163
  GetQueue(self, queue);
164
164
  queue->capacity = new_capacity;
165
-
165
+
166
166
  if (queue->capacity)
167
167
  queue_schedule_blocked_fibers_to_capacity(queue);
168
168
  else
169
169
  queue_schedule_all_blocked_fibers(&queue->push_queue);
170
-
170
+
171
171
  return self;
172
172
  }
173
173
 
@@ -61,8 +61,9 @@ inline void runqueue_ring_buffer_push(runqueue_ring_buffer *buffer, VALUE fiber,
61
61
 
62
62
  inline void runqueue_ring_buffer_mark(runqueue_ring_buffer *buffer) {
63
63
  for (unsigned int i = 0; i < buffer->count; i++) {
64
- rb_gc_mark(buffer->entries[(buffer->head + i) % buffer->size].fiber);
65
- rb_gc_mark(buffer->entries[(buffer->head + i) % buffer->size].value);
64
+ runqueue_entry entry = buffer->entries[(buffer->head + i) % buffer->size];
65
+ rb_gc_mark(entry.fiber);
66
+ rb_gc_mark(entry.value);
66
67
  }
67
68
  }
68
69
 
@@ -40,7 +40,7 @@ VALUE Thread_fiber_schedule_and_wakeup(VALUE self, VALUE fiber, VALUE resume_obj
40
40
  }
41
41
 
42
42
  if (Backend_wakeup(rb_ivar_get(self, ID_ivar_backend)) == Qnil) {
43
- // we're not inside the ev_loop, so we just do a switchpoint
43
+ // we're not inside Backend_poll, so we just do a switchpoint
44
44
  Thread_switch_fiber(self);
45
45
  }
46
46
 
@@ -3,26 +3,36 @@
3
3
  require 'polyphony'
4
4
 
5
5
  if Object.constants.include?(:Reline)
6
+ puts "reline"
6
7
  class Reline::ANSI
7
8
  def self.select(read_ios = [], write_ios = [], error_ios = [], timeout = nil)
8
- p [:select, read_ios]
9
+ # p [:select, read_ios, timeout]
10
+ # puts caller.join("\n")
9
11
  raise if read_ios.size > 1
10
12
  raise if write_ios.size > 0
11
13
  raise if error_ios.size > 0
12
14
 
15
+ # p 1
13
16
  fiber = Fiber.current
14
17
  timer = spin do
15
18
  sleep timeout
16
19
  fiber.cancel
17
20
  end
21
+ # p 2
18
22
  read_ios.each do |io|
23
+ # p wait: io
19
24
  Polyphony.backend_wait_io(io, false)
25
+ # p :done_wait
20
26
  return [io]
21
27
  end
28
+ # p 3
22
29
  rescue Polyphony::Cancel
30
+ # p :cancel
23
31
  return nil
24
32
  ensure
33
+ # p :ensure
25
34
  timer.stop
35
+ # p :ensure_done
26
36
  end
27
37
  end
28
38
  else
File without changes
@@ -1,8 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative '../extensions/core'
4
- require_relative '../extensions/fiber'
5
- require_relative './exceptions'
6
3
  require_relative './throttler'
7
4
 
8
5
  module Polyphony
@@ -73,7 +70,7 @@ module Polyphony
73
70
 
74
71
  def spin_scope
75
72
  raise unless block_given?
76
-
73
+
77
74
  spin do
78
75
  result = yield
79
76
  Fiber.current.await_all_children
@@ -122,8 +119,8 @@ module Polyphony
122
119
  Fiber.current.receive_all_pending
123
120
  end
124
121
 
125
- def supervise(*args, &block)
126
- Fiber.current.supervise(*args, &block)
122
+ def supervise(*args, **opts, &block)
123
+ Fiber.current.supervise(*args, **opts, &block)
127
124
  end
128
125
 
129
126
  def sleep(duration = nil)
@@ -44,7 +44,7 @@ module Polyphony
44
44
  ensure
45
45
  @timeouts.delete(fiber)
46
46
  end
47
-
47
+
48
48
  def cancel_after(interval, with_exception: Polyphony::Cancel)
49
49
  fiber = Fiber.current
50
50
  @timeouts[fiber] = {
@@ -74,7 +74,7 @@ module Polyphony
74
74
  def reset
75
75
  record = @timeouts[Fiber.current]
76
76
  return unless record
77
-
77
+
78
78
  record[:target_stamp] = now + record[:interval]
79
79
  end
80
80
 
@@ -9,11 +9,11 @@ module Polyphony
9
9
  :return,
10
10
  :b_call,
11
11
  :b_return
12
- ]
12
+ ]
13
13
 
14
14
  def self.start_debug_server(socket_path)
15
15
  server = DebugServer.new(socket_path)
16
- controller = DebugController.new(server)
16
+ controller = DebugController.new(server)
17
17
  trace = TracePoint.new(*TP_EVENTS) { |tp| controller.handle_tp(trace, tp) }
18
18
  trace.enable
19
19
 
@@ -124,7 +124,7 @@ module Polyphony
124
124
  h
125
125
  end
126
126
  end
127
-
127
+
128
128
  def cmd_step(cmd)
129
129
  tp = nil
130
130
  fiber = nil
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Exeption overrides
4
+ class ::Exception
5
+ class << self
6
+ attr_accessor :__disable_sanitized_backtrace__
7
+ end
8
+
9
+ attr_accessor :source_fiber, :raising_fiber
10
+
11
+ alias_method :orig_initialize, :initialize
12
+ def initialize(*args)
13
+ @raising_fiber = Fiber.current
14
+ orig_initialize(*args)
15
+ end
16
+
17
+ alias_method :orig_backtrace, :backtrace
18
+ def backtrace
19
+ unless @backtrace_called
20
+ @backtrace_called = true
21
+ return orig_backtrace
22
+ end
23
+
24
+ sanitized_backtrace
25
+ end
26
+
27
+ def sanitized_backtrace
28
+ return sanitize(orig_backtrace) unless @raising_fiber
29
+
30
+ backtrace = orig_backtrace || []
31
+ sanitize(backtrace + @raising_fiber.caller)
32
+ end
33
+
34
+ POLYPHONY_DIR = File.expand_path(File.join(__dir__, '..'))
35
+
36
+ def sanitize(backtrace)
37
+ return backtrace if ::Exception.__disable_sanitized_backtrace__
38
+
39
+ backtrace.reject { |l| l[POLYPHONY_DIR] }
40
+ end
41
+
42
+ def invoke
43
+ Kernel.raise(self)
44
+ end
45
+ end
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'fiber'
4
-
5
3
  require_relative '../core/exceptions'
6
4
 
7
5
  module Polyphony
@@ -19,7 +17,7 @@ module Polyphony
19
17
  alias_method :stop, :interrupt
20
18
 
21
19
  def restart(value = nil)
22
- raise "Can''t restart main fiber" if @main
20
+ raise "Can't restart main fiber" if @main
23
21
 
24
22
  if @running
25
23
  schedule Polyphony::Restart.new(value)
@@ -83,6 +81,8 @@ module Polyphony
83
81
  def supervise(*fibers, **opts, &block)
84
82
  block ||= supervise_opts_to_block(opts)
85
83
 
84
+ @supervise_mode = true
85
+ fibers = children if fibers.empty?
86
86
  fibers.each do |f|
87
87
  f.attach_to(self) unless f.parent == self
88
88
  f.monitor(self)
@@ -94,15 +94,18 @@ module Polyphony
94
94
  (fiber, result) = mailbox.shift
95
95
  block&.call(fiber, result)
96
96
  end
97
+ ensure
98
+ @supervise_mode = false
97
99
  end
98
100
 
99
101
  def supervise_opts_to_block(opts)
100
102
  block = opts[:on_done] || opts[:on_error]
101
- return nil unless block || opts[:restart]
103
+ restart = opts[:restart]
104
+ return nil unless block || restart
102
105
 
103
106
  error_only = !!opts[:on_error]
104
- restart_always = opts[:restart] == :always
105
- restart_on_error = opts[:restart] == :on_error
107
+ restart_always = (restart == :always) || (restart == true)
108
+ restart_on_error = restart == :on_error
106
109
 
107
110
  ->(f, r) do
108
111
  is_error = r.is_a?(Exception)
@@ -114,6 +117,14 @@ module Polyphony
114
117
 
115
118
  # Class methods for controlling fibers (namely await and select)
116
119
  module FiberControlClassMethods
120
+ # call-seq:
121
+ # Fiber.await(*fibers) -> [*results]
122
+ # Fiber.join(*fibers) -> [*results]
123
+ #
124
+ # Waits for all given fibers to terminate, then returns the respective
125
+ # return values for all terminated fibers. If any of the awaited fibers
126
+ # terminates with an uncaught exception, `Fiber.await` will await all the
127
+ # other fibers to terminate, then reraise the exception.
117
128
  def await(*fibers)
118
129
  return [] if fibers.empty?
119
130
 
@@ -149,7 +160,7 @@ module Polyphony
149
160
 
150
161
  def select(*fibers)
151
162
  return nil if fibers.empty?
152
-
163
+
153
164
  current_fiber = self.current
154
165
  mailbox = current_fiber.monitor_mailbox
155
166
  fibers.each do |f|
@@ -163,7 +174,7 @@ module Polyphony
163
174
  while true
164
175
  (fiber, result) = mailbox.shift
165
176
  next unless fibers.include?(fiber)
166
-
177
+
167
178
  fibers.each { |f| f.unmonitor(current_fiber) }
168
179
  if result.is_a?(Exception)
169
180
  raise result
@@ -197,6 +208,7 @@ module Polyphony
197
208
 
198
209
  def add_child(child_fiber)
199
210
  (@children ||= {})[child_fiber] = true
211
+ child_fiber.monitor(self) if @supervise_mode
200
212
  end
201
213
 
202
214
  def remove_child(child_fiber)
@@ -207,6 +219,7 @@ module Polyphony
207
219
  f = Fiber.new { |v| f.run(v) }
208
220
  f.prepare(tag, block, orig_caller, self)
209
221
  (@children ||= {})[f] = true
222
+ f.monitor(self) if @supervise_mode
210
223
  f
211
224
  end
212
225
 
@@ -237,10 +250,15 @@ module Polyphony
237
250
  end
238
251
  end
239
252
 
253
+ def attach_all_children_to(fiber)
254
+ @children&.keys.each { |c| c.attach_to(fiber) }
255
+ end
256
+
240
257
  def detach
241
258
  @parent.remove_child(self)
242
259
  @parent = @thread.main_fiber
243
260
  @parent.add_child(self)
261
+ self
244
262
  end
245
263
 
246
264
  def attach_to(fiber)
@@ -328,7 +346,7 @@ module Polyphony
328
346
  # the children are shut down, it is returned along with the uncaught_exception
329
347
  # flag set. Otherwise, it returns the given arguments.
330
348
  def finalize_children(result, uncaught_exception)
331
- shutdown_all_children
349
+ shutdown_all_children(graceful_shutdown?)
332
350
  [result, uncaught_exception]
333
351
  rescue Exception => e
334
352
  [e, true]
@@ -103,7 +103,7 @@ class ::IO
103
103
  alias_method :orig_getc, :getc
104
104
  def getc
105
105
  return @read_buffer.slice!(0) if @read_buffer && !@read_buffer.empty?
106
-
106
+
107
107
  @read_buffer ||= +''
108
108
  Polyphony.backend_read(self, @read_buffer, 8192, false, -1)
109
109
  return @read_buffer.slice!(0) if !@read_buffer.empty?
@@ -116,7 +116,7 @@ class ::IO
116
116
  if buf
117
117
  return Polyphony.backend_read(self, buf, len, true, buf_pos)
118
118
  end
119
-
119
+
120
120
  @read_buffer ||= +''
121
121
  result = Polyphony.backend_read(self, @read_buffer, len, true, -1)
122
122
  return nil unless result
@@ -1,73 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'fiber'
4
- require 'timeout'
5
3
  require 'open3'
6
4
 
7
- require_relative '../core/exceptions'
8
-
9
- # Exeption overrides
10
- class ::Exception
11
- class << self
12
- attr_accessor :__disable_sanitized_backtrace__
13
- end
14
-
15
- attr_accessor :source_fiber, :raising_fiber
16
-
17
- alias_method :orig_initialize, :initialize
18
- def initialize(*args)
19
- @raising_fiber = Fiber.current
20
- orig_initialize(*args)
21
- end
22
-
23
- alias_method :orig_backtrace, :backtrace
24
- def backtrace
25
- unless @backtrace_called
26
- @backtrace_called = true
27
- return orig_backtrace
28
- end
29
-
30
- sanitized_backtrace
31
- end
32
-
33
- def sanitized_backtrace
34
- return sanitize(orig_backtrace) unless @raising_fiber
35
-
36
- backtrace = orig_backtrace || []
37
- sanitize(backtrace + @raising_fiber.caller)
38
- end
39
-
40
- POLYPHONY_DIR = File.expand_path(File.join(__dir__, '..'))
41
-
42
- def sanitize(backtrace)
43
- return backtrace if ::Exception.__disable_sanitized_backtrace__
44
-
45
- backtrace.reject { |l| l[POLYPHONY_DIR] }
46
- end
47
-
48
- def invoke
49
- Kernel.raise(self)
50
- end
51
- end
52
-
53
- # Overrides for Process
54
- module ::Process
55
- class << self
56
- alias_method :orig_detach, :detach
57
- def detach(pid)
58
- fiber = spin { Polyphony.backend_waitpid(pid) }
59
- fiber.define_singleton_method(:pid) { pid }
60
- fiber
61
- end
62
-
63
- alias_method :orig_daemon, :daemon
64
- def daemon(*args)
65
- orig_daemon(*args)
66
- Polyphony.original_pid = Process.pid
67
- end
68
- end
69
- end
70
-
71
5
  # Kernel extensions (methods available to all objects / call sites)
72
6
  module ::Kernel
73
7
  alias_method :orig_sleep, :sleep
@@ -159,10 +93,3 @@ module ::Kernel
159
93
  end
160
94
  end
161
95
  end
162
-
163
- # Override Timeout to use cancel scope
164
- module ::Timeout
165
- def self.timeout(sec, klass = Timeout::Error, message = 'execution expired', &block)
166
- cancel_after(sec, with_exception: [klass, message], &block)
167
- end
168
- end
@@ -130,24 +130,32 @@ class ::OpenSSL::SSL::SSLServer
130
130
  end
131
131
  end
132
132
 
133
+ # STDOUT.puts 'SSLServer#accept'
133
134
  sock, = @svr.accept
135
+ # STDOUT.puts "- raw sock: #{sock.inspect}"
134
136
  begin
135
137
  ssl = OpenSSL::SSL::SSLSocket.new(sock, @ctx)
138
+ # STDOUT.puts "- ssl sock: #{ssl.inspect}"
136
139
  ssl.sync_close = true
137
140
  if @use_accept_worker
141
+ # STDOUT.puts "- send to accept worker"
138
142
  @accept_worker_fiber << [ssl, Fiber.current]
139
- receive
143
+ # STDOUT.puts "- wait for accept worker"
144
+ r = receive
145
+ # STDOUT.puts "- got reply from accept worker: #{r.inspect}"
146
+ r.invoke if r.is_a?(Exception)
140
147
  else
141
148
  ssl.accept
142
149
  end
143
150
  ssl
144
- rescue Exception => ex
151
+ rescue Exception => e
152
+ # STDOUT.puts "- accept exception: #{e.inspect}"
145
153
  if ssl
146
154
  ssl.close
147
155
  else
148
156
  sock.close
149
157
  end
150
- raise ex
158
+ raise e
151
159
  end
152
160
  end
153
161
 
@@ -156,11 +164,18 @@ class ::OpenSSL::SSL::SSLServer
156
164
  @accept_worker_thread = Thread.new do
157
165
  fiber << Fiber.current
158
166
  loop do
167
+ # STDOUT.puts "- accept_worker wait for work"
159
168
  socket, peer = receive
169
+ # STDOUT.puts "- accept_worker got socket from peer #{peer.inspect}"
160
170
  socket.accept
171
+ # STDOUT.puts "- accept_worker accept returned"
161
172
  peer << socket
162
- rescue => e
163
- peer.schedule(e) if fiber
173
+ # STDOUT.puts "- accept_worker sent socket back to peer"
174
+ rescue Polyphony::BaseException
175
+ raise
176
+ rescue Exception => e
177
+ # STDOUT.puts "- accept_worker error: #{e}"
178
+ peer << e if peer
164
179
  end
165
180
  end
166
181
  @accept_worker_fiber = receive
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Overrides for Process
4
+ module ::Process
5
+ class << self
6
+ alias_method :orig_detach, :detach
7
+ def detach(pid)
8
+ fiber = spin { Polyphony.backend_waitpid(pid) }
9
+ fiber.define_singleton_method(:pid) { pid }
10
+ fiber
11
+ end
12
+
13
+ alias_method :orig_daemon, :daemon
14
+ def daemon(*args)
15
+ orig_daemon(*args)
16
+ Polyphony.original_pid = Process.pid
17
+ end
18
+ end
19
+ end
@@ -32,7 +32,7 @@ class ::Socket
32
32
  def read(maxlen = nil, buf = nil, buf_pos = 0)
33
33
  return Polyphony.backend_recv(self, buf, maxlen, buf_pos) if buf
34
34
  return Polyphony.backend_recv(self, buf || +'', maxlen, 0) if maxlen
35
-
35
+
36
36
  buf = +''
37
37
  len = buf.bytesize
38
38
  while true
@@ -168,7 +168,7 @@ class ::TCPSocket
168
168
  def read(maxlen = nil, buf = nil, buf_pos = 0)
169
169
  return Polyphony.backend_recv(self, buf, maxlen, buf_pos) if buf
170
170
  return Polyphony.backend_recv(self, buf || +'', maxlen, 0) if maxlen
171
-
171
+
172
172
  buf = +''
173
173
  len = buf.bytesize
174
174
  while true
@@ -232,7 +232,6 @@ class ::TCPServer
232
232
  alias_method :orig_accept, :accept
233
233
  def accept
234
234
  Polyphony.backend_accept(@io, TCPSocket)
235
- # @io.accept
236
235
  end
237
236
 
238
237
  def accept_loop(&block)
@@ -261,7 +260,7 @@ class ::UNIXSocket
261
260
  def read(maxlen = nil, buf = nil, buf_pos = 0)
262
261
  return Polyphony.backend_recv(self, buf, maxlen, buf_pos) if buf
263
262
  return Polyphony.backend_recv(self, buf || +'', maxlen, 0) if maxlen
264
-
263
+
265
264
  buf = +''
266
265
  len = buf.bytesize
267
266
  while true
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'timeout'
4
+
5
+ # Override Timeout to use cancel scope
6
+ module ::Timeout
7
+ def self.timeout(sec, klass = Timeout::Error, message = 'execution expired', &block)
8
+ cancel_after(sec, with_exception: [klass, message], &block)
9
+ end
10
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative './extensions/exception'
4
+ require_relative './extensions/fiber'
5
+ require_relative './extensions/io'
6
+ require_relative './extensions/kernel'
7
+ require_relative './extensions/process'
8
+ require_relative './extensions/thread'
9
+ require_relative './extensions/timeout'
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Polyphony
4
- VERSION = '0.70'
4
+ VERSION = '0.73.1'
5
5
  end
data/lib/polyphony.rb CHANGED
@@ -2,15 +2,12 @@
2
2
 
3
3
  require 'fiber'
4
4
  require_relative './polyphony_ext'
5
-
6
- require_relative './polyphony/extensions/core'
7
- require_relative './polyphony/extensions/thread'
8
- require_relative './polyphony/extensions/fiber'
9
- require_relative './polyphony/extensions/io'
5
+ require_relative './polyphony/extensions'
10
6
 
11
7
  Thread.current.setup_fiber_scheduling
12
8
  Thread.current.backend = Polyphony::Backend.new
13
9
 
10
+ require_relative './polyphony/core/exceptions'
14
11
  require_relative './polyphony/core/global_api'
15
12
  require_relative './polyphony/core/resource_pool'
16
13
  require_relative './polyphony/core/sync'
data/polyphony.gemspec CHANGED
@@ -27,7 +27,7 @@ Gem::Specification.new do |s|
27
27
  s.add_development_dependency 'simplecov', '0.17.1'
28
28
  s.add_development_dependency 'rubocop', '0.85.1'
29
29
  s.add_development_dependency 'pry', '0.13.1'
30
-
30
+
31
31
  s.add_development_dependency 'msgpack', '1.4.2'
32
32
  s.add_development_dependency 'httparty', '0.17.1'
33
33
  s.add_development_dependency 'localhost', '~>1.1.4'
data/test/coverage.rb CHANGED
@@ -24,10 +24,10 @@ module Coverage
24
24
  @result = {}
25
25
  trace = TracePoint.new(:line) do |tp|
26
26
  next if tp.path =~ /\(/
27
-
27
+
28
28
  absolute = File.expand_path(tp.path)
29
29
  next unless LIB_FILES.include?(absolute)# =~ /^#{LIB_DIR}/
30
-
30
+
31
31
  @result[absolute] ||= relevant_lines_for_filename(absolute)
32
32
  @result[absolute][tp.lineno - 1] = 1
33
33
  end