polyphony 0.45.0 → 0.46.0
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/workflows/test.yml +2 -0
- data/.gitmodules +0 -0
- data/.rubocop.yml +1 -0
- data/CHANGELOG.md +38 -0
- data/Gemfile.lock +11 -3
- data/README.md +3 -3
- data/Rakefile +1 -1
- data/TODO.md +10 -18
- data/examples/adapters/redis_client.rb +3 -1
- data/examples/adapters/redis_pubsub_perf.rb +11 -8
- data/examples/adapters/sequel_mysql.rb +1 -1
- data/examples/adapters/sequel_pg.rb +24 -0
- data/examples/core/{02-awaiting-fibers.rb → await.rb} +0 -0
- data/examples/core/{xx-channels.rb → channels.rb} +0 -0
- data/examples/core/deferring-an-operation.rb +16 -0
- data/examples/core/{xx-erlang-style-genserver.rb → erlang-style-genserver.rb} +16 -9
- data/examples/core/{xx-forking.rb → forking.rb} +1 -1
- data/examples/core/handling-signals.rb +11 -0
- data/examples/core/{03-interrupting.rb → interrupt.rb} +0 -0
- data/examples/core/{xx-pingpong.rb → pingpong.rb} +7 -5
- data/examples/core/{xx-recurrent-timer.rb → recurrent-timer.rb} +1 -1
- data/examples/core/{xx-resource_delegate.rb → resource_delegate.rb} +3 -4
- data/examples/core/{01-spinning-up-fibers.rb → spin.rb} +1 -1
- data/examples/core/{xx-spin_error_backtrace.rb → spin_error_backtrace.rb} +1 -1
- data/examples/core/{xx-supervise-process.rb → supervise-process.rb} +8 -5
- data/examples/core/supervisor.rb +20 -0
- data/examples/core/{xx-thread-sleep.rb → thread-sleep.rb} +0 -0
- data/examples/core/{xx-thread_pool.rb → thread_pool.rb} +0 -0
- data/examples/core/{xx-throttling.rb → throttling.rb} +0 -0
- data/examples/core/{xx-timeout.rb → timeout.rb} +0 -0
- data/examples/core/{xx-using-a-mutex.rb → using-a-mutex.rb} +0 -0
- data/examples/core/{xx-worker-thread.rb → worker-thread.rb} +2 -2
- data/examples/io/{xx-backticks.rb → backticks.rb} +0 -0
- data/examples/io/{xx-echo_client.rb → echo_client.rb} +1 -1
- data/examples/io/{xx-echo_client_from_stdin.rb → echo_client_from_stdin.rb} +2 -2
- data/examples/io/{xx-echo_pipe.rb → echo_pipe.rb} +1 -1
- data/examples/io/{xx-echo_server.rb → echo_server.rb} +0 -0
- data/examples/io/{xx-echo_server_with_timeout.rb → echo_server_with_timeout.rb} +1 -1
- data/examples/io/{xx-echo_stdin.rb → echo_stdin.rb} +0 -0
- data/examples/io/{xx-happy-eyeballs.rb → happy-eyeballs.rb} +0 -0
- data/examples/io/{xx-httparty.rb → httparty.rb} +4 -13
- data/examples/io/{xx-irb.rb → irb.rb} +0 -0
- data/examples/io/{xx-net-http.rb → net-http.rb} +0 -0
- data/examples/io/{xx-open.rb → open.rb} +0 -0
- data/examples/io/{xx-pry.rb → pry.rb} +0 -0
- data/examples/io/{xx-rack_server.rb → rack_server.rb} +0 -0
- data/examples/io/raw.rb +14 -0
- data/examples/io/reline.rb +18 -0
- data/examples/io/{xx-system.rb → system.rb} +1 -1
- data/examples/io/{xx-tcpserver.rb → tcpserver.rb} +0 -0
- data/examples/io/{xx-tcpsocket.rb → tcpsocket.rb} +0 -0
- data/examples/io/tunnel.rb +6 -1
- data/examples/io/{xx-zip.rb → zip.rb} +0 -0
- data/examples/performance/fiber_transfer.rb +2 -1
- data/examples/performance/fs_read.rb +5 -6
- data/examples/performance/multi_snooze.rb +0 -1
- data/examples/{io/xx-switch.rb → performance/switch.rb} +2 -1
- data/examples/performance/thread-vs-fiber/{xx-httparty_multi.rb → httparty_multi.rb} +3 -4
- data/examples/performance/thread-vs-fiber/{xx-httparty_threaded.rb → httparty_threaded.rb} +0 -0
- data/examples/performance/thread-vs-fiber/polyphony_mt_server.rb +1 -1
- data/examples/performance/thread-vs-fiber/polyphony_server.rb +1 -2
- data/examples/performance/thread-vs-fiber/threaded_server.rb +1 -5
- data/examples/performance/thread_pool_perf.rb +6 -7
- data/ext/liburing/liburing.h +585 -0
- data/ext/liburing/liburing/README.md +4 -0
- data/ext/liburing/liburing/barrier.h +73 -0
- data/ext/liburing/liburing/compat.h +15 -0
- data/ext/liburing/liburing/io_uring.h +343 -0
- data/ext/liburing/queue.c +333 -0
- data/ext/liburing/register.c +187 -0
- data/ext/liburing/setup.c +210 -0
- data/ext/liburing/syscall.c +54 -0
- data/ext/liburing/syscall.h +18 -0
- data/ext/polyphony/backend.h +1 -16
- data/ext/polyphony/backend_common.h +109 -0
- data/ext/polyphony/backend_io_uring.c +884 -0
- data/ext/polyphony/backend_io_uring_context.c +73 -0
- data/ext/polyphony/backend_io_uring_context.h +52 -0
- data/ext/polyphony/{libev_backend.c → backend_libev.c} +255 -345
- data/ext/polyphony/event.c +1 -1
- data/ext/polyphony/extconf.rb +31 -13
- data/ext/polyphony/fiber.c +111 -27
- data/ext/polyphony/libev.c +4 -0
- data/ext/polyphony/libev.h +8 -2
- data/ext/polyphony/liburing.c +8 -0
- data/ext/polyphony/playground.c +51 -0
- data/ext/polyphony/polyphony.c +6 -8
- data/ext/polyphony/polyphony.h +29 -25
- data/ext/polyphony/polyphony_ext.c +13 -6
- data/ext/polyphony/queue.c +3 -4
- data/ext/polyphony/ring_buffer.c +0 -1
- data/ext/polyphony/runqueue.c +102 -0
- data/ext/polyphony/runqueue_ring_buffer.c +85 -0
- data/ext/polyphony/runqueue_ring_buffer.h +31 -0
- data/ext/polyphony/thread.c +45 -92
- data/lib/polyphony.rb +2 -2
- data/lib/polyphony/adapters/fs.rb +1 -1
- data/lib/polyphony/adapters/process.rb +0 -3
- data/lib/polyphony/adapters/redis.rb +1 -1
- data/lib/polyphony/adapters/trace.rb +2 -2
- data/lib/polyphony/core/global_api.rb +9 -12
- data/lib/polyphony/core/sync.rb +6 -2
- data/lib/polyphony/extensions/core.rb +6 -24
- data/lib/polyphony/extensions/debug.rb +13 -0
- data/lib/polyphony/extensions/fiber.rb +21 -44
- data/lib/polyphony/extensions/io.rb +55 -10
- data/lib/polyphony/extensions/socket.rb +70 -12
- data/lib/polyphony/version.rb +1 -1
- data/polyphony.gemspec +3 -2
- data/test/helper.rb +36 -4
- data/test/io_uring_test.rb +55 -0
- data/test/stress.rb +5 -2
- data/test/test_backend.rb +4 -6
- data/test/test_ext.rb +1 -2
- data/test/test_fiber.rb +31 -24
- data/test/test_global_api.rb +58 -31
- data/test/test_io.rb +58 -0
- data/test/test_signal.rb +11 -8
- data/test/test_socket.rb +17 -0
- data/test/test_sync.rb +21 -0
- data/test/test_throttler.rb +3 -6
- data/test/test_trace.rb +7 -5
- metadata +86 -76
- data/examples/adapters/concurrent-ruby.rb +0 -9
- data/examples/core/04-handling-signals.rb +0 -19
- data/examples/core/xx-at_exit.rb +0 -29
- data/examples/core/xx-backend.rb +0 -102
- data/examples/core/xx-caller.rb +0 -12
- data/examples/core/xx-daemon.rb +0 -14
- data/examples/core/xx-deadlock.rb +0 -8
- data/examples/core/xx-deferring-an-operation.rb +0 -14
- data/examples/core/xx-exception-backtrace.rb +0 -40
- data/examples/core/xx-fork-cleanup.rb +0 -22
- data/examples/core/xx-fork-spin.rb +0 -42
- data/examples/core/xx-fork-terminate.rb +0 -27
- data/examples/core/xx-move_on.rb +0 -23
- data/examples/core/xx-queue-async.rb +0 -120
- data/examples/core/xx-readpartial.rb +0 -18
- data/examples/core/xx-signals.rb +0 -16
- data/examples/core/xx-sleep-forever.rb +0 -9
- data/examples/core/xx-sleeping.rb +0 -25
- data/examples/core/xx-snooze-starve.rb +0 -16
- data/examples/core/xx-spin-fork.rb +0 -49
- data/examples/core/xx-state-machine.rb +0 -51
- data/examples/core/xx-stop.rb +0 -20
- data/examples/core/xx-supervisors.rb +0 -21
- data/examples/core/xx-thread-selector-sleep.rb +0 -51
- data/examples/core/xx-thread-selector-snooze.rb +0 -46
- data/examples/core/xx-thread-snooze.rb +0 -34
- data/examples/core/xx-timer-gc.rb +0 -17
- data/examples/core/xx-trace.rb +0 -79
- data/examples/performance/xx-array.rb +0 -11
- data/examples/performance/xx-fiber-switch.rb +0 -9
- data/examples/performance/xx-snooze.rb +0 -15
- data/examples/xx-spin.rb +0 -32
data/lib/polyphony.rb
CHANGED
|
@@ -76,10 +76,10 @@ module Polyphony
|
|
|
76
76
|
end
|
|
77
77
|
|
|
78
78
|
def install_terminating_signal_handlers
|
|
79
|
-
trap('SIGTERM'
|
|
79
|
+
trap('SIGTERM') { raise SystemExit }
|
|
80
80
|
orig_trap('SIGINT') do
|
|
81
81
|
orig_trap('SIGINT') { exit! }
|
|
82
|
-
|
|
82
|
+
Fiber.schedule_priority_oob_fiber { raise Interrupt }
|
|
83
83
|
end
|
|
84
84
|
end
|
|
85
85
|
|
|
@@ -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
|
-
|
|
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
|
|
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
|
|
96
|
-
Fiber.current.
|
|
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.
|
|
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
|
|
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
|
data/lib/polyphony/core/sync.rb
CHANGED
|
@@ -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
|
-
|
|
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
|
|
162
|
-
#
|
|
163
|
-
#
|
|
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
|
-
|
|
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
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
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
|
-
|
|
212
|
+
results = @children.dup
|
|
229
213
|
@on_child_done = proc do |c, r|
|
|
230
|
-
|
|
214
|
+
results[c] = r
|
|
231
215
|
schedule if @children.empty?
|
|
232
216
|
end
|
|
233
217
|
suspend
|
|
234
218
|
@on_child_done = nil
|
|
235
|
-
|
|
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 =
|
|
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
|
|
328
|
-
|
|
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&.
|
|
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
|
-
|
|
94
|
-
|
|
93
|
+
alias_method :orig_getbyte, :getbyte
|
|
94
|
+
def getbyte
|
|
95
|
+
char = getc
|
|
96
|
+
char ? char.getbyte(0) : nil
|
|
97
|
+
end
|
|
95
98
|
|
|
96
|
-
|
|
97
|
-
|
|
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
|
|
183
|
+
write LINEFEED
|
|
170
184
|
return
|
|
171
185
|
end
|
|
172
186
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
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
|
-
|
|
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(
|
|
17
|
-
|
|
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
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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.
|