polyphony 0.45.0 → 0.46.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (156) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/test.yml +2 -0
  3. data/.gitmodules +0 -0
  4. data/.rubocop.yml +1 -0
  5. data/CHANGELOG.md +38 -0
  6. data/Gemfile.lock +11 -3
  7. data/README.md +3 -3
  8. data/Rakefile +1 -1
  9. data/TODO.md +10 -18
  10. data/examples/adapters/redis_client.rb +3 -1
  11. data/examples/adapters/redis_pubsub_perf.rb +11 -8
  12. data/examples/adapters/sequel_mysql.rb +1 -1
  13. data/examples/adapters/sequel_pg.rb +24 -0
  14. data/examples/core/{02-awaiting-fibers.rb → await.rb} +0 -0
  15. data/examples/core/{xx-channels.rb → channels.rb} +0 -0
  16. data/examples/core/deferring-an-operation.rb +16 -0
  17. data/examples/core/{xx-erlang-style-genserver.rb → erlang-style-genserver.rb} +16 -9
  18. data/examples/core/{xx-forking.rb → forking.rb} +1 -1
  19. data/examples/core/handling-signals.rb +11 -0
  20. data/examples/core/{03-interrupting.rb → interrupt.rb} +0 -0
  21. data/examples/core/{xx-pingpong.rb → pingpong.rb} +7 -5
  22. data/examples/core/{xx-recurrent-timer.rb → recurrent-timer.rb} +1 -1
  23. data/examples/core/{xx-resource_delegate.rb → resource_delegate.rb} +3 -4
  24. data/examples/core/{01-spinning-up-fibers.rb → spin.rb} +1 -1
  25. data/examples/core/{xx-spin_error_backtrace.rb → spin_error_backtrace.rb} +1 -1
  26. data/examples/core/{xx-supervise-process.rb → supervise-process.rb} +8 -5
  27. data/examples/core/supervisor.rb +20 -0
  28. data/examples/core/{xx-thread-sleep.rb → thread-sleep.rb} +0 -0
  29. data/examples/core/{xx-thread_pool.rb → thread_pool.rb} +0 -0
  30. data/examples/core/{xx-throttling.rb → throttling.rb} +0 -0
  31. data/examples/core/{xx-timeout.rb → timeout.rb} +0 -0
  32. data/examples/core/{xx-using-a-mutex.rb → using-a-mutex.rb} +0 -0
  33. data/examples/core/{xx-worker-thread.rb → worker-thread.rb} +2 -2
  34. data/examples/io/{xx-backticks.rb → backticks.rb} +0 -0
  35. data/examples/io/{xx-echo_client.rb → echo_client.rb} +1 -1
  36. data/examples/io/{xx-echo_client_from_stdin.rb → echo_client_from_stdin.rb} +2 -2
  37. data/examples/io/{xx-echo_pipe.rb → echo_pipe.rb} +1 -1
  38. data/examples/io/{xx-echo_server.rb → echo_server.rb} +0 -0
  39. data/examples/io/{xx-echo_server_with_timeout.rb → echo_server_with_timeout.rb} +1 -1
  40. data/examples/io/{xx-echo_stdin.rb → echo_stdin.rb} +0 -0
  41. data/examples/io/{xx-happy-eyeballs.rb → happy-eyeballs.rb} +0 -0
  42. data/examples/io/{xx-httparty.rb → httparty.rb} +4 -13
  43. data/examples/io/{xx-irb.rb → irb.rb} +0 -0
  44. data/examples/io/{xx-net-http.rb → net-http.rb} +0 -0
  45. data/examples/io/{xx-open.rb → open.rb} +0 -0
  46. data/examples/io/{xx-pry.rb → pry.rb} +0 -0
  47. data/examples/io/{xx-rack_server.rb → rack_server.rb} +0 -0
  48. data/examples/io/raw.rb +14 -0
  49. data/examples/io/reline.rb +18 -0
  50. data/examples/io/{xx-system.rb → system.rb} +1 -1
  51. data/examples/io/{xx-tcpserver.rb → tcpserver.rb} +0 -0
  52. data/examples/io/{xx-tcpsocket.rb → tcpsocket.rb} +0 -0
  53. data/examples/io/tunnel.rb +6 -1
  54. data/examples/io/{xx-zip.rb → zip.rb} +0 -0
  55. data/examples/performance/fiber_transfer.rb +2 -1
  56. data/examples/performance/fs_read.rb +5 -6
  57. data/examples/performance/multi_snooze.rb +0 -1
  58. data/examples/{io/xx-switch.rb → performance/switch.rb} +2 -1
  59. data/examples/performance/thread-vs-fiber/{xx-httparty_multi.rb → httparty_multi.rb} +3 -4
  60. data/examples/performance/thread-vs-fiber/{xx-httparty_threaded.rb → httparty_threaded.rb} +0 -0
  61. data/examples/performance/thread-vs-fiber/polyphony_mt_server.rb +1 -1
  62. data/examples/performance/thread-vs-fiber/polyphony_server.rb +1 -2
  63. data/examples/performance/thread-vs-fiber/threaded_server.rb +1 -5
  64. data/examples/performance/thread_pool_perf.rb +6 -7
  65. data/ext/liburing/liburing.h +585 -0
  66. data/ext/liburing/liburing/README.md +4 -0
  67. data/ext/liburing/liburing/barrier.h +73 -0
  68. data/ext/liburing/liburing/compat.h +15 -0
  69. data/ext/liburing/liburing/io_uring.h +343 -0
  70. data/ext/liburing/queue.c +333 -0
  71. data/ext/liburing/register.c +187 -0
  72. data/ext/liburing/setup.c +210 -0
  73. data/ext/liburing/syscall.c +54 -0
  74. data/ext/liburing/syscall.h +18 -0
  75. data/ext/polyphony/backend.h +1 -16
  76. data/ext/polyphony/backend_common.h +109 -0
  77. data/ext/polyphony/backend_io_uring.c +884 -0
  78. data/ext/polyphony/backend_io_uring_context.c +73 -0
  79. data/ext/polyphony/backend_io_uring_context.h +52 -0
  80. data/ext/polyphony/{libev_backend.c → backend_libev.c} +255 -345
  81. data/ext/polyphony/event.c +1 -1
  82. data/ext/polyphony/extconf.rb +31 -13
  83. data/ext/polyphony/fiber.c +111 -27
  84. data/ext/polyphony/libev.c +4 -0
  85. data/ext/polyphony/libev.h +8 -2
  86. data/ext/polyphony/liburing.c +8 -0
  87. data/ext/polyphony/playground.c +51 -0
  88. data/ext/polyphony/polyphony.c +6 -8
  89. data/ext/polyphony/polyphony.h +29 -25
  90. data/ext/polyphony/polyphony_ext.c +13 -6
  91. data/ext/polyphony/queue.c +3 -4
  92. data/ext/polyphony/ring_buffer.c +0 -1
  93. data/ext/polyphony/runqueue.c +102 -0
  94. data/ext/polyphony/runqueue_ring_buffer.c +85 -0
  95. data/ext/polyphony/runqueue_ring_buffer.h +31 -0
  96. data/ext/polyphony/thread.c +45 -92
  97. data/lib/polyphony.rb +2 -2
  98. data/lib/polyphony/adapters/fs.rb +1 -1
  99. data/lib/polyphony/adapters/process.rb +0 -3
  100. data/lib/polyphony/adapters/redis.rb +1 -1
  101. data/lib/polyphony/adapters/trace.rb +2 -2
  102. data/lib/polyphony/core/global_api.rb +9 -12
  103. data/lib/polyphony/core/sync.rb +6 -2
  104. data/lib/polyphony/extensions/core.rb +6 -24
  105. data/lib/polyphony/extensions/debug.rb +13 -0
  106. data/lib/polyphony/extensions/fiber.rb +21 -44
  107. data/lib/polyphony/extensions/io.rb +55 -10
  108. data/lib/polyphony/extensions/socket.rb +70 -12
  109. data/lib/polyphony/version.rb +1 -1
  110. data/polyphony.gemspec +3 -2
  111. data/test/helper.rb +36 -4
  112. data/test/io_uring_test.rb +55 -0
  113. data/test/stress.rb +5 -2
  114. data/test/test_backend.rb +4 -6
  115. data/test/test_ext.rb +1 -2
  116. data/test/test_fiber.rb +31 -24
  117. data/test/test_global_api.rb +58 -31
  118. data/test/test_io.rb +58 -0
  119. data/test/test_signal.rb +11 -8
  120. data/test/test_socket.rb +17 -0
  121. data/test/test_sync.rb +21 -0
  122. data/test/test_throttler.rb +3 -6
  123. data/test/test_trace.rb +7 -5
  124. metadata +86 -76
  125. data/examples/adapters/concurrent-ruby.rb +0 -9
  126. data/examples/core/04-handling-signals.rb +0 -19
  127. data/examples/core/xx-at_exit.rb +0 -29
  128. data/examples/core/xx-backend.rb +0 -102
  129. data/examples/core/xx-caller.rb +0 -12
  130. data/examples/core/xx-daemon.rb +0 -14
  131. data/examples/core/xx-deadlock.rb +0 -8
  132. data/examples/core/xx-deferring-an-operation.rb +0 -14
  133. data/examples/core/xx-exception-backtrace.rb +0 -40
  134. data/examples/core/xx-fork-cleanup.rb +0 -22
  135. data/examples/core/xx-fork-spin.rb +0 -42
  136. data/examples/core/xx-fork-terminate.rb +0 -27
  137. data/examples/core/xx-move_on.rb +0 -23
  138. data/examples/core/xx-queue-async.rb +0 -120
  139. data/examples/core/xx-readpartial.rb +0 -18
  140. data/examples/core/xx-signals.rb +0 -16
  141. data/examples/core/xx-sleep-forever.rb +0 -9
  142. data/examples/core/xx-sleeping.rb +0 -25
  143. data/examples/core/xx-snooze-starve.rb +0 -16
  144. data/examples/core/xx-spin-fork.rb +0 -49
  145. data/examples/core/xx-state-machine.rb +0 -51
  146. data/examples/core/xx-stop.rb +0 -20
  147. data/examples/core/xx-supervisors.rb +0 -21
  148. data/examples/core/xx-thread-selector-sleep.rb +0 -51
  149. data/examples/core/xx-thread-selector-snooze.rb +0 -46
  150. data/examples/core/xx-thread-snooze.rb +0 -34
  151. data/examples/core/xx-timer-gc.rb +0 -17
  152. data/examples/core/xx-trace.rb +0 -79
  153. data/examples/performance/xx-array.rb +0 -11
  154. data/examples/performance/xx-fiber-switch.rb +0 -9
  155. data/examples/performance/xx-snooze.rb +0 -15
  156. data/examples/xx-spin.rb +0 -32
@@ -76,10 +76,10 @@ module Polyphony
76
76
  end
77
77
 
78
78
  def install_terminating_signal_handlers
79
- trap('SIGTERM', SystemExit)
79
+ trap('SIGTERM') { raise SystemExit }
80
80
  orig_trap('SIGINT') do
81
81
  orig_trap('SIGINT') { exit! }
82
- Thread.current.break_out_of_ev_loop(Thread.main.main_fiber, Interrupt.new)
82
+ Fiber.schedule_priority_oob_fiber { raise Interrupt }
83
83
  end
84
84
  end
85
85
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  require 'fileutils'
4
4
 
5
- require_relative './core/thread_pool'
5
+ require_relative '../core/thread_pool'
6
6
 
7
7
  ::File.singleton_class.instance_eval do
8
8
  alias_method :orig_stat, :stat
@@ -24,9 +24,6 @@ module Polyphony
24
24
  def kill_and_await(sig, pid)
25
25
  ::Process.kill(sig, pid)
26
26
  Thread.current.backend.waitpid(pid)
27
- rescue SystemCallError
28
- # ignore
29
- puts 'SystemCallError in kill_and_await'
30
27
  end
31
28
  end
32
29
  end
@@ -56,7 +56,7 @@ class Polyphony::RedisDriver
56
56
  reply = @reader.gets
57
57
  return reply if reply
58
58
 
59
- while (data = @connection.readpartial(8192))
59
+ @connection.read_loop do |data|
60
60
  @reader.feed(data)
61
61
  reply = @reader.gets
62
62
  return reply unless reply == false
@@ -118,13 +118,13 @@ module Polyphony
118
118
 
119
119
  ALL_FIBER_EVENTS = %i[
120
120
  fiber_create fiber_terminate fiber_schedule fiber_switchpoint fiber_run
121
- fiber_ev_loop_enter fiber_ev_loop_leave
121
+ fiber_event_poll_enter fiber_event_poll_leave
122
122
  ].freeze
123
123
 
124
124
  def event_masks(events)
125
125
  events.each_with_object([[], []]) do |e, masks|
126
126
  case e
127
- when /fiber_/
127
+ when /^fiber_/
128
128
  masks[1] += e == :fiber_all ? ALL_FIBER_EVENTS : [e]
129
129
  masks[0] << :c_return unless masks[0].include?(:c_return)
130
130
  else
@@ -32,7 +32,7 @@ module Polyphony
32
32
  end
33
33
 
34
34
  def cancel_after_wrap_block(canceller, &block)
35
- block.call
35
+ block.call(canceller)
36
36
  ensure
37
37
  canceller.stop
38
38
  end
@@ -81,7 +81,7 @@ module Polyphony
81
81
  sleep interval
82
82
  fiber.schedule Polyphony::MoveOn.new(with_value)
83
83
  end
84
- block.call
84
+ block.call(canceller)
85
85
  rescue Polyphony::MoveOn => e
86
86
  e.value
87
87
  ensure
@@ -92,8 +92,8 @@ module Polyphony
92
92
  Fiber.current.receive
93
93
  end
94
94
 
95
- def receive_pending
96
- Fiber.current.receive_pending
95
+ def receive_all_pending
96
+ Fiber.current.receive_all_pending
97
97
  end
98
98
 
99
99
  def supervise(*args, &block)
@@ -107,16 +107,13 @@ module Polyphony
107
107
  end
108
108
 
109
109
  def sleep_forever
110
- Thread.current.backend.ref
111
- loop { sleep 60 }
112
- ensure
113
- Thread.current.backend.unref
110
+ Thread.current.backend.wait_event(true)
114
111
  end
115
112
 
116
- def throttled_loop(rate, count: nil, &block)
117
- throttler = Polyphony::Throttler.new(rate)
118
- if count
119
- count.times { |_i| throttler.(&block) }
113
+ def throttled_loop(rate = nil, **opts, &block)
114
+ throttler = Polyphony::Throttler.new(rate || opts)
115
+ if opts[:count]
116
+ opts[:count].times { |_i| throttler.(&block) }
120
117
  else
121
118
  loop { throttler.(&block) }
122
119
  end
@@ -8,11 +8,15 @@ module Polyphony
8
8
  @store << :token
9
9
  end
10
10
 
11
- def synchronize
11
+ def synchronize(&block)
12
12
  return yield if @holding_fiber == Fiber.current
13
13
 
14
+ synchronize_not_holding(&block)
15
+ end
16
+
17
+ def synchronize_not_holding
18
+ @token = @store.shift
14
19
  begin
15
- @token = @store.shift
16
20
  @holding_fiber = Fiber.current
17
21
  yield
18
22
  ensure
@@ -141,12 +141,7 @@ module ::Kernel
141
141
  end
142
142
 
143
143
  def pipe_to_eof(src, dest)
144
- loop do
145
- data = src.readpartial(8192)
146
- dest << data
147
- rescue EOFError
148
- break
149
- end
144
+ src.read_loop { |data| dest << data }
150
145
  end
151
146
 
152
147
  alias_method :orig_trap, :trap
@@ -154,32 +149,19 @@ module ::Kernel
154
149
  return orig_trap(sig, command) if command.is_a? String
155
150
 
156
151
  block = command if !block && command.respond_to?(:call)
157
- exception = signal_exception(block, command)
158
152
 
159
153
  # The signal trap can be invoked at any time, including while the system
160
154
  # backend is blocking while polling for events. In order to deal with this
161
- # correctly, we spin a fiber that will run the signal handler code, then
162
- # call break_out_of_ev_loop, which will put the fiber at the front of the
163
- # run queue, then wake up the backend.
164
- #
165
- # If the command argument is an exception class however, it will be raised
166
- # directly in the context of the main fiber.
155
+ # correctly, we run the signal handler code in an out-of-band, priority
156
+ # scheduled fiber, that will pass any uncaught exception (including
157
+ # SystemExit and Interrupt) to the main thread's main fiber. See also
158
+ # `Fiber#schedule_priority_oob_fiber`.
167
159
  orig_trap(sig) do
168
- Thread.current.break_out_of_ev_loop(Thread.main.main_fiber, exception)
160
+ Fiber.schedule_priority_oob_fiber(&block)
169
161
  end
170
162
  end
171
163
  end
172
164
 
173
- def signal_exception(block, command)
174
- if block
175
- Polyphony::Interjection.new(block)
176
- elsif command.is_a?(Class)
177
- command.new
178
- else
179
- raise ArgumentError, 'Must supply block or exception or callable object'
180
- end
181
- end
182
-
183
165
  # Override Timeout to use cancel scope
184
166
  module ::Timeout
185
167
  def self.timeout(sec, klass = nil, message = nil, &block)
@@ -0,0 +1,13 @@
1
+ module ::Kernel
2
+ def trace(*args)
3
+ STDOUT.orig_write(format_trace(args))
4
+ end
5
+
6
+ def format_trace(args)
7
+ if args.size > 1 && args.first.is_a?(String)
8
+ format("%s: %p\n", args.shift, args.size == 1 ? args.first : args)
9
+ else
10
+ format("%p\n", args.size == 1 ? args.first : args)
11
+ end
12
+ end
13
+ end
@@ -7,20 +7,6 @@ require_relative '../core/exceptions'
7
7
  module Polyphony
8
8
  # Fiber control API
9
9
  module FiberControl
10
- def await
11
- if @running == false
12
- return @result.is_a?(Exception) ? (Kernel.raise @result) : @result
13
- end
14
-
15
- fiber = Fiber.current
16
- @waiting_fibers ||= {}
17
- @waiting_fibers[fiber] = true
18
- suspend
19
- ensure
20
- @waiting_fibers&.delete(fiber)
21
- end
22
- alias_method :join, :await
23
-
24
10
  def interrupt(value = nil)
25
11
  return if @running == false
26
12
 
@@ -117,7 +103,7 @@ module Polyphony
117
103
  suspend
118
104
  fibers.map(&:result)
119
105
  ensure
120
- await_select_cleanup(state)
106
+ await_select_cleanup(state) if state
121
107
  end
122
108
  alias_method :join, :await
123
109
 
@@ -179,21 +165,19 @@ module Polyphony
179
165
  state[:selected] = true
180
166
  end
181
167
  end
182
- end
183
168
 
184
- # Messaging functionality
185
- module FiberMessaging
186
- def <<(value)
187
- @mailbox << value
188
- end
189
- alias_method :send, :<<
190
-
191
- def receive
192
- @mailbox.shift
193
- end
194
-
195
- def receive_pending
196
- @mailbox.shift_all
169
+ # Creates and schedules with priority an out-of-band fiber that runs the
170
+ # supplied block. If any uncaught exception is raised while the fiber is
171
+ # running, it will bubble up to the main thread's main fiber, which will
172
+ # also be scheduled with priority. This method is mainly used trapping
173
+ # signals (see also the patched `Kernel#trap`)
174
+ def schedule_priority_oob_fiber(&block)
175
+ f = Fiber.new do
176
+ block.call
177
+ rescue Exception => e
178
+ Thread.current.schedule_and_wakeup(Thread.main.main_fiber, e)
179
+ end
180
+ Thread.current.schedule_and_wakeup(f, nil)
197
181
  end
198
182
  end
199
183
 
@@ -225,14 +209,14 @@ module Polyphony
225
209
  def await_all_children
226
210
  return unless @children && !@children.empty?
227
211
 
228
- @results = @children.dup
212
+ results = @children.dup
229
213
  @on_child_done = proc do |c, r|
230
- @results[c] = r
214
+ results[c] = r
231
215
  schedule if @children.empty?
232
216
  end
233
217
  suspend
234
218
  @on_child_done = nil
235
- @results.values
219
+ results.values
236
220
  end
237
221
 
238
222
  def shutdown_all_children
@@ -249,7 +233,6 @@ module Polyphony
249
233
  @parent = parent
250
234
  @caller = caller
251
235
  @block = block
252
- @mailbox = Polyphony::Queue.new
253
236
  __fiber_trace__(:fiber_create, self)
254
237
  schedule
255
238
  end
@@ -279,7 +262,6 @@ module Polyphony
279
262
  # allows the fiber to be scheduled and to receive messages.
280
263
  def setup_raw
281
264
  @thread = Thread.current
282
- @mailbox = Polyphony::Queue.new
283
265
  end
284
266
 
285
267
  def setup_main_fiber
@@ -288,11 +270,10 @@ module Polyphony
288
270
  @thread = Thread.current
289
271
  @running = true
290
272
  @children&.clear
291
- @mailbox = Polyphony::Queue.new
292
273
  end
293
274
 
294
275
  def restart_self(first_value)
295
- @mailbox = Polyphony::Queue.new
276
+ @mailbox = nil
296
277
  @when_done_procs = nil
297
278
  @waiting_fibers = nil
298
279
  run(first_value)
@@ -324,13 +305,10 @@ module Polyphony
324
305
  def inform_dependants(result, uncaught_exception)
325
306
  @parent&.child_done(self, result)
326
307
  @when_done_procs&.each { |p| p.(result) }
327
- @waiting_fibers&.each_key do |f|
328
- f.schedule(result)
329
- end
330
- return unless uncaught_exception && !@waiting_fibers
331
-
308
+ @waiting_fibers&.each_key { |f| f.schedule(result) }
309
+
332
310
  # propagate unaught exception to parent
333
- @parent&.schedule(result)
311
+ @parent&.schedule_with_priority(result) if uncaught_exception && !@waiting_fibers
334
312
  end
335
313
 
336
314
  def when_done(&block)
@@ -344,14 +322,13 @@ end
344
322
  class ::Fiber
345
323
  prepend Polyphony::FiberControl
346
324
  include Polyphony::FiberSupervision
347
- include Polyphony::FiberMessaging
348
325
  include Polyphony::ChildFiberControl
349
326
  include Polyphony::FiberLifeCycle
350
327
 
351
328
  extend Polyphony::FiberControlClassMethods
352
329
 
353
330
  attr_accessor :tag, :thread, :parent
354
- attr_reader :result
331
+ attr_reader :result, :mailbox
355
332
 
356
333
  def running?
357
334
  @running
@@ -90,11 +90,22 @@ class ::IO
90
90
  # def each_codepoint
91
91
  # end
92
92
 
93
- # def getbyte
94
- # end
93
+ alias_method :orig_getbyte, :getbyte
94
+ def getbyte
95
+ char = getc
96
+ char ? char.getbyte(0) : nil
97
+ end
95
98
 
96
- # def getc
97
- # end
99
+ alias_method :orig_getc, :getc
100
+ def getc
101
+ return @read_buffer.slice!(0) if @read_buffer && !@read_buffer.empty?
102
+
103
+ @read_buffer ||= +''
104
+ Thread.current.backend.read(self, @read_buffer, 8192, false)
105
+ return @read_buffer.slice!(0) if !@read_buffer.empty?
106
+
107
+ nil
108
+ end
98
109
 
99
110
  alias_method :orig_read, :read
100
111
  def read(len = nil)
@@ -163,19 +174,29 @@ class ::IO
163
174
  # def putc(obj)
164
175
  # end
165
176
 
177
+ LINEFEED = "\n"
178
+ LINEFEED_RE = /\n$/.freeze
179
+
166
180
  alias_method :orig_puts, :puts
167
181
  def puts(*args)
168
182
  if args.empty?
169
- write "\n"
183
+ write LINEFEED
170
184
  return
171
185
  end
172
186
 
173
- strs = args.each_with_object([]) do |a, m|
174
- a = a.to_s
175
- m << a
176
- m << "\n" unless a =~ /\n$/
187
+ idx = 0
188
+ while idx < args.size
189
+ arg = args[idx]
190
+ args[idx] = arg = arg.to_s unless arg.is_a?(String)
191
+ if arg =~ LINEFEED_RE
192
+ idx += 1
193
+ else
194
+ args.insert(idx + 1, LINEFEED)
195
+ idx += 2
196
+ end
177
197
  end
178
- write(*strs)
198
+
199
+ write(*args)
179
200
  nil
180
201
  end
181
202
 
@@ -217,4 +238,28 @@ class ::IO
217
238
  # end
218
239
  # outbuf
219
240
  # end
241
+
242
+ def wait_readable(timeout = nil)
243
+ if timeout
244
+ move_on_after(timeout) do
245
+ Thread.current.backend.wait_io(self, false)
246
+ self
247
+ end
248
+ else
249
+ Thread.current.backend.wait_io(self, false)
250
+ self
251
+ end
252
+ end
253
+
254
+ def wait_writable(timeout = nil)
255
+ if timeout
256
+ move_on_after(timeout) do
257
+ Thread.current.backend.wait_io(self, true)
258
+ self
259
+ end
260
+ else
261
+ Thread.current.backend.wait_io(self, true)
262
+ self
263
+ end
264
+ end
220
265
  end
@@ -13,21 +13,27 @@ class ::Socket
13
13
 
14
14
  NO_EXCEPTION = { exception: false }.freeze
15
15
 
16
- def connect(remotesockaddr)
17
- Thread.current.backend.connect(self, remotesockaddr.ip_address, remotesockaddr.ip_port)
16
+ def connect(addr)
17
+ addr = Addrinfo.new(addr) if addr.is_a?(String)
18
+ Thread.current.backend.connect(self, addr.ip_address, addr.ip_port)
18
19
  end
19
20
 
20
21
  def recv(maxlen, flags = 0, outbuf = nil)
21
- outbuf ||= +''
22
- loop do
23
- result = recv_nonblock(maxlen, flags, outbuf, **NO_EXCEPTION)
24
- case result
25
- when nil then raise IOError
26
- when :wait_readable then Thread.current.backend.wait_io(self, false)
27
- else
28
- return result
29
- end
30
- end
22
+ Thread.current.backend.recv(self, buf || +'', maxlen)
23
+ # outbuf ||= +''
24
+ # loop do
25
+ # result = recv_nonblock(maxlen, flags, outbuf, **NO_EXCEPTION)
26
+ # case result
27
+ # when nil then raise IOError
28
+ # when :wait_readable then Thread.current.backend.wait_io(self, false)
29
+ # else
30
+ # return result
31
+ # end
32
+ # end
33
+ end
34
+
35
+ def recv_loop(&block)
36
+ Thread.current.backend.recv_loop(self, &block)
31
37
  end
32
38
 
33
39
  def recvfrom(maxlen, flags = 0)
@@ -43,6 +49,19 @@ class ::Socket
43
49
  end
44
50
  end
45
51
 
52
+ def send(mesg, flags = 0)
53
+ Thread.current.backend.send(self, mesg)
54
+ end
55
+
56
+ def write(str)
57
+ Thread.current.backend.send(self, str)
58
+ end
59
+ alias_method :<<, :write
60
+
61
+ def readpartial(maxlen, str = +'')
62
+ Thread.current.backend.recv(self, str, maxlen)
63
+ end
64
+
46
65
  ZERO_LINGER = [0, 0].pack('ii').freeze
47
66
 
48
67
  def dont_linger
@@ -118,6 +137,45 @@ class ::TCPSocket
118
137
  def reuse_port
119
138
  setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_REUSEPORT, 1)
120
139
  end
140
+
141
+ def recv(maxlen, flags = 0, outbuf = nil)
142
+ Thread.current.backend.recv(self, buf || +'', maxlen)
143
+ end
144
+
145
+ def recv_loop(&block)
146
+ Thread.current.backend.recv_loop(self, &block)
147
+ end
148
+
149
+ def send(mesg, flags = 0)
150
+ Thread.current.backend.send(self, mesg)
151
+ end
152
+
153
+ def write(str)
154
+ Thread.current.backend.send(self, str)
155
+ end
156
+ alias_method :<<, :write
157
+
158
+ def readpartial(maxlen, str = nil)
159
+ @read_buffer ||= +''
160
+ result = Thread.current.backend.recv(self, @read_buffer, maxlen)
161
+ raise EOFError unless result
162
+
163
+ if str
164
+ str << @read_buffer
165
+ else
166
+ str = @read_buffer
167
+ end
168
+ @read_buffer = +''
169
+ str
170
+ end
171
+
172
+ def read_nonblock(len, str = nil, exception: true)
173
+ @io.read_nonblock(len, str, exception: exception)
174
+ end
175
+
176
+ def write_nonblock(buf, exception: true)
177
+ @io.write_nonblock(buf, exception: exception)
178
+ end
121
179
  end
122
180
 
123
181
  # Override stock TCPServer code by encapsulating a Socket instance.