polyphony 0.43.10 → 0.45.2
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/.rubocop.yml +8 -1
- data/CHANGELOG.md +37 -0
- data/Gemfile.lock +16 -6
- data/Rakefile +1 -1
- data/TODO.md +15 -10
- data/docs/_posts/2020-07-26-polyphony-0.44.md +77 -0
- data/docs/api-reference/thread.md +1 -1
- data/docs/getting-started/overview.md +14 -14
- data/docs/getting-started/tutorial.md +1 -1
- data/examples/adapters/redis_client.rb +3 -1
- data/examples/adapters/redis_pubsub_perf.rb +11 -8
- data/examples/adapters/sequel_mysql.rb +23 -0
- data/examples/adapters/sequel_mysql_pool.rb +33 -0
- 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/pry.rb +18 -0
- data/examples/io/rack_server.rb +71 -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/{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 -1
- data/examples/performance/thread-vs-fiber/polyphony_server_read_loop.rb +1 -1
- data/examples/performance/thread-vs-fiber/threaded_server.rb +1 -5
- data/examples/performance/thread_pool_perf.rb +6 -7
- data/ext/polyphony/backend.h +40 -0
- data/ext/polyphony/event.c +3 -3
- data/ext/polyphony/extconf.rb +1 -1
- data/ext/polyphony/fiber.c +66 -6
- data/ext/polyphony/{libev_agent.c → libev_backend.c} +239 -235
- data/ext/polyphony/polyphony.c +3 -3
- data/ext/polyphony/polyphony.h +15 -23
- data/ext/polyphony/polyphony_ext.c +3 -4
- data/ext/polyphony/queue.c +25 -12
- data/ext/polyphony/ring_buffer.c +0 -1
- data/ext/polyphony/thread.c +36 -33
- data/lib/polyphony.rb +25 -38
- data/lib/polyphony/adapters/fs.rb +1 -1
- data/lib/polyphony/adapters/irb.rb +2 -17
- data/lib/polyphony/adapters/mysql2.rb +19 -0
- data/lib/polyphony/adapters/postgres.rb +5 -5
- data/lib/polyphony/adapters/process.rb +2 -2
- data/lib/polyphony/adapters/readline.rb +17 -0
- data/lib/polyphony/adapters/redis.rb +1 -1
- data/lib/polyphony/adapters/sequel.rb +45 -0
- data/lib/polyphony/core/exceptions.rb +11 -0
- data/lib/polyphony/core/global_api.rb +17 -12
- data/lib/polyphony/core/resource_pool.rb +20 -7
- data/lib/polyphony/core/sync.rb +46 -8
- data/lib/polyphony/core/throttler.rb +1 -1
- data/lib/polyphony/extensions/core.rb +38 -25
- data/lib/polyphony/extensions/fiber.rb +12 -45
- data/lib/polyphony/extensions/io.rb +45 -12
- data/lib/polyphony/extensions/openssl.rb +6 -6
- data/lib/polyphony/extensions/socket.rb +22 -15
- data/lib/polyphony/extensions/thread.rb +6 -5
- data/lib/polyphony/net.rb +2 -1
- data/lib/polyphony/version.rb +1 -1
- data/polyphony.gemspec +7 -3
- data/test/helper.rb +1 -1
- data/test/{test_agent.rb → test_backend.rb} +22 -22
- data/test/test_fiber.rb +28 -11
- data/test/test_io.rb +17 -1
- data/test/test_kernel.rb +5 -0
- data/test/test_resource_pool.rb +50 -16
- data/test/test_signal.rb +5 -29
- data/test/test_socket.rb +17 -0
- data/test/test_sync.rb +52 -0
- metadata +126 -98
- data/.gitbook.yaml +0 -4
- data/examples/adapters/concurrent-ruby.rb +0 -9
- data/examples/core/04-handling-signals.rb +0 -19
- data/examples/core/xx-agent.rb +0 -102
- data/examples/core/xx-at_exit.rb +0 -29
- 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/ext/polyphony/agent.h +0 -39
|
@@ -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
|
|
|
@@ -67,6 +53,10 @@ module Polyphony
|
|
|
67
53
|
else RuntimeError.new
|
|
68
54
|
end
|
|
69
55
|
end
|
|
56
|
+
|
|
57
|
+
def interject(&block)
|
|
58
|
+
raise Polyphony::Interjection.new(block)
|
|
59
|
+
end
|
|
70
60
|
end
|
|
71
61
|
|
|
72
62
|
# Fiber supervision
|
|
@@ -177,22 +167,6 @@ module Polyphony
|
|
|
177
167
|
end
|
|
178
168
|
end
|
|
179
169
|
|
|
180
|
-
# Messaging functionality
|
|
181
|
-
module FiberMessaging
|
|
182
|
-
def <<(value)
|
|
183
|
-
@mailbox << value
|
|
184
|
-
end
|
|
185
|
-
alias_method :send, :<<
|
|
186
|
-
|
|
187
|
-
def receive
|
|
188
|
-
@mailbox.shift
|
|
189
|
-
end
|
|
190
|
-
|
|
191
|
-
def receive_pending
|
|
192
|
-
@mailbox.shift_all
|
|
193
|
-
end
|
|
194
|
-
end
|
|
195
|
-
|
|
196
170
|
# Methods for controlling child fibers
|
|
197
171
|
module ChildFiberControl
|
|
198
172
|
def children
|
|
@@ -221,14 +195,14 @@ module Polyphony
|
|
|
221
195
|
def await_all_children
|
|
222
196
|
return unless @children && !@children.empty?
|
|
223
197
|
|
|
224
|
-
|
|
198
|
+
results = @children.dup
|
|
225
199
|
@on_child_done = proc do |c, r|
|
|
226
|
-
|
|
227
|
-
|
|
200
|
+
results[c] = r
|
|
201
|
+
schedule if @children.empty?
|
|
228
202
|
end
|
|
229
203
|
suspend
|
|
230
204
|
@on_child_done = nil
|
|
231
|
-
|
|
205
|
+
results.values
|
|
232
206
|
end
|
|
233
207
|
|
|
234
208
|
def shutdown_all_children
|
|
@@ -245,7 +219,6 @@ module Polyphony
|
|
|
245
219
|
@parent = parent
|
|
246
220
|
@caller = caller
|
|
247
221
|
@block = block
|
|
248
|
-
@mailbox = Polyphony::Queue.new
|
|
249
222
|
__fiber_trace__(:fiber_create, self)
|
|
250
223
|
schedule
|
|
251
224
|
end
|
|
@@ -275,7 +248,6 @@ module Polyphony
|
|
|
275
248
|
# allows the fiber to be scheduled and to receive messages.
|
|
276
249
|
def setup_raw
|
|
277
250
|
@thread = Thread.current
|
|
278
|
-
@mailbox = Polyphony::Queue.new
|
|
279
251
|
end
|
|
280
252
|
|
|
281
253
|
def setup_main_fiber
|
|
@@ -284,11 +256,10 @@ module Polyphony
|
|
|
284
256
|
@thread = Thread.current
|
|
285
257
|
@running = true
|
|
286
258
|
@children&.clear
|
|
287
|
-
@mailbox = Polyphony::Queue.new
|
|
288
259
|
end
|
|
289
260
|
|
|
290
261
|
def restart_self(first_value)
|
|
291
|
-
@mailbox =
|
|
262
|
+
@mailbox = nil
|
|
292
263
|
@when_done_procs = nil
|
|
293
264
|
@waiting_fibers = nil
|
|
294
265
|
run(first_value)
|
|
@@ -320,13 +291,10 @@ module Polyphony
|
|
|
320
291
|
def inform_dependants(result, uncaught_exception)
|
|
321
292
|
@parent&.child_done(self, result)
|
|
322
293
|
@when_done_procs&.each { |p| p.(result) }
|
|
323
|
-
@waiting_fibers&.each_key
|
|
324
|
-
|
|
325
|
-
end
|
|
326
|
-
return unless uncaught_exception && !@waiting_fibers
|
|
327
|
-
|
|
294
|
+
@waiting_fibers&.each_key { |f| f.schedule(result) }
|
|
295
|
+
|
|
328
296
|
# propagate unaught exception to parent
|
|
329
|
-
@parent&.schedule(result)
|
|
297
|
+
@parent&.schedule(result) if uncaught_exception && !@waiting_fibers
|
|
330
298
|
end
|
|
331
299
|
|
|
332
300
|
def when_done(&block)
|
|
@@ -340,7 +308,6 @@ end
|
|
|
340
308
|
class ::Fiber
|
|
341
309
|
prepend Polyphony::FiberControl
|
|
342
310
|
include Polyphony::FiberSupervision
|
|
343
|
-
include Polyphony::FiberMessaging
|
|
344
311
|
include Polyphony::ChildFiberControl
|
|
345
312
|
include Polyphony::FiberLifeCycle
|
|
346
313
|
|
|
@@ -99,7 +99,7 @@ class ::IO
|
|
|
99
99
|
alias_method :orig_read, :read
|
|
100
100
|
def read(len = nil)
|
|
101
101
|
@read_buffer ||= +''
|
|
102
|
-
result = Thread.current.
|
|
102
|
+
result = Thread.current.backend.read(self, @read_buffer, len, true)
|
|
103
103
|
return nil unless result
|
|
104
104
|
|
|
105
105
|
already_read = @read_buffer
|
|
@@ -110,7 +110,7 @@ class ::IO
|
|
|
110
110
|
alias_method :orig_readpartial, :read
|
|
111
111
|
def readpartial(len, str = nil)
|
|
112
112
|
@read_buffer ||= +''
|
|
113
|
-
result = Thread.current.
|
|
113
|
+
result = Thread.current.backend.read(self, @read_buffer, len, false)
|
|
114
114
|
raise EOFError unless result
|
|
115
115
|
|
|
116
116
|
if str
|
|
@@ -124,12 +124,12 @@ class ::IO
|
|
|
124
124
|
|
|
125
125
|
alias_method :orig_write, :write
|
|
126
126
|
def write(str, *args)
|
|
127
|
-
Thread.current.
|
|
127
|
+
Thread.current.backend.write(self, str, *args)
|
|
128
128
|
end
|
|
129
129
|
|
|
130
130
|
alias_method :orig_write_chevron, :<<
|
|
131
131
|
def <<(str)
|
|
132
|
-
Thread.current.
|
|
132
|
+
Thread.current.backend.write(self, str)
|
|
133
133
|
self
|
|
134
134
|
end
|
|
135
135
|
|
|
@@ -163,20 +163,29 @@ class ::IO
|
|
|
163
163
|
# def putc(obj)
|
|
164
164
|
# end
|
|
165
165
|
|
|
166
|
+
LINEFEED = "\n"
|
|
167
|
+
LINEFEED_RE = /\n$/.freeze
|
|
168
|
+
|
|
166
169
|
alias_method :orig_puts, :puts
|
|
167
170
|
def puts(*args)
|
|
168
171
|
if args.empty?
|
|
169
|
-
write
|
|
172
|
+
write LINEFEED
|
|
170
173
|
return
|
|
171
174
|
end
|
|
172
175
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
176
|
+
idx = 0
|
|
177
|
+
while idx < args.size
|
|
178
|
+
arg = args[idx]
|
|
179
|
+
args[idx] = arg = arg.to_s unless arg.is_a?(String)
|
|
180
|
+
if arg =~ LINEFEED_RE
|
|
181
|
+
idx += 1
|
|
182
|
+
else
|
|
183
|
+
args.insert(idx + 1, LINEFEED)
|
|
184
|
+
idx += 2
|
|
185
|
+
end
|
|
178
186
|
end
|
|
179
|
-
|
|
187
|
+
|
|
188
|
+
write(*args)
|
|
180
189
|
nil
|
|
181
190
|
end
|
|
182
191
|
|
|
@@ -203,7 +212,7 @@ class ::IO
|
|
|
203
212
|
end
|
|
204
213
|
|
|
205
214
|
def read_loop(&block)
|
|
206
|
-
Thread.current.
|
|
215
|
+
Thread.current.backend.read_loop(self, &block)
|
|
207
216
|
end
|
|
208
217
|
|
|
209
218
|
# alias_method :orig_read, :read
|
|
@@ -218,4 +227,28 @@ class ::IO
|
|
|
218
227
|
# end
|
|
219
228
|
# outbuf
|
|
220
229
|
# end
|
|
230
|
+
|
|
231
|
+
def wait_readable(timeout = nil)
|
|
232
|
+
if timeout
|
|
233
|
+
move_on_after(timeout) do
|
|
234
|
+
Thread.current.backend.wait_io(self, false)
|
|
235
|
+
self
|
|
236
|
+
end
|
|
237
|
+
else
|
|
238
|
+
Thread.current.backend.wait_io(self, false)
|
|
239
|
+
self
|
|
240
|
+
end
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
def wait_writable(timeout = nil)
|
|
244
|
+
if timeout
|
|
245
|
+
move_on_after(timeout) do
|
|
246
|
+
Thread.current.backend.wait_io(self, true)
|
|
247
|
+
self
|
|
248
|
+
end
|
|
249
|
+
else
|
|
250
|
+
Thread.current.backend.wait_io(self, true)
|
|
251
|
+
self
|
|
252
|
+
end
|
|
253
|
+
end
|
|
221
254
|
end
|
|
@@ -28,8 +28,8 @@ class ::OpenSSL::SSL::SSLSocket
|
|
|
28
28
|
loop do
|
|
29
29
|
result = accept_nonblock(exception: false)
|
|
30
30
|
case result
|
|
31
|
-
when :wait_readable then Thread.current.
|
|
32
|
-
when :wait_writable then Thread.current.
|
|
31
|
+
when :wait_readable then Thread.current.backend.wait_io(io, false)
|
|
32
|
+
when :wait_writable then Thread.current.backend.wait_io(io, true)
|
|
33
33
|
else
|
|
34
34
|
return result
|
|
35
35
|
end
|
|
@@ -40,8 +40,8 @@ class ::OpenSSL::SSL::SSLSocket
|
|
|
40
40
|
def sysread(maxlen, buf = +'')
|
|
41
41
|
loop do
|
|
42
42
|
case (result = read_nonblock(maxlen, buf, exception: false))
|
|
43
|
-
when :wait_readable then Thread.current.
|
|
44
|
-
when :wait_writable then Thread.current.
|
|
43
|
+
when :wait_readable then Thread.current.backend.wait_io(io, false)
|
|
44
|
+
when :wait_writable then Thread.current.backend.wait_io(io, true)
|
|
45
45
|
else return result
|
|
46
46
|
end
|
|
47
47
|
end
|
|
@@ -51,8 +51,8 @@ class ::OpenSSL::SSL::SSLSocket
|
|
|
51
51
|
def syswrite(buf)
|
|
52
52
|
loop do
|
|
53
53
|
case (result = write_nonblock(buf, exception: false))
|
|
54
|
-
when :wait_readable then Thread.current.
|
|
55
|
-
when :wait_writable then Thread.current.
|
|
54
|
+
when :wait_readable then Thread.current.backend.wait_io(io, false)
|
|
55
|
+
when :wait_writable then Thread.current.backend.wait_io(io, true)
|
|
56
56
|
else
|
|
57
57
|
return result
|
|
58
58
|
end
|
|
@@ -5,26 +5,17 @@ require 'socket'
|
|
|
5
5
|
require_relative './io'
|
|
6
6
|
require_relative '../core/thread_pool'
|
|
7
7
|
|
|
8
|
-
class ::BasicSocket
|
|
9
|
-
def write_nonblock(string, _options = {})
|
|
10
|
-
write(string)
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
def read_nonblock(maxlen, str = nil, _options = {})
|
|
14
|
-
readpartial(maxlen, str)
|
|
15
|
-
end
|
|
16
|
-
end
|
|
17
|
-
|
|
18
8
|
# Socket overrides (eventually rewritten in C)
|
|
19
9
|
class ::Socket
|
|
20
10
|
def accept
|
|
21
|
-
Thread.current.
|
|
11
|
+
Thread.current.backend.accept(self)
|
|
22
12
|
end
|
|
23
13
|
|
|
24
14
|
NO_EXCEPTION = { exception: false }.freeze
|
|
25
15
|
|
|
26
|
-
def connect(
|
|
27
|
-
|
|
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)
|
|
28
19
|
end
|
|
29
20
|
|
|
30
21
|
def recv(maxlen, flags = 0, outbuf = nil)
|
|
@@ -33,7 +24,7 @@ class ::Socket
|
|
|
33
24
|
result = recv_nonblock(maxlen, flags, outbuf, **NO_EXCEPTION)
|
|
34
25
|
case result
|
|
35
26
|
when nil then raise IOError
|
|
36
|
-
when :wait_readable then Thread.current.
|
|
27
|
+
when :wait_readable then Thread.current.backend.wait_io(self, false)
|
|
37
28
|
else
|
|
38
29
|
return result
|
|
39
30
|
end
|
|
@@ -46,7 +37,7 @@ class ::Socket
|
|
|
46
37
|
result = recvfrom_nonblock(maxlen, flags, @read_buffer, **NO_EXCEPTION)
|
|
47
38
|
case result
|
|
48
39
|
when nil then raise IOError
|
|
49
|
-
when :wait_readable then Thread.current.
|
|
40
|
+
when :wait_readable then Thread.current.backend.wait_io(self, false)
|
|
50
41
|
else
|
|
51
42
|
return result
|
|
52
43
|
end
|
|
@@ -67,6 +58,10 @@ class ::Socket
|
|
|
67
58
|
setsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEADDR, 1)
|
|
68
59
|
end
|
|
69
60
|
|
|
61
|
+
def reuse_port
|
|
62
|
+
setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_REUSEPORT, 1)
|
|
63
|
+
end
|
|
64
|
+
|
|
70
65
|
class << self
|
|
71
66
|
alias_method :orig_getaddrinfo, :getaddrinfo
|
|
72
67
|
def getaddrinfo(*args)
|
|
@@ -120,6 +115,18 @@ class ::TCPSocket
|
|
|
120
115
|
def reuse_addr
|
|
121
116
|
setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_REUSEADDR, 1)
|
|
122
117
|
end
|
|
118
|
+
|
|
119
|
+
def reuse_port
|
|
120
|
+
setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_REUSEPORT, 1)
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def read_nonblock(len, str = nil, exception: true)
|
|
124
|
+
@io.read_nonblock(len, str, exception: exception)
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def write_nonblock(buf, exception: true)
|
|
128
|
+
@io.write_nonblock(buf, exception: exception)
|
|
129
|
+
end
|
|
123
130
|
end
|
|
124
131
|
|
|
125
132
|
# Override stock TCPServer code by encapsulating a Socket instance.
|
|
@@ -16,21 +16,22 @@ class ::Thread
|
|
|
16
16
|
end
|
|
17
17
|
|
|
18
18
|
def execute
|
|
19
|
-
#
|
|
19
|
+
# backend must be created in the context of the new thread, therefore it
|
|
20
20
|
# cannot be created in Thread#initialize
|
|
21
|
-
@
|
|
21
|
+
@backend = Polyphony::Backend.new
|
|
22
22
|
setup
|
|
23
23
|
@ready = true
|
|
24
24
|
result = @block.(*@args)
|
|
25
25
|
rescue Polyphony::MoveOn, Polyphony::Terminate => e
|
|
26
26
|
result = e.value
|
|
27
|
-
rescue Exception =>
|
|
27
|
+
rescue Exception => e
|
|
28
|
+
result = e
|
|
28
29
|
ensure
|
|
29
30
|
@ready = true
|
|
30
31
|
finalize(result)
|
|
31
32
|
end
|
|
32
33
|
|
|
33
|
-
attr_accessor :
|
|
34
|
+
attr_accessor :backend
|
|
34
35
|
|
|
35
36
|
def setup
|
|
36
37
|
@main_fiber = Fiber.current
|
|
@@ -48,7 +49,7 @@ class ::Thread
|
|
|
48
49
|
@result = result
|
|
49
50
|
signal_waiters(result)
|
|
50
51
|
end
|
|
51
|
-
@
|
|
52
|
+
@backend.finalize
|
|
52
53
|
end
|
|
53
54
|
|
|
54
55
|
def signal_waiters(result)
|
data/lib/polyphony/net.rb
CHANGED
|
@@ -35,9 +35,10 @@ module Polyphony
|
|
|
35
35
|
::Socket.new(:INET, :STREAM).tap do |s|
|
|
36
36
|
s.reuse_addr if opts[:reuse_addr]
|
|
37
37
|
s.dont_linger if opts[:dont_linger]
|
|
38
|
+
s.reuse_port if opts[:reuse_port]
|
|
38
39
|
addr = ::Socket.sockaddr_in(port, host)
|
|
39
40
|
s.bind(addr)
|
|
40
|
-
s.listen(
|
|
41
|
+
s.listen(opts[:backlog] || Socket::SOMAXCONN)
|
|
41
42
|
end
|
|
42
43
|
end
|
|
43
44
|
|
data/lib/polyphony/version.rb
CHANGED
data/polyphony.gemspec
CHANGED
|
@@ -21,17 +21,21 @@ Gem::Specification.new do |s|
|
|
|
21
21
|
s.require_paths = ["lib"]
|
|
22
22
|
s.required_ruby_version = '>= 2.6'
|
|
23
23
|
|
|
24
|
-
s.add_development_dependency '
|
|
25
|
-
s.add_development_dependency 'localhost', '1.1.4'
|
|
24
|
+
s.add_development_dependency 'rake-compiler', '1.0.5'
|
|
26
25
|
s.add_development_dependency 'minitest', '5.13.0'
|
|
27
26
|
s.add_development_dependency 'minitest-reporters', '1.4.2'
|
|
28
27
|
s.add_development_dependency 'simplecov', '0.17.1'
|
|
29
28
|
s.add_development_dependency 'rubocop', '0.85.1'
|
|
29
|
+
s.add_development_dependency 'pry', '0.13.1'
|
|
30
|
+
|
|
30
31
|
s.add_development_dependency 'pg', '1.1.4'
|
|
31
|
-
s.add_development_dependency 'rake-compiler', '1.0.5'
|
|
32
32
|
s.add_development_dependency 'redis', '4.1.0'
|
|
33
33
|
s.add_development_dependency 'hiredis', '0.6.3'
|
|
34
34
|
s.add_development_dependency 'http_parser.rb', '~>0.6.0'
|
|
35
|
+
s.add_development_dependency 'rack', '>=2.0.8', '<2.3.0'
|
|
36
|
+
s.add_development_dependency 'mysql2', '0.5.3'
|
|
37
|
+
s.add_development_dependency 'sequel', '5.34.0'
|
|
38
|
+
s.add_development_dependency 'httparty', '0.17.1'
|
|
35
39
|
|
|
36
40
|
s.add_development_dependency 'jekyll', '~>3.8.6'
|
|
37
41
|
s.add_development_dependency 'jekyll-remote-theme', '~>0.4.1'
|
data/test/helper.rb
CHANGED
|
@@ -31,7 +31,7 @@ class MiniTest::Test
|
|
|
31
31
|
end
|
|
32
32
|
Fiber.current.setup_main_fiber
|
|
33
33
|
Fiber.current.instance_variable_set(:@auto_watcher, nil)
|
|
34
|
-
Thread.current.
|
|
34
|
+
Thread.current.backend = Polyphony::Backend.new
|
|
35
35
|
sleep 0 # apparently this helps with timer accuracy
|
|
36
36
|
end
|
|
37
37
|
|
|
@@ -2,39 +2,39 @@
|
|
|
2
2
|
|
|
3
3
|
require_relative 'helper'
|
|
4
4
|
|
|
5
|
-
class
|
|
5
|
+
class BackendTest < MiniTest::Test
|
|
6
6
|
def setup
|
|
7
7
|
super
|
|
8
|
-
@
|
|
9
|
-
@
|
|
10
|
-
Thread.current.
|
|
8
|
+
@prev_backend = Thread.current.backend
|
|
9
|
+
@backend = Polyphony::Backend.new
|
|
10
|
+
Thread.current.backend = @backend
|
|
11
11
|
end
|
|
12
12
|
|
|
13
13
|
def teardown
|
|
14
|
-
@
|
|
15
|
-
Thread.current.
|
|
14
|
+
@backend.finalize
|
|
15
|
+
Thread.current.backend = @prev_backend
|
|
16
16
|
end
|
|
17
17
|
|
|
18
18
|
def test_sleep
|
|
19
19
|
count = 0
|
|
20
20
|
t0 = Time.now
|
|
21
21
|
spin {
|
|
22
|
-
@
|
|
22
|
+
@backend.sleep 0.01
|
|
23
23
|
count += 1
|
|
24
|
-
@
|
|
24
|
+
@backend.sleep 0.01
|
|
25
25
|
count += 1
|
|
26
|
-
@
|
|
26
|
+
@backend.sleep 0.01
|
|
27
27
|
count += 1
|
|
28
28
|
}.await
|
|
29
|
-
|
|
29
|
+
assert_in_delta 0.03, Time.now - t0, 0.005
|
|
30
30
|
assert_equal 3, count
|
|
31
31
|
end
|
|
32
32
|
|
|
33
33
|
def test_write_read_partial
|
|
34
34
|
i, o = IO.pipe
|
|
35
35
|
buf = +''
|
|
36
|
-
f = spin { @
|
|
37
|
-
@
|
|
36
|
+
f = spin { @backend.read(i, buf, 5, false) }
|
|
37
|
+
@backend.write(o, 'Hello world')
|
|
38
38
|
return_value = f.await
|
|
39
39
|
|
|
40
40
|
assert_equal 'Hello', buf
|
|
@@ -44,10 +44,10 @@ class AgentTest < MiniTest::Test
|
|
|
44
44
|
def test_write_read_to_eof_limited_buffer
|
|
45
45
|
i, o = IO.pipe
|
|
46
46
|
buf = +''
|
|
47
|
-
f = spin { @
|
|
48
|
-
@
|
|
47
|
+
f = spin { @backend.read(i, buf, 5, true) }
|
|
48
|
+
@backend.write(o, 'Hello')
|
|
49
49
|
snooze
|
|
50
|
-
@
|
|
50
|
+
@backend.write(o, ' world')
|
|
51
51
|
snooze
|
|
52
52
|
o.close
|
|
53
53
|
return_value = f.await
|
|
@@ -59,10 +59,10 @@ class AgentTest < MiniTest::Test
|
|
|
59
59
|
def test_write_read_to_eof
|
|
60
60
|
i, o = IO.pipe
|
|
61
61
|
buf = +''
|
|
62
|
-
f = spin { @
|
|
63
|
-
@
|
|
62
|
+
f = spin { @backend.read(i, buf, 10**6, true) }
|
|
63
|
+
@backend.write(o, 'Hello')
|
|
64
64
|
snooze
|
|
65
|
-
@
|
|
65
|
+
@backend.write(o, ' world')
|
|
66
66
|
snooze
|
|
67
67
|
o.close
|
|
68
68
|
return_value = f.await
|
|
@@ -73,11 +73,11 @@ class AgentTest < MiniTest::Test
|
|
|
73
73
|
|
|
74
74
|
def test_waitpid
|
|
75
75
|
pid = fork do
|
|
76
|
-
@
|
|
76
|
+
@backend.post_fork
|
|
77
77
|
exit(42)
|
|
78
78
|
end
|
|
79
79
|
|
|
80
|
-
result = @
|
|
80
|
+
result = @backend.waitpid(pid)
|
|
81
81
|
assert_equal [pid, 42], result
|
|
82
82
|
end
|
|
83
83
|
|
|
@@ -87,7 +87,7 @@ class AgentTest < MiniTest::Test
|
|
|
87
87
|
buf = []
|
|
88
88
|
spin do
|
|
89
89
|
buf << :ready
|
|
90
|
-
@
|
|
90
|
+
@backend.read_loop(i) { |d| buf << d }
|
|
91
91
|
buf << :done
|
|
92
92
|
end
|
|
93
93
|
|
|
@@ -107,7 +107,7 @@ class AgentTest < MiniTest::Test
|
|
|
107
107
|
|
|
108
108
|
clients = []
|
|
109
109
|
server_fiber = spin do
|
|
110
|
-
@
|
|
110
|
+
@backend.accept_loop(server) { |c| clients << c }
|
|
111
111
|
end
|
|
112
112
|
|
|
113
113
|
c1 = TCPSocket.new('127.0.0.1', 1234)
|