polyphony 0.99 → 0.99.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|