polyphony 0.69 → 0.73

Sign up to get free protection for your applications and to get access to all the features.
Files changed (79) hide show
  1. checksums.yaml +4 -4
  2. data/.github/FUNDING.yml +1 -0
  3. data/.github/workflows/test.yml +2 -2
  4. data/.gitignore +3 -1
  5. data/CHANGELOG.md +33 -4
  6. data/Gemfile.lock +2 -2
  7. data/TODO.md +1 -24
  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 +31 -7
  32. data/ext/polyphony/backend_common.h +2 -1
  33. data/ext/polyphony/backend_io_uring.c +57 -67
  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 +38 -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 +30 -16
  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/net.rb +0 -1
  57. data/lib/polyphony/version.rb +1 -1
  58. data/lib/polyphony.rb +2 -5
  59. data/polyphony.gemspec +1 -1
  60. data/test/coverage.rb +2 -2
  61. data/test/stress.rb +1 -1
  62. data/test/test_backend.rb +12 -12
  63. data/test/test_event.rb +1 -1
  64. data/test/test_ext.rb +1 -1
  65. data/test/test_fiber.rb +52 -12
  66. data/test/test_global_api.rb +16 -4
  67. data/test/test_io.rb +3 -3
  68. data/test/test_process_supervision.rb +39 -10
  69. data/test/test_queue.rb +6 -6
  70. data/test/test_signal.rb +20 -1
  71. data/test/test_socket.rb +12 -10
  72. data/test/test_supervise.rb +249 -81
  73. data/test/test_sync.rb +2 -2
  74. data/test/test_thread.rb +22 -2
  75. data/test/test_thread_pool.rb +1 -1
  76. data/test/test_throttler.rb +1 -1
  77. data/test/test_timer.rb +2 -2
  78. data/test/test_trace.rb +1 -1
  79. metadata +18 -9
@@ -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)
@@ -27,6 +25,7 @@ module Polyphony
27
25
  end
28
26
 
29
27
  fiber = parent.spin(@tag, @caller, &@block)
28
+ @monitors&.each_key { |f| fiber.monitor(f) }
30
29
  fiber.schedule(value) unless value.nil?
31
30
  fiber
32
31
  end
@@ -80,10 +79,10 @@ module Polyphony
80
79
  # Fiber supervision
81
80
  module FiberSupervision
82
81
  def supervise(*fibers, **opts, &block)
83
- block ||= opts[:on_done] ||
84
- (opts[:on_error] && supervise_on_error_proc(opts[:on_error]))
85
- raise "No block given" unless block
82
+ block ||= supervise_opts_to_block(opts)
86
83
 
84
+ @supervise_mode = true
85
+ fibers = children if fibers.empty?
87
86
  fibers.each do |f|
88
87
  f.attach_to(self) unless f.parent == self
89
88
  f.monitor(self)
@@ -95,10 +94,24 @@ module Polyphony
95
94
  (fiber, result) = mailbox.shift
96
95
  block&.call(fiber, result)
97
96
  end
97
+ ensure
98
+ @supervise_mode = false
98
99
  end
99
100
 
100
- def supervise_on_error_proc(on_error)
101
- ->(f, r) { opts[:on_error].(f, r) if r.is_a?(Exception) }
101
+ def supervise_opts_to_block(opts)
102
+ block = opts[:on_done] || opts[:on_error]
103
+ restart = opts[:restart]
104
+ return nil unless block || restart
105
+
106
+ error_only = !!opts[:on_error]
107
+ restart_always = (restart == :always) || (restart == true)
108
+ restart_on_error = restart == :on_error
109
+
110
+ ->(f, r) do
111
+ is_error = r.is_a?(Exception)
112
+ block.(f, r) if block && (!error_only || is_error)
113
+ f.restart if restart_always || (restart_on_error && is_error)
114
+ end
102
115
  end
103
116
  end
104
117
 
@@ -139,7 +152,7 @@ module Polyphony
139
152
 
140
153
  def select(*fibers)
141
154
  return nil if fibers.empty?
142
-
155
+
143
156
  current_fiber = self.current
144
157
  mailbox = current_fiber.monitor_mailbox
145
158
  fibers.each do |f|
@@ -153,7 +166,7 @@ module Polyphony
153
166
  while true
154
167
  (fiber, result) = mailbox.shift
155
168
  next unless fibers.include?(fiber)
156
-
169
+
157
170
  fibers.each { |f| f.unmonitor(current_fiber) }
158
171
  if result.is_a?(Exception)
159
172
  raise result
@@ -187,6 +200,7 @@ module Polyphony
187
200
 
188
201
  def add_child(child_fiber)
189
202
  (@children ||= {})[child_fiber] = true
203
+ child_fiber.monitor(self) if @supervise_mode
190
204
  end
191
205
 
192
206
  def remove_child(child_fiber)
@@ -197,6 +211,7 @@ module Polyphony
197
211
  f = Fiber.new { |v| f.run(v) }
198
212
  f.prepare(tag, block, orig_caller, self)
199
213
  (@children ||= {})[f] = true
214
+ f.monitor(self) if @supervise_mode
200
215
  f
201
216
  end
202
217
 
@@ -225,19 +240,17 @@ module Polyphony
225
240
  c.terminate(graceful)
226
241
  c.await
227
242
  end
228
- reap_dead_children
229
243
  end
230
244
 
231
- def reap_dead_children
232
- return unless @children
233
-
234
- @children.reject! { |f| f.dead? }
245
+ def attach_all_children_to(fiber)
246
+ @children&.keys.each { |c| c.attach_to(fiber) }
235
247
  end
236
248
 
237
249
  def detach
238
250
  @parent.remove_child(self)
239
251
  @parent = @thread.main_fiber
240
252
  @parent.add_child(self)
253
+ self
241
254
  end
242
255
 
243
256
  def attach_to(fiber)
@@ -315,6 +328,7 @@ module Polyphony
315
328
  inform_monitors(result, uncaught_exception)
316
329
  @running = false
317
330
  ensure
331
+ @parent&.remove_child(self)
318
332
  # Prevent fiber from being resumed after terminating
319
333
  @thread.fiber_unschedule(self)
320
334
  Thread.current.switch_fiber
@@ -324,7 +338,7 @@ module Polyphony
324
338
  # the children are shut down, it is returned along with the uncaught_exception
325
339
  # flag set. Otherwise, it returns the given arguments.
326
340
  def finalize_children(result, uncaught_exception)
327
- shutdown_all_children
341
+ shutdown_all_children(graceful_shutdown?)
328
342
  [result, uncaught_exception]
329
343
  rescue Exception => e
330
344
  [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'
data/lib/polyphony/net.rb CHANGED
@@ -67,7 +67,6 @@ module Polyphony
67
67
  def setup_alpn(context, protocols)
68
68
  context.alpn_protocols = protocols
69
69
  context.alpn_select_cb = lambda do |peer_protocols|
70
- p alpn_select_cb: peer_protocols
71
70
  (protocols & peer_protocols).first
72
71
  end
73
72
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Polyphony
4
- VERSION = '0.69'
4
+ VERSION = '0.73'
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'