polyphony 1.5 → 1.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +3 -0
- data/CHANGELOG.md +14 -0
- data/TODO.md +0 -4
- data/ext/polyphony/backend_io_uring.c +34 -1
- 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 +4 -7
- data/ext/polyphony/event.c +21 -0
- data/ext/polyphony/extconf.rb +20 -18
- data/ext/polyphony/fiber.c +0 -2
- data/ext/polyphony/polyphony.c +2 -0
- data/ext/polyphony/polyphony.h +5 -0
- 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/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/thread.rb +19 -27
- 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 +7 -2
- data/test/test_event.rb +10 -3
- data/test/test_ext.rb +2 -1
- data/test/test_fiber.rb +16 -4
- data/test/test_global_api.rb +13 -12
- 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 -3
- 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 +14 -3
- 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
|
#
|