polyphony 0.99 → 0.99.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (94) hide show
  1. checksums.yaml +4 -4
  2. data/.github/FUNDING.yml +1 -1
  3. data/.rubocop.yml +3 -3
  4. data/.yardopts +30 -0
  5. data/CHANGELOG.md +4 -0
  6. data/LICENSE +1 -1
  7. data/README.md +63 -29
  8. data/Rakefile +1 -5
  9. data/TODO.md +0 -4
  10. data/docs/{main-concepts/concurrency.md → concurrency.md} +2 -9
  11. data/docs/{main-concepts/design-principles.md → design-principles.md} +3 -9
  12. data/docs/{main-concepts/exception-handling.md → exception-handling.md} +2 -9
  13. data/docs/{main-concepts/extending.md → extending.md} +2 -9
  14. data/docs/faq.md +3 -16
  15. data/docs/{main-concepts/fiber-scheduling.md → fiber-scheduling.md} +1 -9
  16. data/docs/link_rewriter.rb +16 -0
  17. data/docs/{getting-started/overview.md → overview.md} +1 -30
  18. data/docs/{getting-started/tutorial.md → tutorial.md} +3 -28
  19. data/docs/{_posts/2020-07-26-polyphony-0.44.md → whats-new.md} +3 -1
  20. data/examples/adapters/redis_client.rb +3 -2
  21. data/examples/io/echo_server.rb +1 -1
  22. data/examples/io/echo_server_plain_ruby.rb +26 -0
  23. data/ext/polyphony/backend_io_uring.c +154 -9
  24. data/ext/polyphony/backend_io_uring_context.c +21 -12
  25. data/ext/polyphony/backend_io_uring_context.h +12 -7
  26. data/ext/polyphony/backend_libev.c +1 -1
  27. data/ext/polyphony/extconf.rb +24 -8
  28. data/ext/polyphony/fiber.c +79 -2
  29. data/ext/polyphony/io_extensions.c +53 -0
  30. data/ext/polyphony/pipe.c +42 -2
  31. data/ext/polyphony/polyphony.c +345 -31
  32. data/ext/polyphony/polyphony.h +9 -2
  33. data/ext/polyphony/queue.c +181 -0
  34. data/ext/polyphony/ring_buffer.c +0 -1
  35. data/ext/polyphony/runqueue.c +8 -1
  36. data/ext/polyphony/runqueue_ring_buffer.c +13 -0
  37. data/ext/polyphony/runqueue_ring_buffer.h +2 -1
  38. data/ext/polyphony/socket_extensions.c +6 -0
  39. data/ext/polyphony/thread.c +34 -2
  40. data/lib/polyphony/adapters/process.rb +11 -1
  41. data/lib/polyphony/adapters/sequel.rb +1 -1
  42. data/lib/polyphony/core/channel.rb +2 -0
  43. data/lib/polyphony/core/debug.rb +1 -1
  44. data/lib/polyphony/core/global_api.rb +25 -24
  45. data/lib/polyphony/core/resource_pool.rb +7 -6
  46. data/lib/polyphony/core/sync.rb +2 -2
  47. data/lib/polyphony/core/thread_pool.rb +3 -3
  48. data/lib/polyphony/core/timer.rb +8 -8
  49. data/lib/polyphony/extensions/exception.rb +2 -0
  50. data/lib/polyphony/extensions/fiber.rb +15 -13
  51. data/lib/polyphony/extensions/io.rb +127 -5
  52. data/lib/polyphony/extensions/kernel.rb +20 -2
  53. data/lib/polyphony/extensions/openssl.rb +100 -11
  54. data/lib/polyphony/extensions/pipe.rb +103 -7
  55. data/lib/polyphony/extensions/process.rb +13 -1
  56. data/lib/polyphony/extensions/socket.rb +93 -27
  57. data/lib/polyphony/extensions/thread.rb +9 -1
  58. data/lib/polyphony/extensions/timeout.rb +1 -1
  59. data/lib/polyphony/version.rb +2 -1
  60. data/lib/polyphony.rb +27 -7
  61. data/polyphony.gemspec +1 -8
  62. data/test/stress.rb +1 -1
  63. data/test/test_global_api.rb +45 -7
  64. data/test/test_socket.rb +96 -0
  65. data/test/test_timer.rb +5 -5
  66. metadata +17 -40
  67. data/docs/_config.yml +0 -64
  68. data/docs/_includes/head.html +0 -40
  69. data/docs/_includes/title.html +0 -1
  70. data/docs/_sass/custom/custom.scss +0 -10
  71. data/docs/_sass/overrides.scss +0 -0
  72. data/docs/api-reference/exception.md +0 -31
  73. data/docs/api-reference/fiber.md +0 -425
  74. data/docs/api-reference/index.md +0 -9
  75. data/docs/api-reference/io.md +0 -36
  76. data/docs/api-reference/object.md +0 -99
  77. data/docs/api-reference/polyphony-baseexception.md +0 -33
  78. data/docs/api-reference/polyphony-cancel.md +0 -26
  79. data/docs/api-reference/polyphony-moveon.md +0 -24
  80. data/docs/api-reference/polyphony-net.md +0 -20
  81. data/docs/api-reference/polyphony-process.md +0 -28
  82. data/docs/api-reference/polyphony-resourcepool.md +0 -59
  83. data/docs/api-reference/polyphony-restart.md +0 -18
  84. data/docs/api-reference/polyphony-terminate.md +0 -18
  85. data/docs/api-reference/polyphony-threadpool.md +0 -67
  86. data/docs/api-reference/polyphony-throttler.md +0 -77
  87. data/docs/api-reference/polyphony.md +0 -36
  88. data/docs/api-reference/thread.md +0 -88
  89. data/docs/favicon.ico +0 -0
  90. data/docs/getting-started/index.md +0 -10
  91. data/docs/getting-started/installing.md +0 -34
  92. /data/{docs/assets/img → assets}/echo-fibers.svg +0 -0
  93. /data/{docs → assets}/polyphony-logo.png +0 -0
  94. /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
- # @param &block [Proc] allocator block
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
- # @param &block [Proc] code to run
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 *args [Array<any>] method arguments
58
- # @param &block [Proc] block passed to method
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
- # :no-doc:
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
- # @param &block [Proc] given block
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
@@ -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
- # @param &block [Proc] code to run
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 timeout [Number, nil] timeout in seconds (currently not implemented)
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
- # @param &block [Proc] given block
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
- # @param &block [Proc] given block
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
- # @param &block [Proc] given block
58
+ # @yield [] given block
59
59
  # @return [Polyphony::ThreadPool] self
60
60
  def cast(&block)
61
61
  setup unless @task_queue
@@ -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: [Number] timer granularity in seconds or fractions thereof
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 delay [Number] delay in seconds before running the given block
47
- # @param &block [Proc] block to run
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
- # @param &block [Proc] block to run
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: [Class, Exception] exception or exception class
113
- # @param &block [Proc] block to execute
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: [any] return value in case of timeout
167
- # @param &block [Proc] block to execute
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
- # @param &block [Proc] given block
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
- # :no-doc:
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 *fibers [Array<Fiber>] fibers to supervise
175
- # @param on_done: [Proc, nil] proc to call when a supervised fiber is terminated
176
- # @param on_error: [Proc, nil] proc to call when a supervised fiber is terminated with an exception
177
- # @param restart: [:always, :on_error, nil] whether to restart terminated fibers
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
- # :no-doc:
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 *fibers [Array<Fiber>] fibers to wait for
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 *fibers [Array<Fiber>] Fibers to wait for
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
- # @param &block [Proc] given block
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
- # :no-doc:
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
- # @param &block [Proc] child fiber's block
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 parent [Fiber] new parent
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
- # TODO: add docs to all methods in this file
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
- # TODO: add docs to all methods in this file
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
- # :no-doc:
114
+ # @!visibility private
97
115
  def pipe_to_eof(src, dest)
98
116
  src.read_loop { |data| dest << data }
99
117
  end