polyphony 0.69 → 0.73
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/.github/FUNDING.yml +1 -0
- data/.github/workflows/test.yml +2 -2
- data/.gitignore +3 -1
- data/CHANGELOG.md +33 -4
- data/Gemfile.lock +2 -2
- data/TODO.md +1 -24
- data/bin/pdbg +1 -1
- data/bin/polyphony-debug +0 -0
- data/bin/stress.rb +0 -0
- data/bin/test +0 -0
- data/docs/_user-guide/all-about-timers.md +1 -1
- data/docs/api-reference/exception.md +5 -1
- data/docs/api-reference/fiber.md +2 -2
- data/docs/faq.md +1 -1
- data/docs/getting-started/overview.md +8 -8
- data/docs/getting-started/tutorial.md +3 -3
- data/docs/main-concepts/concurrency.md +1 -1
- data/docs/main-concepts/extending.md +3 -3
- data/docs/main-concepts/fiber-scheduling.md +1 -1
- data/examples/core/calc.rb +37 -0
- data/examples/core/calc_with_restart.rb +40 -0
- data/examples/core/calc_with_supervise.rb +37 -0
- data/examples/core/message_based_supervision.rb +1 -1
- data/examples/core/ring.rb +29 -0
- data/examples/io/rack_server.rb +1 -1
- data/examples/io/tunnel.rb +1 -1
- data/examples/performance/fiber_transfer.rb +1 -1
- data/examples/performance/line_splitting.rb +1 -1
- data/examples/performance/thread-vs-fiber/compare.rb +1 -1
- data/ext/polyphony/backend_common.c +31 -7
- data/ext/polyphony/backend_common.h +2 -1
- data/ext/polyphony/backend_io_uring.c +57 -67
- data/ext/polyphony/backend_io_uring_context.c +1 -1
- data/ext/polyphony/backend_io_uring_context.h +1 -1
- data/ext/polyphony/backend_libev.c +38 -30
- data/ext/polyphony/extconf.rb +25 -13
- data/ext/polyphony/polyphony.h +5 -1
- data/ext/polyphony/queue.c +2 -2
- data/ext/polyphony/runqueue_ring_buffer.c +3 -2
- data/ext/polyphony/thread.c +1 -1
- data/lib/polyphony/adapters/irb.rb +11 -1
- data/lib/polyphony/{extensions → core}/debug.rb +0 -0
- data/lib/polyphony/core/global_api.rb +3 -6
- data/lib/polyphony/core/timer.rb +2 -2
- data/lib/polyphony/debugger.rb +3 -3
- data/lib/polyphony/extensions/exception.rb +45 -0
- data/lib/polyphony/extensions/fiber.rb +30 -16
- data/lib/polyphony/extensions/io.rb +2 -2
- data/lib/polyphony/extensions/{core.rb → kernel.rb} +0 -73
- data/lib/polyphony/extensions/openssl.rb +20 -5
- data/lib/polyphony/extensions/process.rb +19 -0
- data/lib/polyphony/extensions/socket.rb +3 -4
- data/lib/polyphony/extensions/timeout.rb +10 -0
- data/lib/polyphony/extensions.rb +9 -0
- data/lib/polyphony/net.rb +0 -1
- data/lib/polyphony/version.rb +1 -1
- data/lib/polyphony.rb +2 -5
- data/polyphony.gemspec +1 -1
- data/test/coverage.rb +2 -2
- data/test/stress.rb +1 -1
- data/test/test_backend.rb +12 -12
- data/test/test_event.rb +1 -1
- data/test/test_ext.rb +1 -1
- data/test/test_fiber.rb +52 -12
- data/test/test_global_api.rb +16 -4
- data/test/test_io.rb +3 -3
- data/test/test_process_supervision.rb +39 -10
- data/test/test_queue.rb +6 -6
- data/test/test_signal.rb +20 -1
- data/test/test_socket.rb +12 -10
- data/test/test_supervise.rb +249 -81
- data/test/test_sync.rb +2 -2
- data/test/test_thread.rb +22 -2
- data/test/test_thread_pool.rb +1 -1
- data/test/test_throttler.rb +1 -1
- data/test/test_timer.rb +2 -2
- data/test/test_trace.rb +1 -1
- metadata +18 -9
data/ext/polyphony/extconf.rb
CHANGED
@@ -3,32 +3,43 @@
|
|
3
3
|
require 'rubygems'
|
4
4
|
require 'mkmf'
|
5
5
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
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
|
-
|
18
|
-
|
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'
|
data/ext/polyphony/polyphony.h
CHANGED
@@ -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
|
-
#
|
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
|
|
data/ext/polyphony/queue.c
CHANGED
@@ -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
|
-
|
65
|
-
rb_gc_mark(
|
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
|
|
data/ext/polyphony/thread.c
CHANGED
@@ -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
|
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)
|
data/lib/polyphony/core/timer.rb
CHANGED
@@ -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
|
|
data/lib/polyphony/debugger.rb
CHANGED
@@ -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'
|
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
|
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
|
101
|
-
|
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
|
232
|
-
|
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
|
-
|
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 =>
|
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
|
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
|
-
|
163
|
-
|
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
data/lib/polyphony/version.rb
CHANGED
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'
|