polyphony 0.43.6 → 0.44.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/CHANGELOG.md +45 -0
- data/Gemfile.lock +5 -1
- data/README.md +20 -5
- data/TODO.md +10 -14
- data/bin/stress.rb +28 -0
- data/docs/getting-started/overview.md +2 -2
- data/examples/adapters/sequel_mysql.rb +23 -0
- data/examples/adapters/sequel_mysql_pool.rb +33 -0
- data/examples/core/xx-channels.rb +4 -2
- data/examples/core/xx-using-a-mutex.rb +2 -1
- data/examples/performance/fiber_transfer.rb +47 -0
- data/ext/polyphony/agent.h +41 -0
- data/ext/polyphony/event.c +86 -0
- data/ext/polyphony/fiber.c +0 -5
- data/ext/polyphony/libev_agent.c +201 -128
- data/ext/polyphony/polyphony.c +4 -2
- data/ext/polyphony/polyphony.h +24 -24
- data/ext/polyphony/polyphony_ext.c +4 -2
- data/ext/polyphony/queue.c +208 -0
- data/ext/polyphony/ring_buffer.c +0 -24
- data/ext/polyphony/thread.c +53 -38
- data/lib/polyphony.rb +13 -31
- data/lib/polyphony/adapters/mysql2.rb +19 -0
- data/lib/polyphony/adapters/sequel.rb +45 -0
- data/lib/polyphony/core/channel.rb +3 -34
- data/lib/polyphony/core/exceptions.rb +11 -0
- data/lib/polyphony/core/resource_pool.rb +23 -72
- data/lib/polyphony/core/sync.rb +12 -9
- data/lib/polyphony/extensions/core.rb +15 -8
- data/lib/polyphony/extensions/fiber.rb +4 -0
- data/lib/polyphony/extensions/socket.rb +9 -9
- data/lib/polyphony/extensions/thread.rb +1 -1
- data/lib/polyphony/net.rb +2 -1
- data/lib/polyphony/version.rb +1 -1
- data/polyphony.gemspec +2 -0
- data/test/helper.rb +2 -2
- data/test/test_agent.rb +2 -2
- data/test/test_event.rb +12 -0
- data/test/test_fiber.rb +17 -1
- data/test/test_io.rb +14 -0
- data/test/test_queue.rb +33 -0
- data/test/test_resource_pool.rb +34 -43
- data/test/test_signal.rb +2 -26
- data/test/test_socket.rb +0 -43
- data/test/test_trace.rb +18 -17
- metadata +40 -5
- data/ext/polyphony/libev_queue.c +0 -288
- data/lib/polyphony/event.rb +0 -27
@@ -18,7 +18,7 @@ class ::Thread
|
|
18
18
|
def execute
|
19
19
|
# agent must be created in the context of the new thread, therefore it
|
20
20
|
# cannot be created in Thread#initialize
|
21
|
-
@agent = Polyphony::
|
21
|
+
@agent = Polyphony::Agent.new
|
22
22
|
setup
|
23
23
|
@ready = true
|
24
24
|
result = @block.(*@args)
|
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
@@ -32,6 +32,8 @@ Gem::Specification.new do |s|
|
|
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 'mysql2', '0.5.3'
|
36
|
+
s.add_development_dependency 'sequel', '5.34.0'
|
35
37
|
|
36
38
|
s.add_development_dependency 'jekyll', '~>3.8.6'
|
37
39
|
s.add_development_dependency 'jekyll-remote-theme', '~>0.4.1'
|
data/test/helper.rb
CHANGED
@@ -31,8 +31,8 @@ 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.agent = Polyphony::
|
35
|
-
sleep 0
|
34
|
+
Thread.current.agent = Polyphony::Agent.new
|
35
|
+
sleep 0 # apparently this helps with timer accuracy
|
36
36
|
end
|
37
37
|
|
38
38
|
def teardown
|
data/test/test_agent.rb
CHANGED
@@ -6,7 +6,7 @@ class AgentTest < MiniTest::Test
|
|
6
6
|
def setup
|
7
7
|
super
|
8
8
|
@prev_agent = Thread.current.agent
|
9
|
-
@agent = Polyphony::
|
9
|
+
@agent = Polyphony::Agent.new
|
10
10
|
Thread.current.agent = @agent
|
11
11
|
end
|
12
12
|
|
@@ -97,7 +97,7 @@ class AgentTest < MiniTest::Test
|
|
97
97
|
o.close
|
98
98
|
|
99
99
|
# read_loop will snooze after every read
|
100
|
-
|
100
|
+
6.times { snooze }
|
101
101
|
|
102
102
|
assert_equal [:ready, 'foo', 'bar', :done], buf
|
103
103
|
end
|
data/test/test_event.rb
CHANGED
@@ -34,6 +34,7 @@ class EventTest < MiniTest::Test
|
|
34
34
|
}
|
35
35
|
}
|
36
36
|
snooze
|
37
|
+
|
37
38
|
t = Thread.new do
|
38
39
|
orig_sleep 0.001
|
39
40
|
3.times { a.signal }
|
@@ -45,4 +46,15 @@ class EventTest < MiniTest::Test
|
|
45
46
|
t&.kill
|
46
47
|
t&.join
|
47
48
|
end
|
49
|
+
|
50
|
+
def test_exception_while_waiting_for_event
|
51
|
+
e = Polyphony::Event.new
|
52
|
+
|
53
|
+
f = spin { e.await }
|
54
|
+
g = spin { f.raise 'foo' }
|
55
|
+
|
56
|
+
assert_raises(RuntimeError) do
|
57
|
+
f.await
|
58
|
+
end
|
59
|
+
end
|
48
60
|
end
|
data/test/test_fiber.rb
CHANGED
@@ -413,6 +413,22 @@ class FiberTest < MiniTest::Test
|
|
413
413
|
f2&.stop
|
414
414
|
end
|
415
415
|
|
416
|
+
def test_interject
|
417
|
+
buf = []
|
418
|
+
f = spin_loop { sleep }
|
419
|
+
snooze
|
420
|
+
f.interject { buf << Fiber.current }
|
421
|
+
snooze
|
422
|
+
assert_equal [f], buf
|
423
|
+
assert_equal :waiting, f.state
|
424
|
+
|
425
|
+
f.interject { buf << :foo; raise Polyphony::MoveOn }
|
426
|
+
snooze
|
427
|
+
|
428
|
+
assert_equal [f, :foo], buf
|
429
|
+
assert_equal :dead, f.state
|
430
|
+
end
|
431
|
+
|
416
432
|
def test_state
|
417
433
|
counter = 0
|
418
434
|
f = spin do
|
@@ -692,7 +708,7 @@ class FiberTest < MiniTest::Test
|
|
692
708
|
|
693
709
|
f.schedule
|
694
710
|
f << 'bar'
|
695
|
-
snooze
|
711
|
+
2.times { snooze }
|
696
712
|
assert_equal ['bar'], buffer
|
697
713
|
end
|
698
714
|
end
|
data/test/test_io.rb
CHANGED
@@ -91,6 +91,20 @@ class IOTest < MiniTest::Test
|
|
91
91
|
|
92
92
|
assert_raises(EOFError) { i.readpartial(1) }
|
93
93
|
end
|
94
|
+
|
95
|
+
# see https://github.com/digital-fabric/polyphony/issues/30
|
96
|
+
def test_reopened_tempfile
|
97
|
+
file = Tempfile.new
|
98
|
+
file << 'hello: world'
|
99
|
+
file.close
|
100
|
+
|
101
|
+
buf = nil
|
102
|
+
File.open(file, 'r:bom|utf-8') do |f|
|
103
|
+
buf = f.read(16384)
|
104
|
+
end
|
105
|
+
|
106
|
+
assert_equal 'hello: world', buf
|
107
|
+
end
|
94
108
|
end
|
95
109
|
|
96
110
|
class IOClassMethodsTest < MiniTest::Test
|
data/test/test_queue.rb
CHANGED
@@ -96,4 +96,37 @@ class QueueTest < MiniTest::Test
|
|
96
96
|
assert_nil f2.await
|
97
97
|
assert_equal :bar, f3.await
|
98
98
|
end
|
99
|
+
|
100
|
+
def test_fiber_removal_from_queue_simple
|
101
|
+
f1 = spin { @queue.shift }
|
102
|
+
|
103
|
+
# let fibers run
|
104
|
+
snooze
|
105
|
+
|
106
|
+
f1.stop
|
107
|
+
snooze
|
108
|
+
|
109
|
+
@queue << :foo
|
110
|
+
assert_nil f1.await
|
111
|
+
end
|
112
|
+
|
113
|
+
def test_queue_size
|
114
|
+
assert_equal 0, @queue.size
|
115
|
+
|
116
|
+
@queue.push 1
|
117
|
+
|
118
|
+
assert_equal 1, @queue.size
|
119
|
+
|
120
|
+
@queue.push 2
|
121
|
+
|
122
|
+
assert_equal 2, @queue.size
|
123
|
+
|
124
|
+
@queue.shift
|
125
|
+
|
126
|
+
assert_equal 1, @queue.size
|
127
|
+
|
128
|
+
@queue.shift
|
129
|
+
|
130
|
+
assert_equal 0, @queue.size
|
131
|
+
end
|
99
132
|
end
|
data/test/test_resource_pool.rb
CHANGED
@@ -12,29 +12,19 @@ class ResourcePoolTest < MiniTest::Test
|
|
12
12
|
assert_equal 0, pool.size
|
13
13
|
|
14
14
|
results = []
|
15
|
-
4.times {
|
16
|
-
spin {
|
17
|
-
snooze
|
15
|
+
4.times { |i|
|
16
|
+
spin(:"foo#{i}") {
|
18
17
|
pool.acquire { |resource|
|
19
18
|
results << resource
|
20
19
|
snooze
|
21
20
|
}
|
22
21
|
}
|
23
22
|
}
|
24
|
-
|
25
|
-
assert_equal 2, pool.limit
|
26
|
-
assert_equal 0, pool.available
|
27
|
-
assert_equal 2, pool.size
|
28
|
-
|
29
|
-
2.times { snooze }
|
30
|
-
|
31
|
-
assert_equal ['a', 'b', 'a', 'b'], results
|
32
|
-
|
33
|
-
2.times { snooze }
|
34
|
-
|
23
|
+
Fiber.current.await_all_children
|
35
24
|
assert_equal 2, pool.limit
|
36
25
|
assert_equal 2, pool.available
|
37
26
|
assert_equal 2, pool.size
|
27
|
+
assert_equal ['a', 'b', 'a', 'b'], results
|
38
28
|
end
|
39
29
|
|
40
30
|
def test_discard
|
@@ -46,40 +36,18 @@ class ResourcePoolTest < MiniTest::Test
|
|
46
36
|
spin {
|
47
37
|
snooze
|
48
38
|
pool.acquire { |resource|
|
49
|
-
|
50
|
-
|
39
|
+
results << resource
|
40
|
+
pool.discard! if resource == 'b'
|
51
41
|
snooze
|
52
42
|
}
|
53
43
|
}
|
54
44
|
}
|
55
|
-
|
45
|
+
Fiber.current.await_all_children
|
56
46
|
|
57
47
|
assert_equal ['a', 'b', 'a', 'a'], results
|
58
48
|
assert_equal 1, pool.size
|
59
49
|
end
|
60
50
|
|
61
|
-
def test_add
|
62
|
-
resources = [+'a', +'b']
|
63
|
-
pool = Polyphony::ResourcePool.new(limit: 2) { resources.shift }
|
64
|
-
|
65
|
-
pool << +'c'
|
66
|
-
|
67
|
-
results = []
|
68
|
-
4.times {
|
69
|
-
spin {
|
70
|
-
snooze
|
71
|
-
pool.acquire { |resource|
|
72
|
-
results << resource
|
73
|
-
resource.__discard__ if resource == 'b'
|
74
|
-
snooze
|
75
|
-
}
|
76
|
-
}
|
77
|
-
}
|
78
|
-
6.times { snooze }
|
79
|
-
|
80
|
-
assert_equal ['c', 'a', 'c', 'a'], results
|
81
|
-
end
|
82
|
-
|
83
51
|
def test_single_resource_limit
|
84
52
|
resources = [+'a', +'b']
|
85
53
|
pool = Polyphony::ResourcePool.new(limit: 1) { resources.shift }
|
@@ -94,7 +62,7 @@ class ResourcePoolTest < MiniTest::Test
|
|
94
62
|
}
|
95
63
|
}
|
96
64
|
}
|
97
|
-
|
65
|
+
Fiber.current.await_all_children
|
98
66
|
|
99
67
|
assert_equal ['a'] * 10, results
|
100
68
|
end
|
@@ -128,11 +96,34 @@ class ResourcePoolTest < MiniTest::Test
|
|
128
96
|
resources = [+'a', +'b']
|
129
97
|
pool = Polyphony::ResourcePool.new(limit: 1) { resources.shift }
|
130
98
|
|
99
|
+
results = []
|
131
100
|
pool.acquire do |r|
|
132
|
-
|
133
|
-
|
134
|
-
|
101
|
+
results << r
|
102
|
+
2.times do
|
103
|
+
pool.acquire do |r|
|
104
|
+
results << r
|
105
|
+
end
|
135
106
|
end
|
136
107
|
end
|
108
|
+
assert_equal ['a']*3, results
|
109
|
+
end
|
110
|
+
|
111
|
+
def test_overloaded_resource_pool
|
112
|
+
pool = Polyphony::ResourcePool.new(limit: 1) { 1 }
|
113
|
+
|
114
|
+
buf = []
|
115
|
+
fibers = 2.times.map do |i|
|
116
|
+
spin(:"foo#{i}") do
|
117
|
+
2.times do
|
118
|
+
pool.acquire do |r|
|
119
|
+
buf << r
|
120
|
+
snooze
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
Fiber.current.await_all_children
|
126
|
+
|
127
|
+
assert_equal [1, 1, 1, 1], buf
|
137
128
|
end
|
138
129
|
end
|
data/test/test_signal.rb
CHANGED
@@ -56,35 +56,11 @@ class SignalTrapTest < Minitest::Test
|
|
56
56
|
assert_equal "3 - interrupted\n2 - terminated\n1 - terminated\n", buffer
|
57
57
|
end
|
58
58
|
|
59
|
-
def
|
59
|
+
def test_interrupt_signal_scheduling
|
60
60
|
i, o = IO.pipe
|
61
61
|
pid = Polyphony.fork do
|
62
62
|
i.close
|
63
|
-
|
64
|
-
f2 = spin do
|
65
|
-
# this fiber will try to create a race condition by
|
66
|
-
# - being scheduled before f1 is scheduled with the Interrupt exception
|
67
|
-
# - scheduling f1 without an exception
|
68
|
-
suspend
|
69
|
-
f1.schedule
|
70
|
-
rescue ::Interrupt => e
|
71
|
-
o.puts '2-interrupt'
|
72
|
-
raise e
|
73
|
-
end
|
74
|
-
f1 = spin do
|
75
|
-
# this fiber is the one that will be current when the
|
76
|
-
# signal is trapped
|
77
|
-
sleep 1
|
78
|
-
o << 'boom'
|
79
|
-
rescue ::Interrupt => e
|
80
|
-
o.puts '1-interrupt'
|
81
|
-
raise e
|
82
|
-
end
|
83
|
-
old_trap = trap('INT') do
|
84
|
-
f2.schedule
|
85
|
-
old_trap.()
|
86
|
-
end
|
87
|
-
Fiber.current.await_all_children
|
63
|
+
sleep
|
88
64
|
rescue ::Interrupt => e
|
89
65
|
o.puts '3-interrupt'
|
90
66
|
ensure
|
data/test/test_socket.rb
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative 'helper'
|
4
|
-
require 'localhost/authority'
|
5
4
|
|
6
5
|
class SocketTest < MiniTest::Test
|
7
6
|
def setup
|
@@ -32,46 +31,4 @@ class SocketTest < MiniTest::Test
|
|
32
31
|
server_fiber&.await
|
33
32
|
server&.close
|
34
33
|
end
|
35
|
-
|
36
|
-
def test_openssl
|
37
|
-
authority = Localhost::Authority.fetch
|
38
|
-
context = authority.server_context
|
39
|
-
port = rand(1234..5678)
|
40
|
-
sock = TCPServer.new('127.0.0.1', port)
|
41
|
-
server = OpenSSL::SSL::SSLServer.new(sock, context)
|
42
|
-
|
43
|
-
server_fiber = spin do
|
44
|
-
while (socket = server.accept)
|
45
|
-
puts "accepted: #{socket.inspect}"
|
46
|
-
spin do
|
47
|
-
while (data = socket.gets(8192))
|
48
|
-
socket << data
|
49
|
-
end
|
50
|
-
end
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
snooze
|
55
|
-
p 1
|
56
|
-
sock = TCPSocket.new('127.0.0.1', port)
|
57
|
-
p 2
|
58
|
-
sock.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
|
59
|
-
context = OpenSSL::SSL::SSLContext.new
|
60
|
-
context.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
61
|
-
client = OpenSSL::SSL::SSLSocket.new(sock, context)
|
62
|
-
client.sync_close = true
|
63
|
-
client.hostname = 'localhost'
|
64
|
-
p 3
|
65
|
-
client.connect
|
66
|
-
p 4
|
67
|
-
client.write("GET / HTTP/1.0\r\nHost: realiteq.net\r\n\r\n")#("1234\n")
|
68
|
-
p 5
|
69
|
-
assert_equal "1234\n", client.readpartial(8192)
|
70
|
-
p 6
|
71
|
-
client.close
|
72
|
-
ensure
|
73
|
-
server_fiber&.stop
|
74
|
-
server_fiber&.await
|
75
|
-
server&.close
|
76
|
-
end
|
77
34
|
end
|
data/test/test_trace.rb
CHANGED
@@ -34,7 +34,8 @@ class TraceTest < MiniTest::Test
|
|
34
34
|
|
35
35
|
def test_2_fiber_trace
|
36
36
|
records = []
|
37
|
-
|
37
|
+
thread = Thread.current
|
38
|
+
t = Polyphony::Trace.new(:fiber_all) { |r| records << r if Thread.current == thread && r[:event] =~ /^fiber_/ }
|
38
39
|
t.enable
|
39
40
|
Polyphony.trace(true)
|
40
41
|
|
@@ -42,23 +43,23 @@ class TraceTest < MiniTest::Test
|
|
42
43
|
suspend
|
43
44
|
sleep 0
|
44
45
|
|
45
|
-
events = records.map { |r| [r[:fiber], r[:event]] }
|
46
|
+
events = records.map { |r| [r[:fiber] == f ? :f : :current, r[:event]] }
|
46
47
|
assert_equal [
|
47
|
-
[f, :fiber_create],
|
48
|
-
[f, :fiber_schedule],
|
49
|
-
[
|
50
|
-
[f, :fiber_run],
|
51
|
-
[f, :fiber_switchpoint],
|
52
|
-
[f, :fiber_ev_loop_enter],
|
53
|
-
[f, :fiber_schedule],
|
54
|
-
[f, :fiber_ev_loop_leave],
|
55
|
-
[f, :fiber_run],
|
56
|
-
[f, :fiber_terminate],
|
57
|
-
[
|
58
|
-
[
|
59
|
-
[
|
60
|
-
[
|
61
|
-
[
|
48
|
+
[:f, :fiber_create],
|
49
|
+
[:f, :fiber_schedule],
|
50
|
+
[:current, :fiber_switchpoint],
|
51
|
+
[:f, :fiber_run],
|
52
|
+
[:f, :fiber_switchpoint],
|
53
|
+
[:f, :fiber_ev_loop_enter],
|
54
|
+
[:f, :fiber_schedule],
|
55
|
+
[:f, :fiber_ev_loop_leave],
|
56
|
+
[:f, :fiber_run],
|
57
|
+
[:f, :fiber_terminate],
|
58
|
+
[:current, :fiber_switchpoint],
|
59
|
+
[:current, :fiber_ev_loop_enter],
|
60
|
+
[:current, :fiber_schedule],
|
61
|
+
[:current, :fiber_ev_loop_leave],
|
62
|
+
[:current, :fiber_run]
|
62
63
|
], events
|
63
64
|
ensure
|
64
65
|
t&.disable
|