polyphony 1.4 → 1.6
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/.rubocop.yml +3 -0
- data/CHANGELOG.md +22 -0
- data/TODO.md +5 -14
- data/examples/pipes/http_server.rb +42 -12
- data/examples/pipes/http_server2.rb +45 -0
- data/ext/polyphony/backend_common.h +5 -0
- data/ext/polyphony/backend_io_uring.c +174 -121
- data/ext/polyphony/backend_io_uring_context.c +24 -18
- data/ext/polyphony/backend_io_uring_context.h +4 -2
- data/ext/polyphony/backend_libev.c +46 -22
- data/ext/polyphony/event.c +21 -0
- data/ext/polyphony/extconf.rb +25 -19
- data/ext/polyphony/fiber.c +0 -2
- data/ext/polyphony/pipe.c +1 -1
- data/ext/polyphony/polyphony.c +2 -20
- data/ext/polyphony/polyphony.h +5 -5
- data/ext/polyphony/ring_buffer.c +1 -0
- data/ext/polyphony/runqueue_ring_buffer.c +1 -0
- data/ext/polyphony/thread.c +63 -0
- data/ext/polyphony/win_uio.h +18 -0
- data/lib/polyphony/adapters/open3.rb +190 -0
- data/lib/polyphony/core/sync.rb +83 -13
- data/lib/polyphony/core/timer.rb +7 -25
- data/lib/polyphony/extensions/exception.rb +15 -0
- data/lib/polyphony/extensions/fiber.rb +14 -13
- data/lib/polyphony/extensions/io.rb +56 -14
- data/lib/polyphony/extensions/kernel.rb +1 -1
- data/lib/polyphony/extensions/object.rb +1 -13
- data/lib/polyphony/extensions/process.rb +76 -1
- data/lib/polyphony/extensions/socket.rb +0 -14
- data/lib/polyphony/extensions/thread.rb +19 -27
- data/lib/polyphony/extensions/timeout.rb +5 -1
- data/lib/polyphony/version.rb +1 -1
- data/lib/polyphony.rb +11 -5
- data/test/helper.rb +46 -4
- data/test/open3/envutil.rb +380 -0
- data/test/open3/find_executable.rb +24 -0
- data/test/stress.rb +11 -7
- data/test/test_backend.rb +11 -4
- data/test/test_event.rb +10 -3
- data/test/test_ext.rb +16 -1
- data/test/test_fiber.rb +16 -4
- data/test/test_global_api.rb +17 -16
- data/test/test_io.rb +39 -0
- data/test/test_kernel.rb +2 -2
- data/test/test_monitor.rb +356 -0
- data/test/test_open3.rb +338 -0
- data/test/test_signal.rb +5 -1
- data/test/test_socket.rb +6 -98
- data/test/test_sync.rb +46 -0
- data/test/test_thread.rb +10 -1
- data/test/test_thread_pool.rb +5 -0
- data/test/test_throttler.rb +1 -1
- data/test/test_timer.rb +8 -2
- data/test/test_trace.rb +2 -0
- data/vendor/liburing/.github/workflows/build.yml +8 -0
- data/vendor/liburing/.gitignore +1 -0
- data/vendor/liburing/CHANGELOG +8 -0
- data/vendor/liburing/configure +17 -25
- data/vendor/liburing/debian/liburing-dev.manpages +2 -0
- data/vendor/liburing/debian/rules +2 -1
- data/vendor/liburing/examples/Makefile +2 -1
- data/vendor/liburing/examples/io_uring-udp.c +11 -3
- data/vendor/liburing/examples/rsrc-update-bench.c +100 -0
- data/vendor/liburing/liburing.spec +1 -1
- data/vendor/liburing/make-debs.sh +4 -2
- data/vendor/liburing/src/Makefile +5 -5
- data/vendor/liburing/src/arch/aarch64/lib.h +1 -1
- data/vendor/liburing/src/include/liburing/io_uring.h +41 -16
- data/vendor/liburing/src/include/liburing.h +86 -11
- data/vendor/liburing/src/int_flags.h +1 -0
- data/vendor/liburing/src/liburing-ffi.map +12 -0
- data/vendor/liburing/src/liburing.map +8 -0
- data/vendor/liburing/src/register.c +7 -2
- data/vendor/liburing/src/setup.c +373 -81
- data/vendor/liburing/test/232c93d07b74.c +3 -3
- data/vendor/liburing/test/Makefile +10 -3
- data/vendor/liburing/test/accept.c +2 -1
- data/vendor/liburing/test/buf-ring.c +35 -75
- data/vendor/liburing/test/connect-rep.c +204 -0
- data/vendor/liburing/test/coredump.c +59 -0
- data/vendor/liburing/test/fallocate.c +9 -0
- data/vendor/liburing/test/fd-pass.c +34 -3
- data/vendor/liburing/test/file-verify.c +27 -6
- data/vendor/liburing/test/helpers.c +3 -1
- data/vendor/liburing/test/io_uring_register.c +25 -28
- data/vendor/liburing/test/io_uring_setup.c +1 -1
- data/vendor/liburing/test/poll-cancel-all.c +29 -5
- data/vendor/liburing/test/poll-race-mshot.c +6 -22
- data/vendor/liburing/test/read-write.c +53 -0
- data/vendor/liburing/test/recv-msgall.c +21 -23
- data/vendor/liburing/test/reg-fd-only.c +55 -0
- data/vendor/liburing/test/reg-hint.c +56 -0
- data/vendor/liburing/test/regbuf-merge.c +91 -0
- data/vendor/liburing/test/ringbuf-read.c +2 -10
- data/vendor/liburing/test/send_recvmsg.c +5 -16
- data/vendor/liburing/test/shutdown.c +2 -1
- data/vendor/liburing/test/socket-io-cmd.c +215 -0
- data/vendor/liburing/test/socket-rw-eagain.c +2 -1
- data/vendor/liburing/test/socket-rw-offset.c +2 -1
- data/vendor/liburing/test/socket-rw.c +2 -1
- data/vendor/liburing/test/timeout.c +276 -0
- data/vendor/liburing/test/xattr.c +38 -25
- metadata +20 -7
- data/vendor/liburing/test/timeout-overflow.c +0 -204
@@ -0,0 +1,190 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Adapted from https://github.com/ruby/open3/blob/master/lib/open3.rb
|
4
|
+
|
5
|
+
module Open3
|
6
|
+
|
7
|
+
# Open3.capture3 captures the standard output and the standard error of a command.
|
8
|
+
#
|
9
|
+
# stdout_str, stderr_str, status = Open3.capture3([env,] cmd... [, opts])
|
10
|
+
#
|
11
|
+
# The arguments env, cmd and opts are passed to Open3.popen3 except
|
12
|
+
# <code>opts[:stdin_data]</code> and <code>opts[:binmode]</code>. See Process.spawn.
|
13
|
+
#
|
14
|
+
# If <code>opts[:stdin_data]</code> is specified, it is sent to the command's standard input.
|
15
|
+
#
|
16
|
+
# If <code>opts[:binmode]</code> is true, internal pipes are set to binary mode.
|
17
|
+
#
|
18
|
+
# Examples:
|
19
|
+
#
|
20
|
+
# # dot is a command of graphviz.
|
21
|
+
# graph = <<'End'
|
22
|
+
# digraph g {
|
23
|
+
# a -> b
|
24
|
+
# }
|
25
|
+
# End
|
26
|
+
# drawn_graph, dot_log = Open3.capture3("dot -v", :stdin_data=>graph)
|
27
|
+
#
|
28
|
+
# o, e, s = Open3.capture3("echo abc; sort >&2", :stdin_data=>"foo\nbar\nbaz\n")
|
29
|
+
# p o #=> "abc\n"
|
30
|
+
# p e #=> "bar\nbaz\nfoo\n"
|
31
|
+
# p s #=> #<Process::Status: pid 32682 exit 0>
|
32
|
+
#
|
33
|
+
# # generate a thumbnail image using the convert command of ImageMagick.
|
34
|
+
# # However, if the image is really stored in a file,
|
35
|
+
# # system("convert", "-thumbnail", "80", "png:#{filename}", "png:-") is better
|
36
|
+
# # because of reduced memory consumption.
|
37
|
+
# # But if the image is stored in a DB or generated by the gnuplot Open3.capture2 example,
|
38
|
+
# # Open3.capture3 should be considered.
|
39
|
+
# #
|
40
|
+
# image = File.read("/usr/share/openclipart/png/animals/mammals/sheep-md-v0.1.png", :binmode=>true)
|
41
|
+
# thumbnail, err, s = Open3.capture3("convert -thumbnail 80 png:- png:-", :stdin_data=>image, :binmode=>true)
|
42
|
+
# if s.success?
|
43
|
+
# STDOUT.binmode; print thumbnail
|
44
|
+
# end
|
45
|
+
#
|
46
|
+
def capture3(*cmd)
|
47
|
+
if Hash === cmd.last
|
48
|
+
opts = cmd.pop.dup
|
49
|
+
else
|
50
|
+
opts = {}
|
51
|
+
end
|
52
|
+
|
53
|
+
stdin_data = opts.delete(:stdin_data) || ''
|
54
|
+
binmode = opts.delete(:binmode)
|
55
|
+
|
56
|
+
popen3(*cmd, opts) {|i, o, e, t|
|
57
|
+
if binmode
|
58
|
+
i.binmode
|
59
|
+
o.binmode
|
60
|
+
e.binmode
|
61
|
+
end
|
62
|
+
out_reader = spin { o.read }
|
63
|
+
err_reader = spin { e.read }
|
64
|
+
begin
|
65
|
+
if stdin_data.respond_to? :readpartial
|
66
|
+
IO.double_splice(stdin_data, i)
|
67
|
+
else
|
68
|
+
i.write stdin_data
|
69
|
+
end
|
70
|
+
rescue Errno::EPIPE
|
71
|
+
end
|
72
|
+
i.close
|
73
|
+
[out_reader.value, err_reader.value, t.value]
|
74
|
+
}
|
75
|
+
end
|
76
|
+
module_function :capture3
|
77
|
+
|
78
|
+
# Open3.capture2 captures the standard output of a command.
|
79
|
+
#
|
80
|
+
# stdout_str, status = Open3.capture2([env,] cmd... [, opts])
|
81
|
+
#
|
82
|
+
# The arguments env, cmd and opts are passed to Open3.popen3 except
|
83
|
+
# <code>opts[:stdin_data]</code> and <code>opts[:binmode]</code>. See Process.spawn.
|
84
|
+
#
|
85
|
+
# If <code>opts[:stdin_data]</code> is specified, it is sent to the command's standard input.
|
86
|
+
#
|
87
|
+
# If <code>opts[:binmode]</code> is true, internal pipes are set to binary mode.
|
88
|
+
#
|
89
|
+
# Example:
|
90
|
+
#
|
91
|
+
# # factor is a command for integer factorization.
|
92
|
+
# o, s = Open3.capture2("factor", :stdin_data=>"42")
|
93
|
+
# p o #=> "42: 2 3 7\n"
|
94
|
+
#
|
95
|
+
# # generate x**2 graph in png using gnuplot.
|
96
|
+
# gnuplot_commands = <<"End"
|
97
|
+
# set terminal png
|
98
|
+
# plot x**2, "-" with lines
|
99
|
+
# 1 14
|
100
|
+
# 2 1
|
101
|
+
# 3 8
|
102
|
+
# 4 5
|
103
|
+
# e
|
104
|
+
# End
|
105
|
+
# image, s = Open3.capture2("gnuplot", :stdin_data=>gnuplot_commands, :binmode=>true)
|
106
|
+
#
|
107
|
+
def capture2(*cmd)
|
108
|
+
if Hash === cmd.last
|
109
|
+
opts = cmd.pop.dup
|
110
|
+
else
|
111
|
+
opts = {}
|
112
|
+
end
|
113
|
+
|
114
|
+
stdin_data = opts.delete(:stdin_data)
|
115
|
+
binmode = opts.delete(:binmode)
|
116
|
+
|
117
|
+
popen2(*cmd, opts) {|i, o, t|
|
118
|
+
if binmode
|
119
|
+
i.binmode
|
120
|
+
o.binmode
|
121
|
+
end
|
122
|
+
out_reader = spin { o.read }
|
123
|
+
if stdin_data
|
124
|
+
begin
|
125
|
+
if stdin_data.respond_to? :readpartial
|
126
|
+
IO.double_splice(stdin_data, i)
|
127
|
+
else
|
128
|
+
i.write stdin_data
|
129
|
+
end
|
130
|
+
rescue Errno::EPIPE
|
131
|
+
end
|
132
|
+
end
|
133
|
+
i.close
|
134
|
+
[out_reader.value, t.value]
|
135
|
+
}
|
136
|
+
end
|
137
|
+
module_function :capture2
|
138
|
+
|
139
|
+
# Open3.capture2e captures the standard output and the standard error of a command.
|
140
|
+
#
|
141
|
+
# stdout_and_stderr_str, status = Open3.capture2e([env,] cmd... [, opts])
|
142
|
+
#
|
143
|
+
# The arguments env, cmd and opts are passed to Open3.popen3 except
|
144
|
+
# <code>opts[:stdin_data]</code> and <code>opts[:binmode]</code>. See Process.spawn.
|
145
|
+
#
|
146
|
+
# If <code>opts[:stdin_data]</code> is specified, it is sent to the command's standard input.
|
147
|
+
#
|
148
|
+
# If <code>opts[:binmode]</code> is true, internal pipes are set to binary mode.
|
149
|
+
#
|
150
|
+
# Example:
|
151
|
+
#
|
152
|
+
# # capture make log
|
153
|
+
# make_log, s = Open3.capture2e("make")
|
154
|
+
#
|
155
|
+
def capture2e(*cmd)
|
156
|
+
if Hash === cmd.last
|
157
|
+
opts = cmd.pop.dup
|
158
|
+
else
|
159
|
+
opts = {}
|
160
|
+
end
|
161
|
+
|
162
|
+
stdin_data = opts.delete(:stdin_data)
|
163
|
+
binmode = opts.delete(:binmode)
|
164
|
+
|
165
|
+
popen2e(*cmd, opts) {|i, oe, t|
|
166
|
+
if binmode
|
167
|
+
i.binmode
|
168
|
+
oe.binmode
|
169
|
+
end
|
170
|
+
outerr_reader = spin { oe.read }
|
171
|
+
if stdin_data
|
172
|
+
begin
|
173
|
+
if stdin_data.respond_to? :readpartial
|
174
|
+
IO.double_splice(stdin_data, i)
|
175
|
+
else
|
176
|
+
i.write stdin_data
|
177
|
+
end
|
178
|
+
rescue Errno::EPIPE
|
179
|
+
# ignore
|
180
|
+
end
|
181
|
+
end
|
182
|
+
i.close
|
183
|
+
[outerr_reader.value, t.value]
|
184
|
+
}
|
185
|
+
end
|
186
|
+
module_function :capture2e
|
187
|
+
end
|
188
|
+
|
189
|
+
# JRuby uses different popen logic on Windows, require it here to reuse wrapper methods above.
|
190
|
+
require 'open3/jruby_windows' if RUBY_ENGINE == 'jruby' && JRuby::Util::ON_WINDOWS
|
data/lib/polyphony/core/sync.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'monitor'
|
4
|
+
|
3
5
|
module Polyphony
|
4
6
|
# Implements mutex lock for synchronizing access to a shared resource. This
|
5
7
|
# class replaces the stock `Thread::Mutex` class.
|
@@ -27,8 +29,7 @@ module Polyphony
|
|
27
29
|
#
|
28
30
|
# @return [nil]
|
29
31
|
def conditional_release
|
30
|
-
@store <<
|
31
|
-
@token = nil
|
32
|
+
@store << true
|
32
33
|
@holding_fiber = nil
|
33
34
|
end
|
34
35
|
|
@@ -37,7 +38,7 @@ module Polyphony
|
|
37
38
|
#
|
38
39
|
# @return [Fiber] current fiber
|
39
40
|
def conditional_reacquire
|
40
|
-
@
|
41
|
+
@store.shift
|
41
42
|
@holding_fiber = Fiber.current
|
42
43
|
end
|
43
44
|
|
@@ -60,9 +61,10 @@ module Polyphony
|
|
60
61
|
#
|
61
62
|
# @return [Mutex] self
|
62
63
|
def lock
|
64
|
+
check_dead_holder
|
63
65
|
raise ThreadError if owned?
|
64
66
|
|
65
|
-
@
|
67
|
+
@store.shift
|
66
68
|
@holding_fiber = Fiber.current
|
67
69
|
self
|
68
70
|
end
|
@@ -75,8 +77,7 @@ module Polyphony
|
|
75
77
|
raise ThreadError if !owned?
|
76
78
|
|
77
79
|
@holding_fiber = nil
|
78
|
-
@store <<
|
79
|
-
@token = nil
|
80
|
+
@store << true
|
80
81
|
end
|
81
82
|
|
82
83
|
# Attempts to obtain the lock and returns immediately. Returns `true` if the
|
@@ -86,7 +87,7 @@ module Polyphony
|
|
86
87
|
def try_lock
|
87
88
|
return false if @holding_fiber
|
88
89
|
|
89
|
-
@
|
90
|
+
@store.shift
|
90
91
|
@holding_fiber = Fiber.current
|
91
92
|
true
|
92
93
|
end
|
@@ -114,16 +115,79 @@ module Polyphony
|
|
114
115
|
#
|
115
116
|
# @return [any] return value of given block.
|
116
117
|
def synchronize_not_holding
|
117
|
-
@
|
118
|
+
@store.shift
|
118
119
|
begin
|
119
120
|
@holding_fiber = Fiber.current
|
120
121
|
yield
|
121
122
|
ensure
|
122
123
|
@holding_fiber = nil
|
123
|
-
@store <<
|
124
|
-
|
124
|
+
@store << true
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def check_dead_holder
|
129
|
+
return if !@holding_fiber&.dead?
|
130
|
+
|
131
|
+
@holding_fiber = nil
|
132
|
+
@store << true
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
# Implements a fiber-aware Monitor class. This class replaces the stock
|
137
|
+
# `Monitor` class.
|
138
|
+
class Monitor < Mutex
|
139
|
+
def enter
|
140
|
+
if @holding_fiber == Fiber.current
|
141
|
+
@holding_count += 1
|
142
|
+
else
|
143
|
+
lock
|
144
|
+
@holding_count = 1
|
145
|
+
end
|
146
|
+
end
|
147
|
+
alias_method :mon_enter, :enter
|
148
|
+
|
149
|
+
def exit
|
150
|
+
raise ThreadError if !owned?
|
151
|
+
|
152
|
+
@holding_count -= 1
|
153
|
+
unlock if @holding_count == 0
|
154
|
+
end
|
155
|
+
alias_method :mon_exit, :exit
|
156
|
+
|
157
|
+
def mon_check_owner
|
158
|
+
if Fiber.current == @holding_fiber
|
159
|
+
nil
|
160
|
+
else
|
161
|
+
raise ThreadError, 'current fiber not owner'
|
125
162
|
end
|
126
163
|
end
|
164
|
+
|
165
|
+
def mon_locked?
|
166
|
+
!!@holding_fiber
|
167
|
+
end
|
168
|
+
|
169
|
+
def mon_owned?
|
170
|
+
@holding_fiber == Fiber.current
|
171
|
+
end
|
172
|
+
|
173
|
+
alias_method :mon_synchronize, :synchronize
|
174
|
+
|
175
|
+
def new_cond
|
176
|
+
MonitorMixin::ConditionVariable.new(self)
|
177
|
+
end
|
178
|
+
|
179
|
+
def try_enter
|
180
|
+
check_dead_holder
|
181
|
+
return false if @holding_fiber
|
182
|
+
|
183
|
+
enter
|
184
|
+
true
|
185
|
+
end
|
186
|
+
alias_method :try_mon_enter, :try_enter
|
187
|
+
|
188
|
+
def wait_for_cond(cond, timeout)
|
189
|
+
cond.wait(self, timeout)
|
190
|
+
end
|
127
191
|
end
|
128
192
|
|
129
193
|
# Implements a fiber-aware ConditionVariable
|
@@ -136,12 +200,18 @@ module Polyphony
|
|
136
200
|
# Waits for the condition variable to be signalled.
|
137
201
|
#
|
138
202
|
# @param mutex [Polyphony::Mutex] mutex to release while waiting for signal
|
139
|
-
# @param
|
203
|
+
# @param timeout [Number, nil] timeout in seconds, or nil for no timeout
|
140
204
|
# @return [any]
|
141
|
-
def wait(mutex,
|
205
|
+
def wait(mutex, timeout = nil)
|
142
206
|
mutex.conditional_release
|
143
207
|
@queue << Fiber.current
|
144
|
-
|
208
|
+
if timeout
|
209
|
+
move_on_after(timeout, with_value: false) { Polyphony.backend_wait_event(true); true }
|
210
|
+
else
|
211
|
+
Polyphony.backend_wait_event(true)
|
212
|
+
true
|
213
|
+
end
|
214
|
+
ensure
|
145
215
|
mutex.conditional_reacquire
|
146
216
|
end
|
147
217
|
|
data/lib/polyphony/core/timer.rb
CHANGED
@@ -192,38 +192,20 @@ module Polyphony
|
|
192
192
|
::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
|
193
193
|
end
|
194
194
|
|
195
|
-
# Converts a timeout record's exception spec to an exception instance.
|
196
|
-
#
|
197
|
-
# @param record [Array, Class, Exception, String] exception spec
|
198
|
-
# @return [Exception] exception instance
|
199
|
-
def timeout_exception(record)
|
200
|
-
case (exception = record[:exception])
|
201
|
-
when Array
|
202
|
-
exception[0].new(exception[1])
|
203
|
-
when Class
|
204
|
-
exception.new
|
205
|
-
when Exception
|
206
|
-
exception
|
207
|
-
else
|
208
|
-
RuntimeError.new(exception)
|
209
|
-
end
|
210
|
-
end
|
211
|
-
|
212
195
|
# Runs a timer iteration, invoking any timeouts that are due.
|
213
196
|
def update
|
214
197
|
return if @timeouts.empty?
|
215
198
|
|
216
|
-
@timeouts.each do |
|
217
|
-
next if
|
199
|
+
@timeouts.each do |f, r|
|
200
|
+
next if r[:target_stamp] > now
|
218
201
|
|
219
|
-
|
220
|
-
|
202
|
+
f.schedule(
|
203
|
+
r[:exception] ? Exception.instantiate(r[:exception]) : r[:value]
|
204
|
+
)
|
221
205
|
|
222
|
-
next
|
206
|
+
next if !r[:recurring]
|
223
207
|
|
224
|
-
while
|
225
|
-
record[:target_stamp] += record[:interval]
|
226
|
-
end
|
208
|
+
r[:target_stamp] += r[:interval] while r[:target_stamp] <= now
|
227
209
|
end
|
228
210
|
end
|
229
211
|
end
|
@@ -6,6 +6,21 @@ class ::Exception
|
|
6
6
|
# Set to true to disable sanitizing the backtrace (to remove frames occuring
|
7
7
|
# in the Polyphony code itself.)
|
8
8
|
attr_accessor :__disable_sanitized_backtrace__
|
9
|
+
|
10
|
+
# Creates an exception instance from the given error according to its type.
|
11
|
+
#
|
12
|
+
# @param error [nil, String, Class, Exception] error
|
13
|
+
# @param message [nil, String] error message
|
14
|
+
# @return [Exception] exception instance
|
15
|
+
def instantiate(error = nil, message = nil)
|
16
|
+
case error
|
17
|
+
when String then RuntimeError.new(error)
|
18
|
+
when Array then error[0].new(error[1])
|
19
|
+
when Class then error.new(message)
|
20
|
+
when Exception then error
|
21
|
+
else RuntimeError.new
|
22
|
+
end
|
23
|
+
end
|
9
24
|
end
|
10
25
|
|
11
26
|
# Set to the fiber in which the exception was *originally* raised (in case the
|
@@ -167,7 +167,7 @@ class ::Fiber
|
|
167
167
|
# @param any [Exception] exception to raise
|
168
168
|
# @return [Fiber] self
|
169
169
|
def raise(*args)
|
170
|
-
error =
|
170
|
+
error = Exception.instantiate(*args)
|
171
171
|
schedule(error)
|
172
172
|
self
|
173
173
|
end
|
@@ -182,13 +182,24 @@ class ::Fiber
|
|
182
182
|
raise Polyphony::Interjection.new(block)
|
183
183
|
end
|
184
184
|
|
185
|
-
#
|
185
|
+
# Waits for the fiber to terminate, and returns its return value (the result
|
186
|
+
# of its last statement). If the fiber has terminated with an ancaught
|
187
|
+
# exception, the exception will be raised.
|
186
188
|
#
|
187
|
-
#
|
189
|
+
# f = spin { :foo; :bar }
|
190
|
+
# f.await #=> :bar
|
191
|
+
#
|
192
|
+
# @overload await
|
193
|
+
# @return [any] fiber's return value
|
194
|
+
# @overload join
|
195
|
+
# @return [any] fiber's return value
|
196
|
+
# @overload value
|
197
|
+
# @return [any] fiber's return value
|
188
198
|
def await
|
189
199
|
Fiber.await(self).first
|
190
200
|
end
|
191
201
|
alias_method :join, :await
|
202
|
+
alias_method :value, :await
|
192
203
|
|
193
204
|
#############################
|
194
205
|
# Fiber supervision methods #
|
@@ -648,16 +659,6 @@ class ::Fiber
|
|
648
659
|
|
649
660
|
private
|
650
661
|
|
651
|
-
# @!visibility private
|
652
|
-
def error_from_raise_args(args)
|
653
|
-
case (arg = args.shift)
|
654
|
-
when String then RuntimeError.new(arg)
|
655
|
-
when Class then arg.new(args.shift)
|
656
|
-
when Exception then arg
|
657
|
-
else RuntimeError.new
|
658
|
-
end
|
659
|
-
end
|
660
|
-
|
661
662
|
# @!visibility private
|
662
663
|
def supervise_opts_to_block(opts)
|
663
664
|
block = opts[:on_done] || opts[:on_error]
|
@@ -59,12 +59,12 @@ class ::IO
|
|
59
59
|
end
|
60
60
|
end
|
61
61
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
62
|
+
alias_method :orig_readlines, :readlines
|
63
|
+
def readlines(name, sep = $/, limit = nil, getline_args = EMPTY_HASH)
|
64
|
+
File.open(name, 'r') do |f|
|
65
|
+
f.readlines(sep, **getline_args)
|
66
|
+
end
|
67
|
+
end
|
68
68
|
|
69
69
|
# @!visibility private
|
70
70
|
alias_method :orig_write, :write
|
@@ -87,6 +87,42 @@ class ::IO
|
|
87
87
|
Open3.popen2(cmd) { |_i, o, _t| yield o }
|
88
88
|
end
|
89
89
|
|
90
|
+
def copy_stream(src, dst, src_length = nil, src_offset = 0)
|
91
|
+
close_src = false
|
92
|
+
close_dst = false
|
93
|
+
if !src.respond_to?(:readpartial)
|
94
|
+
src = File.open(src, 'r+')
|
95
|
+
close_src = true
|
96
|
+
end
|
97
|
+
if !dst.respond_to?(:readpartial)
|
98
|
+
dst = File.open(dst, 'w+')
|
99
|
+
close_dst = true
|
100
|
+
end
|
101
|
+
src.seek(src_offset) if src_offset > 0
|
102
|
+
|
103
|
+
pipe = Polyphony::Pipe.new
|
104
|
+
|
105
|
+
pipe_to_dst = spin { dst.splice_from(pipe, -65536) }
|
106
|
+
|
107
|
+
count = 0
|
108
|
+
if src_length
|
109
|
+
while count < src_length
|
110
|
+
count += pipe.splice_from(src, src_length)
|
111
|
+
end
|
112
|
+
else
|
113
|
+
count = pipe.splice_from(src, -65536)
|
114
|
+
end
|
115
|
+
|
116
|
+
pipe.close
|
117
|
+
pipe_to_dst.await
|
118
|
+
|
119
|
+
count
|
120
|
+
ensure
|
121
|
+
pipe_to_dst&.stop
|
122
|
+
src.close if close_src
|
123
|
+
dst.close if close_dst
|
124
|
+
end
|
125
|
+
|
90
126
|
# Splices from one IO to another IO. At least one of the IOs must be a pipe.
|
91
127
|
# If maxlen is negative, splices repeatedly using absolute value of maxlen
|
92
128
|
# until EOF is encountered.
|
@@ -109,6 +145,17 @@ class ::IO
|
|
109
145
|
Polyphony.backend_double_splice(src, dest)
|
110
146
|
end
|
111
147
|
|
148
|
+
if !Polyphony.respond_to?(:backend_double_splice)
|
149
|
+
def double_splice(src, dest)
|
150
|
+
pipe = Polyphony::Pipe.new
|
151
|
+
f = spin { Polyphony.backend_splice(pipe, dest, -65536) }
|
152
|
+
Polyphony.backend_splice(src, pipe, -65536)
|
153
|
+
pipe.close
|
154
|
+
ensure
|
155
|
+
f.stop
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
112
159
|
# Tees data from the source to the desination.
|
113
160
|
#
|
114
161
|
# @param src [IO, Polyphony::Pipe] source to tee from
|
@@ -120,11 +167,6 @@ class ::IO
|
|
120
167
|
end
|
121
168
|
|
122
169
|
if RUBY_PLATFORM !~ /linux/
|
123
|
-
# @!visibility private
|
124
|
-
def double_splice(src, dest)
|
125
|
-
raise NotImplementedError
|
126
|
-
end
|
127
|
-
|
128
170
|
# @!visibility private
|
129
171
|
def tee(src, dest, maxlen)
|
130
172
|
raise NotImplementedError
|
@@ -203,7 +245,7 @@ class ::IO
|
|
203
245
|
|
204
246
|
@read_buffer ||= +''
|
205
247
|
result = Polyphony.backend_read(self, @read_buffer, len, true, -1)
|
206
|
-
return
|
248
|
+
return '' unless result
|
207
249
|
|
208
250
|
already_read = @read_buffer
|
209
251
|
@read_buffer = +''
|
@@ -280,7 +322,7 @@ class ::IO
|
|
280
322
|
yield line
|
281
323
|
end
|
282
324
|
|
283
|
-
result =
|
325
|
+
result = Polyphony.backend_read(self, @read_buffer, 8192, false, -1)
|
284
326
|
return self if !result
|
285
327
|
end
|
286
328
|
rescue EOFError
|
@@ -436,7 +478,7 @@ class ::IO
|
|
436
478
|
def close
|
437
479
|
return if closed?
|
438
480
|
|
439
|
-
Polyphony.backend_close(self)
|
481
|
+
Polyphony.backend_close(self) rescue nil
|
440
482
|
nil
|
441
483
|
end
|
442
484
|
|
@@ -240,7 +240,7 @@ class ::Object
|
|
240
240
|
fiber = Fiber.current
|
241
241
|
canceller = spin do
|
242
242
|
Polyphony.backend_sleep(interval)
|
243
|
-
exception =
|
243
|
+
exception = Exception.instantiate(exception)
|
244
244
|
exception.raising_fiber = Fiber.current
|
245
245
|
fiber.cancel(exception)
|
246
246
|
end
|
@@ -249,18 +249,6 @@ class ::Object
|
|
249
249
|
canceller.stop
|
250
250
|
end
|
251
251
|
|
252
|
-
# Converts the given exception spec to an exception instance.
|
253
|
-
#
|
254
|
-
# @param exception [Exception, Class, Array<class, message>] exception spec
|
255
|
-
# @return [Exception] exception instance
|
256
|
-
def cancel_exception(exception)
|
257
|
-
case exception
|
258
|
-
when Class then exception.new
|
259
|
-
when Array then exception[0].new(exception[1])
|
260
|
-
else RuntimeError.new(exception)
|
261
|
-
end
|
262
|
-
end
|
263
|
-
|
264
252
|
# Helper method for performing `#spin_loop` without throttling. Spins up a
|
265
253
|
# new fiber in which to run the loop.
|
266
254
|
#
|