polyphony 0.16 → 0.17
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/CHANGELOG.md +11 -0
- data/Gemfile.lock +1 -1
- data/README.md +11 -11
- data/TODO.md +14 -5
- data/examples/core/channel_echo.rb +3 -3
- data/examples/core/enumerator.rb +1 -1
- data/examples/core/fork.rb +1 -1
- data/examples/core/genserver.rb +1 -1
- data/examples/core/lock.rb +3 -3
- data/examples/core/multiple_spawn.rb +2 -2
- data/examples/core/nested_async.rb +1 -1
- data/examples/core/nested_multiple_spawn.rb +3 -3
- data/examples/core/resource.rb +1 -1
- data/examples/core/resource_cancel.rb +1 -1
- data/examples/core/resource_delegate.rb +1 -1
- data/examples/core/sleep_spawn.rb +2 -2
- data/examples/core/spawn.rb +1 -1
- data/examples/core/spawn_cancel.rb +1 -1
- data/examples/core/spawn_error.rb +5 -5
- data/examples/core/supervisor.rb +4 -4
- data/examples/core/supervisor_with_cancel_scope.rb +3 -3
- data/examples/core/supervisor_with_error.rb +4 -4
- data/examples/core/supervisor_with_manual_move_on.rb +4 -4
- data/examples/core/thread.rb +2 -2
- data/examples/core/thread_cancel.rb +2 -2
- data/examples/core/thread_pool.rb +2 -2
- data/examples/core/throttle.rb +3 -3
- data/examples/fs/read.rb +1 -1
- data/examples/http/happy_eyeballs.rb +1 -1
- data/examples/http/http_client.rb +1 -1
- data/examples/http/http_server.rb +1 -1
- data/examples/http/http_server_throttled.rb +1 -1
- data/examples/http/http_ws_server.rb +2 -2
- data/examples/http/https_wss_server.rb +1 -1
- data/examples/interfaces/pg_client.rb +1 -1
- data/examples/interfaces/pg_pool.rb +1 -1
- data/examples/interfaces/redis_channels.rb +5 -5
- data/examples/interfaces/redis_pubsub.rb +2 -2
- data/examples/interfaces/redis_pubsub_perf.rb +3 -3
- data/examples/io/cat.rb +13 -0
- data/examples/io/echo_client.rb +2 -2
- data/examples/io/echo_server.rb +1 -1
- data/examples/io/echo_server_with_timeout.rb +1 -1
- data/examples/io/echo_stdin.rb +1 -1
- data/examples/io/io_read.rb +9 -0
- data/examples/io/system.rb +11 -0
- data/examples/performance/perf_multi_snooze.rb +2 -2
- data/examples/performance/perf_snooze.rb +2 -2
- data/examples/performance/thread-vs-fiber/polyphony_server.rb +2 -2
- data/ext/ev/io.c +53 -4
- data/lib/polyphony/core/coprocess.rb +1 -0
- data/lib/polyphony/core/supervisor.rb +1 -1
- data/lib/polyphony/extensions/io.rb +97 -17
- data/lib/polyphony/extensions/kernel.rb +47 -27
- data/lib/polyphony/http/server.rb +1 -1
- data/lib/polyphony/postgres.rb +0 -4
- data/lib/polyphony/version.rb +1 -1
- data/test/test_coprocess.rb +13 -13
- data/test/test_core.rb +12 -12
- data/test/test_io.rb +95 -3
- data/test/test_kernel.rb +26 -0
- metadata +6 -2
@@ -1,17 +1,62 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
class ::IO
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
4
|
+
class << self
|
5
|
+
alias_method :orig_binread, :binread
|
6
|
+
def binread(name, length = nil, offset = nil)
|
7
|
+
File.open(name, 'rb:ASCII-8BIT') do |f|
|
8
|
+
f.seek(offset) if offset
|
9
|
+
length ? f.read(length) : f.read
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
alias_method :orig_binwrite, :binwrite
|
14
|
+
def binwrite(name, string, offset = nil)
|
15
|
+
File.open(name, 'wb:ASCII-8BIT') do |f|
|
16
|
+
f.seek(offset) if offset
|
17
|
+
f.write(string)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
EMPTY_HASH = {}
|
22
|
+
|
23
|
+
alias_method :orig_foreach, :foreach
|
24
|
+
def foreach(name, sep = $/, limit = nil, getline_args = EMPTY_HASH, &block)
|
25
|
+
sep, limit = $/, sep if sep.is_a?(Integer)
|
26
|
+
File.open(name, 'r') do |f|
|
27
|
+
f.each_line(sep, limit, getline_args, &block)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
alias_method :orig_read, :read
|
32
|
+
def read(name, length = nil, offset = nil, opt = EMPTY_HASH)
|
33
|
+
File.open(name, opt[:mode] || 'r') do |f|
|
34
|
+
f.seek(offset) if offset
|
35
|
+
length ? f.read(length) : f.read
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
alias_method :orig_readlines, :readlines
|
40
|
+
def readlines(name, sep = $/, limit = nil, getline_args = EMPTY_HASH)
|
41
|
+
File.open(name, 'r') do |f|
|
42
|
+
f.readlines(sep, limit, getline_args)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
alias_method :orig_write, :write
|
47
|
+
def write(name, string, offset = nil, opt = EMPTY_HASH)
|
48
|
+
File.open(name, opt[:mode] || 'w') do |f|
|
49
|
+
f.seek(offset) if offset
|
50
|
+
f.write(string)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
alias_method :orig_popen, :popen
|
55
|
+
def popen(*args)
|
56
|
+
Open3.popen2(*args) do |i, o, t|
|
57
|
+
yield o
|
58
|
+
end
|
59
|
+
end
|
15
60
|
end
|
16
61
|
|
17
62
|
# def each(sep = $/, limit = nil, chomp: nil)
|
@@ -34,9 +79,28 @@ class ::IO
|
|
34
79
|
# def getc
|
35
80
|
# end
|
36
81
|
|
37
|
-
|
38
|
-
|
39
|
-
|
82
|
+
alias_method :orig_gets, :gets
|
83
|
+
def gets(sep = $/, limit = nil, chomp: nil)
|
84
|
+
sep, limit = $/, sep if sep.is_a?(Integer)
|
85
|
+
sep_size = sep.bytesize
|
86
|
+
|
87
|
+
@gets_buffer ||= +''
|
88
|
+
|
89
|
+
loop do
|
90
|
+
idx = @gets_buffer.index(sep)
|
91
|
+
return @gets_buffer.slice!(0, idx + sep_size) if idx
|
92
|
+
|
93
|
+
data = readpartial(8192)
|
94
|
+
if data
|
95
|
+
@gets_buffer << data
|
96
|
+
else
|
97
|
+
return nil if @gets_buffer.empty?
|
98
|
+
line, @gets_buffer = @gets_buffer.freeze, +''
|
99
|
+
return line
|
100
|
+
end
|
101
|
+
end
|
102
|
+
# orig_gets(sep, limit, chomp: chomp)
|
103
|
+
end
|
40
104
|
|
41
105
|
# def print(*args)
|
42
106
|
# end
|
@@ -47,8 +111,24 @@ class ::IO
|
|
47
111
|
# def putc(obj)
|
48
112
|
# end
|
49
113
|
|
50
|
-
|
51
|
-
|
114
|
+
alias_method :orig_puts, :puts
|
115
|
+
def puts(*args)
|
116
|
+
if args.empty?
|
117
|
+
write "\n"
|
118
|
+
return
|
119
|
+
end
|
120
|
+
|
121
|
+
s = args.each_with_object(+'') do |a, str|
|
122
|
+
if a.is_a?(Array)
|
123
|
+
a.each { |a2| str << a2.to_s << "\n" }
|
124
|
+
else
|
125
|
+
a = a.to_s
|
126
|
+
str << a
|
127
|
+
str << "\n" unless a =~ /\n$/
|
128
|
+
end
|
129
|
+
end
|
130
|
+
write s
|
131
|
+
end
|
52
132
|
|
53
133
|
# def readbyte
|
54
134
|
# end
|
@@ -61,4 +141,4 @@ class ::IO
|
|
61
141
|
|
62
142
|
# def readlines(sep = $/, limit = nil, chomp: nil)
|
63
143
|
# end
|
64
|
-
end
|
144
|
+
end
|
@@ -35,6 +35,35 @@ class ::Exception
|
|
35
35
|
end
|
36
36
|
end
|
37
37
|
|
38
|
+
class Pulser
|
39
|
+
def initialize(freq)
|
40
|
+
fiber = Fiber.current
|
41
|
+
@timer = EV::Timer.new(freq, freq)
|
42
|
+
@timer.start { fiber.transfer freq }
|
43
|
+
end
|
44
|
+
|
45
|
+
def await
|
46
|
+
suspend
|
47
|
+
rescue Exception => e
|
48
|
+
@timer.stop
|
49
|
+
raise e
|
50
|
+
end
|
51
|
+
|
52
|
+
def stop
|
53
|
+
@timer.stop
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
module ::Process
|
58
|
+
def self.detach(pid)
|
59
|
+
coproc {
|
60
|
+
EV::Child.new(pid).await
|
61
|
+
}
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
require 'open3'
|
66
|
+
|
38
67
|
# Kernel extensions (methods available to all objects)
|
39
68
|
module ::Kernel
|
40
69
|
def after(duration, &block)
|
@@ -64,6 +93,14 @@ module ::Kernel
|
|
64
93
|
CancelScope.new(timeout: duration, mode: :cancel).(&block)
|
65
94
|
end
|
66
95
|
|
96
|
+
def coproc(proc = nil, &block)
|
97
|
+
if proc.is_a?(Coprocess)
|
98
|
+
proc.run
|
99
|
+
else
|
100
|
+
Coprocess.new(&(block || proc)).run
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
67
104
|
def every(freq, &block)
|
68
105
|
EV::Timer.new(freq, freq).start(&block)
|
69
106
|
end
|
@@ -72,25 +109,6 @@ module ::Kernel
|
|
72
109
|
CancelScope.new(timeout: duration).(&block)
|
73
110
|
end
|
74
111
|
|
75
|
-
class Pulser
|
76
|
-
def initialize(freq)
|
77
|
-
fiber = Fiber.current
|
78
|
-
@timer = EV::Timer.new(freq, freq)
|
79
|
-
@timer.start { fiber.transfer freq }
|
80
|
-
end
|
81
|
-
|
82
|
-
def await
|
83
|
-
suspend
|
84
|
-
rescue Exception => e
|
85
|
-
@timer.stop
|
86
|
-
raise e
|
87
|
-
end
|
88
|
-
|
89
|
-
def stop
|
90
|
-
@timer.stop
|
91
|
-
end
|
92
|
-
end
|
93
|
-
|
94
112
|
def pulse(freq)
|
95
113
|
Pulser.new(freq)
|
96
114
|
end
|
@@ -107,18 +125,20 @@ module ::Kernel
|
|
107
125
|
timer.stop
|
108
126
|
end
|
109
127
|
|
110
|
-
def spawn(proc = nil, &block)
|
111
|
-
if proc.is_a?(Coprocess)
|
112
|
-
proc.run
|
113
|
-
else
|
114
|
-
Coprocess.new(&(block || proc)).run
|
115
|
-
end
|
116
|
-
end
|
117
|
-
|
118
128
|
def supervise(&block)
|
119
129
|
Supervisor.new.await(&block)
|
120
130
|
end
|
121
131
|
|
132
|
+
alias_method :orig_system, :system
|
133
|
+
def system(*args)
|
134
|
+
Open3.popen2(*args) do |i, o, t|
|
135
|
+
i.close
|
136
|
+
o.read
|
137
|
+
end
|
138
|
+
rescue SystemCallError => e
|
139
|
+
nil
|
140
|
+
end
|
141
|
+
|
122
142
|
def throttled_loop(rate, &block)
|
123
143
|
throttler = Throttler.new(rate)
|
124
144
|
loop do
|
data/lib/polyphony/postgres.rb
CHANGED
@@ -20,8 +20,6 @@ module ::PG
|
|
20
20
|
return
|
21
21
|
end
|
22
22
|
end
|
23
|
-
ensure
|
24
|
-
conn.socket_io.stop_watchers
|
25
23
|
end
|
26
24
|
|
27
25
|
def self.connect_sync(conn)
|
@@ -46,8 +44,6 @@ class ::PG::Connection
|
|
46
44
|
consume_input
|
47
45
|
end
|
48
46
|
orig_get_result(&block)
|
49
|
-
ensure
|
50
|
-
socket_io.stop_watchers
|
51
47
|
end
|
52
48
|
|
53
49
|
alias_method :orig_async_exec, :async_exec
|
data/lib/polyphony/version.rb
CHANGED
data/test/test_coprocess.rb
CHANGED
@@ -86,7 +86,7 @@ class CoprocessTest < MiniTest::Test
|
|
86
86
|
|
87
87
|
def test_that_coprocess_can_be_awaited
|
88
88
|
result = nil
|
89
|
-
|
89
|
+
coproc do
|
90
90
|
coprocess = Polyphony::Coprocess.new { sleep(0.001); 42 }
|
91
91
|
result = coprocess.await
|
92
92
|
end
|
@@ -96,7 +96,7 @@ class CoprocessTest < MiniTest::Test
|
|
96
96
|
|
97
97
|
def test_that_coprocess_can_be_stopped
|
98
98
|
result = nil
|
99
|
-
coprocess =
|
99
|
+
coprocess = coproc do
|
100
100
|
sleep(0.001)
|
101
101
|
result = 42
|
102
102
|
end
|
@@ -107,7 +107,7 @@ class CoprocessTest < MiniTest::Test
|
|
107
107
|
|
108
108
|
def test_that_coprocess_can_be_cancelled
|
109
109
|
result = nil
|
110
|
-
coprocess =
|
110
|
+
coprocess = coproc do
|
111
111
|
sleep(0.001)
|
112
112
|
result = 42
|
113
113
|
rescue Polyphony::Cancel => e
|
@@ -125,8 +125,8 @@ class CoprocessTest < MiniTest::Test
|
|
125
125
|
def test_that_inner_coprocess_can_be_interrupted
|
126
126
|
result = nil
|
127
127
|
coprocess2 = nil
|
128
|
-
coprocess =
|
129
|
-
coprocess2 =
|
128
|
+
coprocess = coproc do
|
129
|
+
coprocess2 = coproc do
|
130
130
|
sleep(0.001)
|
131
131
|
result = 42
|
132
132
|
end
|
@@ -143,8 +143,8 @@ class CoprocessTest < MiniTest::Test
|
|
143
143
|
def test_that_inner_coprocess_can_interrupt_outer_coprocess
|
144
144
|
result, coprocess2 = nil
|
145
145
|
|
146
|
-
coprocess =
|
147
|
-
coprocess2 =
|
146
|
+
coprocess = coproc do
|
147
|
+
coprocess2 = coproc do
|
148
148
|
EV.next_tick { coprocess.interrupt }
|
149
149
|
sleep(0.001)
|
150
150
|
result = 42
|
@@ -168,7 +168,7 @@ class MailboxTest < MiniTest::Test
|
|
168
168
|
|
169
169
|
def test_that_coprocess_can_receive_messages
|
170
170
|
msgs = []
|
171
|
-
|
171
|
+
coprocess = coproc {
|
172
172
|
loop {
|
173
173
|
msgs << receive
|
174
174
|
}
|
@@ -176,16 +176,16 @@ class MailboxTest < MiniTest::Test
|
|
176
176
|
|
177
177
|
EV.snooze # allow coproc to start
|
178
178
|
|
179
|
-
3.times { |i|
|
179
|
+
3.times { |i| coprocess << i; EV.snooze }
|
180
180
|
|
181
181
|
assert_equal([0, 1, 2], msgs)
|
182
182
|
ensure
|
183
|
-
|
183
|
+
coprocess.stop
|
184
184
|
end
|
185
185
|
|
186
186
|
def test_that_multiple_messages_sent_at_once_arrive
|
187
187
|
msgs = []
|
188
|
-
|
188
|
+
coprocess = coproc {
|
189
189
|
loop {
|
190
190
|
msgs << receive
|
191
191
|
}
|
@@ -193,12 +193,12 @@ class MailboxTest < MiniTest::Test
|
|
193
193
|
|
194
194
|
EV.snooze # allow coproc to start
|
195
195
|
|
196
|
-
3.times { |i|
|
196
|
+
3.times { |i| coprocess << i }
|
197
197
|
|
198
198
|
EV.snooze
|
199
199
|
|
200
200
|
assert_equal([0, 1, 2], msgs)
|
201
201
|
ensure
|
202
|
-
|
202
|
+
coprocess.stop
|
203
203
|
end
|
204
204
|
end
|
data/test/test_core.rb
CHANGED
@@ -9,7 +9,7 @@ class SpawnTest < MiniTest::Test
|
|
9
9
|
|
10
10
|
def test_that_spawn_returns_a_coprocess
|
11
11
|
result = nil
|
12
|
-
coprocess =
|
12
|
+
coprocess = coproc { result = 42 }
|
13
13
|
|
14
14
|
assert_kind_of(Polyphony::Coprocess, coprocess)
|
15
15
|
assert_nil(result)
|
@@ -20,7 +20,7 @@ class SpawnTest < MiniTest::Test
|
|
20
20
|
def test_that_spawn_accepts_coprocess_argument
|
21
21
|
result = nil
|
22
22
|
coprocess = Polyphony::Coprocess.new { result = 42 }
|
23
|
-
|
23
|
+
coproc coprocess
|
24
24
|
|
25
25
|
assert_nil(result)
|
26
26
|
suspend
|
@@ -28,7 +28,7 @@ class SpawnTest < MiniTest::Test
|
|
28
28
|
end
|
29
29
|
|
30
30
|
def test_that_spawned_coprocess_saves_result
|
31
|
-
coprocess =
|
31
|
+
coprocess = coproc { 42 }
|
32
32
|
|
33
33
|
assert_kind_of(Polyphony::Coprocess, coprocess)
|
34
34
|
assert_nil(coprocess.result)
|
@@ -38,7 +38,7 @@ class SpawnTest < MiniTest::Test
|
|
38
38
|
|
39
39
|
def test_that_spawned_coprocess_can_be_interrupted
|
40
40
|
result = nil
|
41
|
-
coprocess =
|
41
|
+
coprocess = coproc { sleep(1); 42 }
|
42
42
|
EV.next_tick { coprocess.interrupt }
|
43
43
|
suspend
|
44
44
|
assert_nil(coprocess.result)
|
@@ -59,7 +59,7 @@ class CancelScopeTest < Minitest::Test
|
|
59
59
|
|
60
60
|
def test_that_cancel_scope_cancels_coprocess
|
61
61
|
ctx = {}
|
62
|
-
|
62
|
+
coproc do
|
63
63
|
EV::Timer.new(0.005, 0).start { ctx[:cancel_scope]&.cancel! }
|
64
64
|
sleep_with_cancel(ctx, :cancel)
|
65
65
|
rescue Exception => e
|
@@ -76,7 +76,7 @@ class CancelScopeTest < Minitest::Test
|
|
76
76
|
|
77
77
|
# def test_that_cancel_scope_cancels_async_op_with_stop
|
78
78
|
# ctx = {}
|
79
|
-
#
|
79
|
+
# coproc do
|
80
80
|
# EV::Timer.new(0, 0).start { ctx[:cancel_scope].cancel! }
|
81
81
|
# sleep_with_cancel(ctx, :stop)
|
82
82
|
# end
|
@@ -88,7 +88,7 @@ class CancelScopeTest < Minitest::Test
|
|
88
88
|
|
89
89
|
def test_that_cancel_after_raises_cancelled_exception
|
90
90
|
result = nil
|
91
|
-
|
91
|
+
coproc do
|
92
92
|
cancel_after(0.01) do
|
93
93
|
sleep(1000)
|
94
94
|
end
|
@@ -103,7 +103,7 @@ class CancelScopeTest < Minitest::Test
|
|
103
103
|
def test_that_cancel_scopes_can_be_nested
|
104
104
|
inner_result = nil
|
105
105
|
outer_result = nil
|
106
|
-
|
106
|
+
coproc do
|
107
107
|
move_on_after(0.01) do
|
108
108
|
move_on_after(0.02) do
|
109
109
|
sleep(1000)
|
@@ -119,7 +119,7 @@ class CancelScopeTest < Minitest::Test
|
|
119
119
|
EV.rerun
|
120
120
|
|
121
121
|
outer_result = nil
|
122
|
-
|
122
|
+
coproc do
|
123
123
|
move_on_after(0.02) do
|
124
124
|
move_on_after(0.01) do
|
125
125
|
sleep(1000)
|
@@ -154,7 +154,7 @@ class SupervisorTest < MiniTest::Test
|
|
154
154
|
|
155
155
|
def test_that_supervisor_waits_for_all_nested_coprocesses_to_complete
|
156
156
|
ctx = {}
|
157
|
-
|
157
|
+
coproc do
|
158
158
|
parallel_sleep(ctx)
|
159
159
|
end
|
160
160
|
suspend
|
@@ -165,10 +165,10 @@ class SupervisorTest < MiniTest::Test
|
|
165
165
|
|
166
166
|
def test_that_supervisor_can_add_coprocesses_after_having_started
|
167
167
|
result = []
|
168
|
-
|
168
|
+
coproc {
|
169
169
|
supervisor = Polyphony::Supervisor.new
|
170
170
|
3.times do |i|
|
171
|
-
|
171
|
+
coproc do
|
172
172
|
sleep(0.001)
|
173
173
|
supervisor.spawn do
|
174
174
|
sleep(0.001)
|