polyphony 0.99 → 0.99.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/FUNDING.yml +1 -1
- data/.rubocop.yml +3 -3
- data/.yardopts +30 -0
- data/CHANGELOG.md +4 -0
- data/LICENSE +1 -1
- data/README.md +63 -29
- data/Rakefile +1 -5
- data/TODO.md +0 -4
- data/docs/{main-concepts/concurrency.md → concurrency.md} +2 -9
- data/docs/{main-concepts/design-principles.md → design-principles.md} +3 -9
- data/docs/{main-concepts/exception-handling.md → exception-handling.md} +2 -9
- data/docs/{main-concepts/extending.md → extending.md} +2 -9
- data/docs/faq.md +3 -16
- data/docs/{main-concepts/fiber-scheduling.md → fiber-scheduling.md} +1 -9
- data/docs/link_rewriter.rb +16 -0
- data/docs/{getting-started/overview.md → overview.md} +1 -30
- data/docs/{getting-started/tutorial.md → tutorial.md} +3 -28
- data/docs/{_posts/2020-07-26-polyphony-0.44.md → whats-new.md} +3 -1
- data/examples/adapters/redis_client.rb +3 -2
- data/examples/io/echo_server.rb +1 -1
- data/examples/io/echo_server_plain_ruby.rb +26 -0
- data/ext/polyphony/backend_io_uring.c +154 -9
- data/ext/polyphony/backend_io_uring_context.c +21 -12
- data/ext/polyphony/backend_io_uring_context.h +12 -7
- data/ext/polyphony/backend_libev.c +1 -1
- data/ext/polyphony/extconf.rb +24 -8
- data/ext/polyphony/fiber.c +79 -2
- data/ext/polyphony/io_extensions.c +53 -0
- data/ext/polyphony/pipe.c +42 -2
- data/ext/polyphony/polyphony.c +345 -31
- data/ext/polyphony/polyphony.h +9 -2
- data/ext/polyphony/queue.c +181 -0
- data/ext/polyphony/ring_buffer.c +0 -1
- data/ext/polyphony/runqueue.c +8 -1
- data/ext/polyphony/runqueue_ring_buffer.c +13 -0
- data/ext/polyphony/runqueue_ring_buffer.h +2 -1
- data/ext/polyphony/socket_extensions.c +6 -0
- data/ext/polyphony/thread.c +34 -2
- data/lib/polyphony/adapters/process.rb +11 -1
- data/lib/polyphony/adapters/sequel.rb +1 -1
- data/lib/polyphony/core/channel.rb +2 -0
- data/lib/polyphony/core/debug.rb +1 -1
- data/lib/polyphony/core/global_api.rb +25 -24
- data/lib/polyphony/core/resource_pool.rb +7 -6
- data/lib/polyphony/core/sync.rb +2 -2
- data/lib/polyphony/core/thread_pool.rb +3 -3
- data/lib/polyphony/core/timer.rb +8 -8
- data/lib/polyphony/extensions/exception.rb +2 -0
- data/lib/polyphony/extensions/fiber.rb +15 -13
- data/lib/polyphony/extensions/io.rb +127 -5
- data/lib/polyphony/extensions/kernel.rb +20 -2
- data/lib/polyphony/extensions/openssl.rb +100 -11
- data/lib/polyphony/extensions/pipe.rb +103 -7
- data/lib/polyphony/extensions/process.rb +13 -1
- data/lib/polyphony/extensions/socket.rb +93 -27
- data/lib/polyphony/extensions/thread.rb +9 -1
- data/lib/polyphony/extensions/timeout.rb +1 -1
- data/lib/polyphony/version.rb +2 -1
- data/lib/polyphony.rb +27 -7
- data/polyphony.gemspec +1 -8
- data/test/stress.rb +1 -1
- data/test/test_global_api.rb +45 -7
- data/test/test_socket.rb +96 -0
- data/test/test_timer.rb +5 -5
- metadata +17 -40
- data/docs/_config.yml +0 -64
- data/docs/_includes/head.html +0 -40
- data/docs/_includes/title.html +0 -1
- data/docs/_sass/custom/custom.scss +0 -10
- data/docs/_sass/overrides.scss +0 -0
- data/docs/api-reference/exception.md +0 -31
- data/docs/api-reference/fiber.md +0 -425
- data/docs/api-reference/index.md +0 -9
- data/docs/api-reference/io.md +0 -36
- data/docs/api-reference/object.md +0 -99
- data/docs/api-reference/polyphony-baseexception.md +0 -33
- data/docs/api-reference/polyphony-cancel.md +0 -26
- data/docs/api-reference/polyphony-moveon.md +0 -24
- data/docs/api-reference/polyphony-net.md +0 -20
- data/docs/api-reference/polyphony-process.md +0 -28
- data/docs/api-reference/polyphony-resourcepool.md +0 -59
- data/docs/api-reference/polyphony-restart.md +0 -18
- data/docs/api-reference/polyphony-terminate.md +0 -18
- data/docs/api-reference/polyphony-threadpool.md +0 -67
- data/docs/api-reference/polyphony-throttler.md +0 -77
- data/docs/api-reference/polyphony.md +0 -36
- data/docs/api-reference/thread.md +0 -88
- data/docs/favicon.ico +0 -0
- data/docs/getting-started/index.md +0 -10
- data/docs/getting-started/installing.md +0 -34
- /data/{docs/assets/img → assets}/echo-fibers.svg +0 -0
- /data/{docs → assets}/polyphony-logo.png +0 -0
- /data/{docs/assets/img → assets}/sleeping-fiber.svg +0 -0
|
@@ -8,7 +8,7 @@ module Polyphony
|
|
|
8
8
|
# Initializes a new resource pool.
|
|
9
9
|
#
|
|
10
10
|
# @param opts [Hash] options
|
|
11
|
-
# @
|
|
11
|
+
# @yield [] allocator block
|
|
12
12
|
def initialize(opts, &block)
|
|
13
13
|
@allocator = block
|
|
14
14
|
@limit = opts[:limit] || 4
|
|
@@ -36,7 +36,7 @@ module Polyphony
|
|
|
36
36
|
# db.query(sql).to_a
|
|
37
37
|
# end
|
|
38
38
|
#
|
|
39
|
-
# @
|
|
39
|
+
# @yield [any] code to run
|
|
40
40
|
# @return [any] return value of block
|
|
41
41
|
def acquire(&block)
|
|
42
42
|
fiber = Fiber.current
|
|
@@ -54,13 +54,14 @@ module Polyphony
|
|
|
54
54
|
# }
|
|
55
55
|
#
|
|
56
56
|
# @param sym [Symbol] method name
|
|
57
|
-
# @param
|
|
58
|
-
# @
|
|
57
|
+
# @param args [Array<any>] method arguments
|
|
58
|
+
# @yield [any] block passed to method
|
|
59
|
+
# @return [any] result of method call
|
|
59
60
|
def method_missing(sym, *args, &block)
|
|
60
61
|
acquire { |r| r.send(sym, *args, &block) }
|
|
61
62
|
end
|
|
62
63
|
|
|
63
|
-
#
|
|
64
|
+
# @!visibility private
|
|
64
65
|
def respond_to_missing?(*_args)
|
|
65
66
|
true
|
|
66
67
|
end
|
|
@@ -87,7 +88,7 @@ module Polyphony
|
|
|
87
88
|
# Acquires a resource from stock, yielding it to the given block.
|
|
88
89
|
#
|
|
89
90
|
# @param fiber [Fiber] the fiber the resource will be associated with
|
|
90
|
-
# @
|
|
91
|
+
# @yield [any] given block
|
|
91
92
|
# @return [any] return value of block
|
|
92
93
|
def acquire_from_stock(fiber)
|
|
93
94
|
add_to_stock if (@stock.empty? || @stock.pending?) && @size < @limit
|
data/lib/polyphony/core/sync.rb
CHANGED
|
@@ -17,7 +17,7 @@ module Polyphony
|
|
|
17
17
|
# This method is re-entrant. Recursive calls from the given block will not
|
|
18
18
|
# block.
|
|
19
19
|
#
|
|
20
|
-
# @
|
|
20
|
+
# @yield [] code to run
|
|
21
21
|
# @return [any] return value of block
|
|
22
22
|
def synchronize(&block)
|
|
23
23
|
return yield if @holding_fiber == Fiber.current
|
|
@@ -140,7 +140,7 @@ module Polyphony
|
|
|
140
140
|
# Waits for the condition variable to be signalled.
|
|
141
141
|
#
|
|
142
142
|
# @param mutex [Polyphony::Mutex] mutex to release while waiting for signal
|
|
143
|
-
# @param
|
|
143
|
+
# @param _timeout [Number, nil] timeout in seconds (currently not implemented)
|
|
144
144
|
# @return [void]
|
|
145
145
|
def wait(mutex, _timeout = nil)
|
|
146
146
|
mutex.conditional_release
|
|
@@ -12,7 +12,7 @@ module Polyphony
|
|
|
12
12
|
|
|
13
13
|
# Runs the given block on an available thread from the default thread pool.
|
|
14
14
|
#
|
|
15
|
-
# @
|
|
15
|
+
# @yield [] given block
|
|
16
16
|
# @return [any] return value of given block
|
|
17
17
|
def self.process(&block)
|
|
18
18
|
@default_pool ||= new
|
|
@@ -41,7 +41,7 @@ module Polyphony
|
|
|
41
41
|
|
|
42
42
|
# Runs the given block on an available thread from the pool.
|
|
43
43
|
#
|
|
44
|
-
# @
|
|
44
|
+
# @yield [] given block
|
|
45
45
|
# @return [any] return value of block
|
|
46
46
|
def process(&block)
|
|
47
47
|
setup unless @task_queue
|
|
@@ -55,7 +55,7 @@ module Polyphony
|
|
|
55
55
|
# method does not block. The task will be performed once a thread becomes
|
|
56
56
|
# available.
|
|
57
57
|
#
|
|
58
|
-
# @
|
|
58
|
+
# @yield [] given block
|
|
59
59
|
# @return [Polyphony::ThreadPool] self
|
|
60
60
|
def cast(&block)
|
|
61
61
|
setup unless @task_queue
|
data/lib/polyphony/core/timer.rb
CHANGED
|
@@ -11,7 +11,7 @@ module Polyphony
|
|
|
11
11
|
# Initializes a new timer with the given resolution.
|
|
12
12
|
#
|
|
13
13
|
# @param tag [any] tag to use for the timer's fiber
|
|
14
|
-
# @param resolution
|
|
14
|
+
# @param resolution [Number] timer granularity in seconds or fractions thereof
|
|
15
15
|
def initialize(tag = nil, resolution:)
|
|
16
16
|
@fiber = spin_loop(tag, interval: resolution) { update }
|
|
17
17
|
@timeouts = {}
|
|
@@ -43,8 +43,8 @@ module Polyphony
|
|
|
43
43
|
# Spins up a fiber that will run the given block after sleeping for the
|
|
44
44
|
# given delay.
|
|
45
45
|
#
|
|
46
|
-
# @param
|
|
47
|
-
# @
|
|
46
|
+
# @param interval [Number] delay in seconds before running the given block
|
|
47
|
+
# @yield [] block to run
|
|
48
48
|
# @return [Fiber] spun fiber
|
|
49
49
|
def after(interval, &block)
|
|
50
50
|
spin do
|
|
@@ -57,7 +57,7 @@ module Polyphony
|
|
|
57
57
|
# consecutive iterations.
|
|
58
58
|
#
|
|
59
59
|
# @param interval [Number] interval between consecutive iterations in seconds
|
|
60
|
-
# @
|
|
60
|
+
# @yield [] block to run
|
|
61
61
|
# @return [void]
|
|
62
62
|
def every(interval)
|
|
63
63
|
fiber = Fiber.current
|
|
@@ -109,8 +109,8 @@ module Polyphony
|
|
|
109
109
|
# end
|
|
110
110
|
#
|
|
111
111
|
# @param interval [Number] timout in seconds
|
|
112
|
-
# @param with_exception
|
|
113
|
-
# @
|
|
112
|
+
# @param with_exception [Class, Exception] exception or exception class
|
|
113
|
+
# @yield [Canceller] block to execute
|
|
114
114
|
# @return [any] block's return value
|
|
115
115
|
def cancel_after(interval, with_exception: Polyphony::Cancel)
|
|
116
116
|
fiber = Fiber.current
|
|
@@ -163,8 +163,8 @@ module Polyphony
|
|
|
163
163
|
# end
|
|
164
164
|
#
|
|
165
165
|
# @param interval [Number] timout in seconds
|
|
166
|
-
# @param with_value
|
|
167
|
-
# @
|
|
166
|
+
# @param with_value [any] return value in case of timeout
|
|
167
|
+
# @yield [Fiber] block to execute
|
|
168
168
|
# @return [any] block's return value
|
|
169
169
|
def move_on_after(interval, with_value: nil)
|
|
170
170
|
fiber = Fiber.current
|
|
@@ -18,6 +18,7 @@ class ::Exception
|
|
|
18
18
|
# Set to the fiber from which the exception was raised.
|
|
19
19
|
attr_accessor :raising_fiber
|
|
20
20
|
|
|
21
|
+
# @!visibility private
|
|
21
22
|
alias_method :orig_initialize, :initialize
|
|
22
23
|
|
|
23
24
|
# Initializes the exception with the given arguments.
|
|
@@ -26,6 +27,7 @@ class ::Exception
|
|
|
26
27
|
orig_initialize(*args)
|
|
27
28
|
end
|
|
28
29
|
|
|
30
|
+
# @!visibility private
|
|
29
31
|
alias_method :orig_backtrace, :backtrace
|
|
30
32
|
|
|
31
33
|
# Returns the backtrace for the exception. If
|
|
@@ -116,7 +116,7 @@ module Polyphony
|
|
|
116
116
|
# operation will be resumed. This API is experimental and might be removed
|
|
117
117
|
# in the future.
|
|
118
118
|
#
|
|
119
|
-
# @
|
|
119
|
+
# @yield [any] given block
|
|
120
120
|
# @return [Fiber] self
|
|
121
121
|
def interject(&block)
|
|
122
122
|
raise Polyphony::Interjection.new(block)
|
|
@@ -132,7 +132,7 @@ module Polyphony
|
|
|
132
132
|
|
|
133
133
|
private
|
|
134
134
|
|
|
135
|
-
#
|
|
135
|
+
# @!visibility private
|
|
136
136
|
def error_from_raise_args(args)
|
|
137
137
|
case (arg = args.shift)
|
|
138
138
|
when String then RuntimeError.new(arg)
|
|
@@ -171,10 +171,11 @@ module Polyphony
|
|
|
171
171
|
#
|
|
172
172
|
# This method blocks indefinitely.
|
|
173
173
|
#
|
|
174
|
-
# @param
|
|
175
|
-
# @
|
|
176
|
-
# @
|
|
177
|
-
# @
|
|
174
|
+
# @param fibers [Array<Fiber>] fibers to supervise
|
|
175
|
+
# @option opts [Proc, nil] :on_done proc to call when a supervised fiber is terminated
|
|
176
|
+
# @option opts [Proc, nil] :on_error proc to call when a supervised fiber is terminated with an exception
|
|
177
|
+
# @option opts [:always, :on_error, nil] :restart whether to restart terminated fibers
|
|
178
|
+
# @yield [] supervisor block
|
|
178
179
|
# @return [void]
|
|
179
180
|
def supervise(*fibers, **opts, &block)
|
|
180
181
|
block ||= supervise_opts_to_block(opts)
|
|
@@ -198,7 +199,7 @@ module Polyphony
|
|
|
198
199
|
|
|
199
200
|
private
|
|
200
201
|
|
|
201
|
-
#
|
|
202
|
+
# @!visibility private
|
|
202
203
|
def supervise_opts_to_block(opts)
|
|
203
204
|
block = opts[:on_done] || opts[:on_error]
|
|
204
205
|
restart = opts[:restart]
|
|
@@ -228,7 +229,7 @@ module Polyphony
|
|
|
228
229
|
# terminates with an uncaught exception, `Fiber.await` will await all the
|
|
229
230
|
# other fibers to terminate, then reraise the exception.
|
|
230
231
|
#
|
|
231
|
-
# @param
|
|
232
|
+
# @param fibers [Array<Fiber>] fibers to wait for
|
|
232
233
|
# @return [Array<any>] return values of given fibers
|
|
233
234
|
def await(*fibers)
|
|
234
235
|
return [] if fibers.empty?
|
|
@@ -267,7 +268,7 @@ module Polyphony
|
|
|
267
268
|
# array containing the first terminated fiber and its return value. If an
|
|
268
269
|
# exception occurs in one of the given fibers, it will be reraised.
|
|
269
270
|
#
|
|
270
|
-
# @param
|
|
271
|
+
# @param fibers [Array<Fiber>] Fibers to wait for
|
|
271
272
|
# @return [Array] Array containing the first terminated fiber and its return value
|
|
272
273
|
def select(*fibers)
|
|
273
274
|
return nil if fibers.empty?
|
|
@@ -301,7 +302,7 @@ module Polyphony
|
|
|
301
302
|
# also be scheduled with priority. This method is mainly used trapping
|
|
302
303
|
# signals (see also the patched `Kernel#trap`)
|
|
303
304
|
#
|
|
304
|
-
# @
|
|
305
|
+
# @yield [] given block
|
|
305
306
|
# @return [void]
|
|
306
307
|
def schedule_priority_oob_fiber(&block)
|
|
307
308
|
oob_fiber = Fiber.new do
|
|
@@ -322,7 +323,7 @@ module Polyphony
|
|
|
322
323
|
|
|
323
324
|
private
|
|
324
325
|
|
|
325
|
-
#
|
|
326
|
+
# @!visibility private
|
|
326
327
|
def prepare_oob_fiber(fiber, block)
|
|
327
328
|
fiber.oob = true
|
|
328
329
|
fiber.tag = :oob
|
|
@@ -348,7 +349,7 @@ module Polyphony
|
|
|
348
349
|
#
|
|
349
350
|
# @param tag [any] child fiber's tag
|
|
350
351
|
# @param orig_caller [Array<String>] caller to set for fiber
|
|
351
|
-
# @
|
|
352
|
+
# @yield [any] child fiber's block
|
|
352
353
|
# @return [Fiber] child fiber
|
|
353
354
|
def spin(tag = nil, orig_caller = Kernel.caller, &block)
|
|
354
355
|
f = Fiber.new { |v| f.run(v) }
|
|
@@ -456,10 +457,11 @@ module Polyphony
|
|
|
456
457
|
|
|
457
458
|
# Removes a child fiber reference. Used internally.
|
|
458
459
|
#
|
|
459
|
-
# @param
|
|
460
|
+
# @param child_fiber [Fiber] child fiber to be removed
|
|
460
461
|
# @return [Fiber] self
|
|
461
462
|
def remove_child(child_fiber)
|
|
462
463
|
@children.delete(child_fiber) if @children
|
|
464
|
+
self
|
|
463
465
|
end
|
|
464
466
|
end
|
|
465
467
|
|
|
@@ -5,9 +5,10 @@ require 'open3'
|
|
|
5
5
|
# IO extensions
|
|
6
6
|
class ::IO
|
|
7
7
|
class << self
|
|
8
|
+
# @!visibility private
|
|
8
9
|
alias_method :orig_binread, :binread
|
|
9
10
|
|
|
10
|
-
#
|
|
11
|
+
# @!visibility private
|
|
11
12
|
def binread(name, length = nil, offset = nil)
|
|
12
13
|
File.open(name, 'rb:ASCII-8BIT') do |f|
|
|
13
14
|
f.seek(offset) if offset
|
|
@@ -15,7 +16,10 @@ class ::IO
|
|
|
15
16
|
end
|
|
16
17
|
end
|
|
17
18
|
|
|
19
|
+
# @!visibility private
|
|
18
20
|
alias_method :orig_binwrite, :binwrite
|
|
21
|
+
|
|
22
|
+
# @!visibility private
|
|
19
23
|
def binwrite(name, string, offset = nil)
|
|
20
24
|
File.open(name, 'wb:ASCII-8BIT') do |f|
|
|
21
25
|
f.seek(offset) if offset
|
|
@@ -23,13 +27,14 @@ class ::IO
|
|
|
23
27
|
end
|
|
24
28
|
end
|
|
25
29
|
|
|
30
|
+
# @!visibility private
|
|
26
31
|
EMPTY_HASH = {}.freeze
|
|
27
32
|
|
|
33
|
+
# @!visibility private
|
|
28
34
|
alias_method :orig_foreach, :foreach
|
|
29
|
-
def foreach(name, sep = $/, limit = nil, getline_args = EMPTY_HASH, &block)
|
|
30
|
-
# IO.orig_read(name).each_line(&block)
|
|
31
|
-
# raise NotImplementedError
|
|
32
35
|
|
|
36
|
+
# @!visibility private
|
|
37
|
+
def foreach(name, sep = $/, limit = nil, getline_args = EMPTY_HASH, &block)
|
|
33
38
|
if sep.is_a?(Integer)
|
|
34
39
|
sep = $/
|
|
35
40
|
limit = sep
|
|
@@ -39,7 +44,10 @@ class ::IO
|
|
|
39
44
|
end
|
|
40
45
|
end
|
|
41
46
|
|
|
47
|
+
# @!visibility private
|
|
42
48
|
alias_method :orig_read, :read
|
|
49
|
+
|
|
50
|
+
# @!visibility private
|
|
43
51
|
def read(name, length = nil, offset = nil, opt = EMPTY_HASH)
|
|
44
52
|
if length.is_a?(Hash)
|
|
45
53
|
opt = length
|
|
@@ -58,7 +66,10 @@ class ::IO
|
|
|
58
66
|
# end
|
|
59
67
|
# end
|
|
60
68
|
|
|
69
|
+
# @!visibility private
|
|
61
70
|
alias_method :orig_write, :write
|
|
71
|
+
|
|
72
|
+
# @!visibility private
|
|
62
73
|
def write(name, string, offset = nil, opt = EMPTY_HASH)
|
|
63
74
|
File.open(name, opt[:mode] || 'w') do |f|
|
|
64
75
|
f.seek(offset) if offset
|
|
@@ -66,22 +77,44 @@ class ::IO
|
|
|
66
77
|
end
|
|
67
78
|
end
|
|
68
79
|
|
|
80
|
+
# @!visibility private
|
|
69
81
|
alias_method :orig_popen, :popen
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
# @!visibility private
|
|
70
85
|
def popen(cmd, mode = 'r')
|
|
71
86
|
return orig_popen(cmd, mode) unless block_given?
|
|
72
87
|
|
|
73
88
|
Open3.popen2(cmd) { |_i, o, _t| yield o }
|
|
74
89
|
end
|
|
75
90
|
|
|
91
|
+
# Splices from one IO to another IO. At least one of the IOs must be a pipe.
|
|
92
|
+
#
|
|
93
|
+
# @param src [IO, Polyphony::Pipe] source to splice from
|
|
94
|
+
# @param dest [IO, Polyphony::Pipe] destination to splice to
|
|
95
|
+
# @param maxlen [Integer] maximum bytes to splice
|
|
96
|
+
# @return [Integer] bytes spliced
|
|
76
97
|
def splice(src, dest, maxlen)
|
|
77
98
|
Polyphony.backend_splice(src, dest, maxlen)
|
|
78
99
|
end
|
|
79
100
|
|
|
80
101
|
if RUBY_PLATFORM =~ /linux/
|
|
102
|
+
# Creates a pipe and splices data between the two given IOs using the
|
|
103
|
+
# pipe, splicing until EOF.
|
|
104
|
+
#
|
|
105
|
+
# @param src [IO, Polyphony::Pipe] source to splice from
|
|
106
|
+
# @param dest [IO, Polyphony::Pipe] destination to splice to
|
|
107
|
+
# @return [Integer] total bytes spliced
|
|
81
108
|
def double_splice(src, dest)
|
|
82
109
|
Polyphony.backend_double_splice(src, dest)
|
|
83
110
|
end
|
|
84
111
|
|
|
112
|
+
# Tees data from the source to the desination.
|
|
113
|
+
#
|
|
114
|
+
# @param src [IO, Polyphony::Pipe] source to tee from
|
|
115
|
+
# @param dest [IO, Polyphony::Pipe] destination to tee to
|
|
116
|
+
# @param maxlen [Integer] maximum bytes to tee
|
|
117
|
+
# @return [Integer] total bytes teed
|
|
85
118
|
def tee(src, dest, maxlen)
|
|
86
119
|
Polyphony.backend_tee(src, dest, maxlen)
|
|
87
120
|
end
|
|
@@ -91,10 +124,12 @@ end
|
|
|
91
124
|
|
|
92
125
|
# IO instance method patches
|
|
93
126
|
class ::IO
|
|
127
|
+
# @!visibility private
|
|
94
128
|
def __read_method__
|
|
95
129
|
:backend_read
|
|
96
130
|
end
|
|
97
131
|
|
|
132
|
+
# @!visibility private
|
|
98
133
|
def __write_method__
|
|
99
134
|
:backend_write
|
|
100
135
|
end
|
|
@@ -113,13 +148,21 @@ class ::IO
|
|
|
113
148
|
# def each_codepoint
|
|
114
149
|
# end
|
|
115
150
|
|
|
151
|
+
# @!visibility private
|
|
116
152
|
alias_method :orig_getbyte, :getbyte
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
# @!visibility private
|
|
117
156
|
def getbyte
|
|
118
157
|
char = getc
|
|
119
158
|
char ? char.getbyte(0) : nil
|
|
120
159
|
end
|
|
121
160
|
|
|
161
|
+
# @!visibility private
|
|
122
162
|
alias_method :orig_getc, :getc
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
# @!visibility private
|
|
123
166
|
def getc
|
|
124
167
|
return @read_buffer.slice!(0) if @read_buffer && !@read_buffer.empty?
|
|
125
168
|
|
|
@@ -130,6 +173,7 @@ class ::IO
|
|
|
130
173
|
nil
|
|
131
174
|
end
|
|
132
175
|
|
|
176
|
+
# @!visibility private
|
|
133
177
|
def ungetc(c)
|
|
134
178
|
c = c.chr if c.is_a?(Integer)
|
|
135
179
|
if @read_buffer
|
|
@@ -140,7 +184,11 @@ class ::IO
|
|
|
140
184
|
end
|
|
141
185
|
alias_method :ungetbyte, :ungetc
|
|
142
186
|
|
|
187
|
+
# @!visibility private
|
|
143
188
|
alias_method :orig_read, :read
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
# @!visibility private
|
|
144
192
|
def read(len = nil, buf = nil, buf_pos = 0)
|
|
145
193
|
return '' if len == 0
|
|
146
194
|
|
|
@@ -157,7 +205,10 @@ class ::IO
|
|
|
157
205
|
already_read
|
|
158
206
|
end
|
|
159
207
|
|
|
208
|
+
# @!visibility private
|
|
160
209
|
alias_method :orig_readpartial, :read
|
|
210
|
+
|
|
211
|
+
# @!visibility private
|
|
161
212
|
def readpartial(len, str = +'', buffer_pos = 0, raise_on_eof = true)
|
|
162
213
|
result = Polyphony.backend_read(self, str, len, false, buffer_pos)
|
|
163
214
|
raise EOFError if !result && raise_on_eof
|
|
@@ -165,18 +216,27 @@ class ::IO
|
|
|
165
216
|
result
|
|
166
217
|
end
|
|
167
218
|
|
|
219
|
+
# @!visibility private
|
|
168
220
|
alias_method :orig_write, :write
|
|
221
|
+
|
|
222
|
+
# @!visibility private
|
|
169
223
|
def write(str, *args)
|
|
170
224
|
Polyphony.backend_write(self, str, *args)
|
|
171
225
|
end
|
|
172
226
|
|
|
227
|
+
# @!visibility private
|
|
173
228
|
alias_method :orig_write_chevron, :<<
|
|
229
|
+
|
|
230
|
+
# @!visibility private
|
|
174
231
|
def <<(str)
|
|
175
232
|
Polyphony.backend_write(self, str)
|
|
176
233
|
self
|
|
177
234
|
end
|
|
178
|
-
|
|
235
|
+
|
|
236
|
+
# @!visibility private
|
|
179
237
|
alias_method :orig_gets, :gets
|
|
238
|
+
|
|
239
|
+
# @!visibility private
|
|
180
240
|
def gets(sep = $/, _limit = nil, _chomp: nil)
|
|
181
241
|
if sep.is_a?(Integer)
|
|
182
242
|
sep = $/
|
|
@@ -197,6 +257,7 @@ class ::IO
|
|
|
197
257
|
return nil
|
|
198
258
|
end
|
|
199
259
|
|
|
260
|
+
# @!visibility private
|
|
200
261
|
def each_line(sep = $/, limit = nil, chomp: false)
|
|
201
262
|
if sep.is_a?(Integer)
|
|
202
263
|
limit = sep
|
|
@@ -230,10 +291,15 @@ class ::IO
|
|
|
230
291
|
# def putc(obj)
|
|
231
292
|
# end
|
|
232
293
|
|
|
294
|
+
# @!visibility private
|
|
233
295
|
LINEFEED = "\n"
|
|
296
|
+
# @!visibility private
|
|
234
297
|
LINEFEED_RE = /\n$/.freeze
|
|
235
298
|
|
|
299
|
+
# @!visibility private
|
|
236
300
|
alias_method :orig_puts, :puts
|
|
301
|
+
|
|
302
|
+
# @!visibility private
|
|
237
303
|
def puts(*args)
|
|
238
304
|
if args.empty?
|
|
239
305
|
write LINEFEED
|
|
@@ -268,24 +334,66 @@ class ::IO
|
|
|
268
334
|
# def readlines(sep = $/, limit = nil, chomp: nil)
|
|
269
335
|
# end
|
|
270
336
|
|
|
337
|
+
# @!visibility private
|
|
271
338
|
alias_method :orig_write_nonblock, :write_nonblock
|
|
339
|
+
|
|
340
|
+
# @!visibility private
|
|
272
341
|
def write_nonblock(string, _options = {})
|
|
273
342
|
write(string)
|
|
274
343
|
end
|
|
275
344
|
|
|
345
|
+
# @!visibility private
|
|
276
346
|
alias_method :orig_read_nonblock, :read_nonblock
|
|
347
|
+
|
|
348
|
+
# @!visibility private
|
|
277
349
|
def read_nonblock(maxlen, buf = nil, _options = nil)
|
|
278
350
|
buf ? readpartial(maxlen, buf) : readpartial(maxlen)
|
|
279
351
|
end
|
|
280
352
|
|
|
353
|
+
# call-seq:
|
|
354
|
+
# io.read_loop { |data| ... }
|
|
355
|
+
# io.read_loop(maxlen) { |data| ... }
|
|
356
|
+
#
|
|
357
|
+
# Reads up to `maxlen` bytes at a time in an infinite loop. Read data
|
|
358
|
+
# will be passed to the given block.
|
|
359
|
+
#
|
|
360
|
+
# @param maxlen [Integer] maximum bytes to receive
|
|
361
|
+
# @yield [String] handler block
|
|
362
|
+
# @return [void]
|
|
281
363
|
def read_loop(maxlen = 8192, &block)
|
|
282
364
|
Polyphony.backend_read_loop(self, maxlen, &block)
|
|
283
365
|
end
|
|
284
366
|
|
|
367
|
+
# call-seq:
|
|
368
|
+
# io.feed_loop(receiver, method)
|
|
369
|
+
# io.feed_loop(receiver, method) { |result| ... }
|
|
370
|
+
#
|
|
371
|
+
# Receives data from the io in an infinite loop, passing the data to the given
|
|
372
|
+
# receiver using the given method. If a block is given, the result of the
|
|
373
|
+
# method call to the receiver is passed to the block.
|
|
374
|
+
#
|
|
375
|
+
# This method can be used to feed data into parser objects. The following
|
|
376
|
+
# example shows how to feed data from a io directly into a MessagePack
|
|
377
|
+
# unpacker:
|
|
378
|
+
#
|
|
379
|
+
# unpacker = MessagePack::Unpacker.new
|
|
380
|
+
# buffer = []
|
|
381
|
+
# reader = spin do
|
|
382
|
+
# io.feed_loop(unpacker, :feed_each) { |msg| handle_msg(msg) }
|
|
383
|
+
# end
|
|
384
|
+
#
|
|
385
|
+
# @param receiver [any] receiver object
|
|
386
|
+
# @param method [Symbol] method to call
|
|
387
|
+
# @yield [any] block to handle result of method call to receiver
|
|
388
|
+
# @return [void]
|
|
285
389
|
def feed_loop(receiver, method = :call, &block)
|
|
286
390
|
Polyphony.backend_feed_loop(self, receiver, method, &block)
|
|
287
391
|
end
|
|
288
392
|
|
|
393
|
+
# Waits for the IO to become readable, with an optional timeout.
|
|
394
|
+
#
|
|
395
|
+
# @param timeout [Integer, nil] optional timeout in seconds.
|
|
396
|
+
# @return [IO] self
|
|
289
397
|
def wait_readable(timeout = nil)
|
|
290
398
|
return self if @read_buffer && @read_buffer.size > 0
|
|
291
399
|
|
|
@@ -300,6 +408,10 @@ class ::IO
|
|
|
300
408
|
end
|
|
301
409
|
end
|
|
302
410
|
|
|
411
|
+
# Waits for the IO to become writeable, with an optional timeout.
|
|
412
|
+
#
|
|
413
|
+
# @param timeout [Integer, nil] optional timeout in seconds.
|
|
414
|
+
# @return [IO] self
|
|
303
415
|
def wait_writable(timeout = nil)
|
|
304
416
|
if timeout
|
|
305
417
|
move_on_after(timeout) do
|
|
@@ -312,11 +424,21 @@ class ::IO
|
|
|
312
424
|
end
|
|
313
425
|
end
|
|
314
426
|
|
|
427
|
+
# Splices data from the given IO.
|
|
428
|
+
#
|
|
429
|
+
# @param src [IO, Polpyhony::Pipe] source to splice from
|
|
430
|
+
# @param maxlen [Integer] maximum bytes to splice
|
|
431
|
+
# @return [Integer] bytes spliced
|
|
315
432
|
def splice_from(src, maxlen)
|
|
316
433
|
Polyphony.backend_splice(src, self, maxlen)
|
|
317
434
|
end
|
|
318
435
|
|
|
319
436
|
if RUBY_PLATFORM =~ /linux/
|
|
437
|
+
# Tees data from the given IO.
|
|
438
|
+
#
|
|
439
|
+
# @param src [IO, Polpyhony::Pipe] source to tee from
|
|
440
|
+
# @param maxlen [Integer] maximum bytes to tee
|
|
441
|
+
# @return [Integer] bytes teed
|
|
320
442
|
def tee_from(src, maxlen)
|
|
321
443
|
Polyphony.backend_tee(src, self, maxlen)
|
|
322
444
|
end
|
|
@@ -4,11 +4,13 @@ require 'open3'
|
|
|
4
4
|
|
|
5
5
|
# Kernel extensions (methods available to all objects / call sites)
|
|
6
6
|
module ::Kernel
|
|
7
|
+
# @!visibility private
|
|
7
8
|
alias_method :orig_sleep, :sleep
|
|
8
9
|
|
|
10
|
+
# @!visibility private
|
|
9
11
|
alias_method :orig_backtick, :`
|
|
10
12
|
|
|
11
|
-
#
|
|
13
|
+
# @!visibility private
|
|
12
14
|
def `(cmd)
|
|
13
15
|
Open3.popen3(cmd) do |i, o, e, _t|
|
|
14
16
|
i.close
|
|
@@ -18,6 +20,7 @@ module ::Kernel
|
|
|
18
20
|
end
|
|
19
21
|
end
|
|
20
22
|
|
|
23
|
+
# @!visibility private
|
|
21
24
|
ARGV_GETS_LOOP = proc do |calling_fiber|
|
|
22
25
|
while (fn = ARGV.shift)
|
|
23
26
|
File.open(fn, 'r') do |f|
|
|
@@ -31,7 +34,10 @@ module ::Kernel
|
|
|
31
34
|
calling_fiber.transfer(e)
|
|
32
35
|
end
|
|
33
36
|
|
|
37
|
+
# @!visibility private
|
|
34
38
|
alias_method :orig_gets, :gets
|
|
39
|
+
|
|
40
|
+
# Reads a single line from STDIN
|
|
35
41
|
def gets(*_args)
|
|
36
42
|
if !ARGV.empty? || @gets_fiber
|
|
37
43
|
@gets_fiber ||= Fiber.new(&ARGV_GETS_LOOP)
|
|
@@ -45,7 +51,10 @@ module ::Kernel
|
|
|
45
51
|
$stdin.gets
|
|
46
52
|
end
|
|
47
53
|
|
|
54
|
+
# @!visibility private
|
|
48
55
|
alias_method :orig_p, :p
|
|
56
|
+
|
|
57
|
+
# @!visibility private
|
|
49
58
|
def p(*args)
|
|
50
59
|
strs = args.inject([]) do |m, a|
|
|
51
60
|
m << a.inspect << "\n"
|
|
@@ -54,13 +63,19 @@ module ::Kernel
|
|
|
54
63
|
args.size == 1 ? args.first : args
|
|
55
64
|
end
|
|
56
65
|
|
|
66
|
+
# @!visibility private
|
|
57
67
|
alias_method :orig_system, :system
|
|
68
|
+
|
|
69
|
+
# @!visibility private
|
|
58
70
|
def system(*args)
|
|
59
71
|
Kernel.system(*args)
|
|
60
72
|
end
|
|
61
73
|
|
|
62
74
|
class << self
|
|
75
|
+
# @!visibility private
|
|
63
76
|
alias_method :orig_system, :system
|
|
77
|
+
|
|
78
|
+
# @!visibility private
|
|
64
79
|
def system(*args)
|
|
65
80
|
waiter = nil
|
|
66
81
|
Open3.popen2(*args) do |i, o, t|
|
|
@@ -74,7 +89,10 @@ module ::Kernel
|
|
|
74
89
|
end
|
|
75
90
|
end
|
|
76
91
|
|
|
92
|
+
# @!visibility private
|
|
77
93
|
alias_method :orig_trap, :trap
|
|
94
|
+
|
|
95
|
+
# @!visibility private
|
|
78
96
|
def trap(sig, command = nil, &block)
|
|
79
97
|
return orig_trap(sig, command) if command.is_a? String
|
|
80
98
|
|
|
@@ -93,7 +111,7 @@ module ::Kernel
|
|
|
93
111
|
|
|
94
112
|
private
|
|
95
113
|
|
|
96
|
-
#
|
|
114
|
+
# @!visibility private
|
|
97
115
|
def pipe_to_eof(src, dest)
|
|
98
116
|
src.read_loop { |data| dest << data }
|
|
99
117
|
end
|