polyphony 0.77 → 0.80
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 +1 -1
- data/CHANGELOG.md +18 -0
- data/Gemfile.lock +2 -1
- data/examples/core/pingpong.rb +7 -4
- data/examples/core/zlib_stream.rb +15 -0
- data/ext/polyphony/backend_common.c +16 -8
- data/ext/polyphony/backend_common.h +8 -3
- data/ext/polyphony/backend_io_uring.c +19 -3
- data/ext/polyphony/backend_libev.c +33 -17
- data/ext/polyphony/fiber.c +28 -28
- data/ext/polyphony/polyphony.c +1 -8
- data/ext/polyphony/polyphony.h +11 -8
- data/ext/polyphony/queue.c +82 -6
- data/ext/polyphony/thread.c +6 -2
- data/lib/polyphony/adapters/fs.rb +4 -0
- data/lib/polyphony/adapters/process.rb +14 -1
- data/lib/polyphony/adapters/redis.rb +28 -0
- data/lib/polyphony/adapters/sequel.rb +19 -1
- data/lib/polyphony/core/debug.rb +203 -0
- data/lib/polyphony/core/exceptions.rb +21 -6
- data/lib/polyphony/core/global_api.rb +228 -73
- data/lib/polyphony/core/resource_pool.rb +65 -20
- data/lib/polyphony/core/sync.rb +57 -12
- data/lib/polyphony/core/thread_pool.rb +42 -5
- data/lib/polyphony/core/throttler.rb +21 -5
- data/lib/polyphony/core/timer.rb +125 -1
- data/lib/polyphony/extensions/exception.rb +36 -6
- data/lib/polyphony/extensions/fiber.rb +244 -61
- data/lib/polyphony/extensions/io.rb +4 -2
- data/lib/polyphony/extensions/kernel.rb +9 -4
- data/lib/polyphony/extensions/object.rb +8 -0
- data/lib/polyphony/extensions/openssl.rb +3 -1
- data/lib/polyphony/extensions/socket.rb +458 -39
- data/lib/polyphony/extensions/thread.rb +108 -43
- data/lib/polyphony/extensions/timeout.rb +12 -1
- data/lib/polyphony/extensions.rb +1 -0
- data/lib/polyphony/net.rb +59 -0
- data/lib/polyphony/version.rb +1 -1
- data/lib/polyphony.rb +0 -2
- data/test/test_backend.rb +6 -2
- data/test/test_global_api.rb +0 -23
- data/test/test_io.rb +7 -7
- data/test/test_queue.rb +103 -1
- data/test/test_resource_pool.rb +1 -1
- data/test/test_signal.rb +15 -15
- data/test/test_supervise.rb +27 -0
- data/test/test_thread.rb +1 -1
- data/test/test_throttler.rb +0 -6
- data/test/test_trace.rb +189 -24
- metadata +9 -8
- data/lib/polyphony/core/channel.rb +0 -15
@@ -5,8 +5,11 @@ require_relative '../core/exceptions'
|
|
5
5
|
# Thread extensions
|
6
6
|
class ::Thread
|
7
7
|
attr_reader :main_fiber, :result
|
8
|
+
attr_accessor :backend
|
8
9
|
|
9
10
|
alias_method :orig_initialize, :initialize
|
11
|
+
|
12
|
+
# Initializes the thread.
|
10
13
|
def initialize(*args, &block)
|
11
14
|
@join_wait_queue = []
|
12
15
|
@finalization_mutex = Mutex.new
|
@@ -15,53 +18,30 @@ class ::Thread
|
|
15
18
|
orig_initialize { execute }
|
16
19
|
end
|
17
20
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
raise_error = false
|
22
|
-
begin
|
23
|
-
@backend = Polyphony::Backend.new
|
24
|
-
rescue Exception => e
|
25
|
-
raise_error = true
|
26
|
-
raise e
|
27
|
-
end
|
28
|
-
setup
|
29
|
-
@ready = true
|
30
|
-
result = @block.(*@args)
|
31
|
-
rescue Polyphony::MoveOn, Polyphony::Terminate => e
|
32
|
-
result = e.value
|
33
|
-
rescue Exception => e
|
34
|
-
raise_error ? (raise e) : (result = e)
|
35
|
-
ensure
|
36
|
-
@ready = true
|
37
|
-
finalize(result)
|
38
|
-
end
|
39
|
-
|
40
|
-
attr_accessor :backend
|
41
|
-
|
21
|
+
# Sets up the thread and its main fiber.
|
22
|
+
#
|
23
|
+
# @return [void]
|
42
24
|
def setup
|
43
25
|
@main_fiber = Fiber.current
|
44
26
|
@main_fiber.setup_main_fiber
|
45
27
|
setup_fiber_scheduling
|
46
28
|
end
|
47
29
|
|
48
|
-
def finalize(result)
|
49
|
-
unless Fiber.current.children.empty?
|
50
|
-
Fiber.current.shutdown_all_children
|
51
|
-
end
|
52
|
-
@finalization_mutex.synchronize do
|
53
|
-
@terminated = true
|
54
|
-
@result = result
|
55
|
-
signal_waiters(result)
|
56
|
-
end
|
57
|
-
@backend&.finalize
|
58
|
-
end
|
59
|
-
|
60
|
-
def signal_waiters(result)
|
61
|
-
@join_wait_queue.each { |w| w.signal(result) }
|
62
|
-
end
|
63
|
-
|
64
30
|
alias_method :orig_join, :join
|
31
|
+
|
32
|
+
# call-seq:
|
33
|
+
# thread.join -> result
|
34
|
+
# thread.join(timeout) -> result
|
35
|
+
# thread.await -> result
|
36
|
+
# thread.await(timeout) -> result
|
37
|
+
#
|
38
|
+
# Waits for the thread to terminate and returns its return value. If the
|
39
|
+
# thread terminated with an uncaught exception, it is propagated to the
|
40
|
+
# waiting fiber. If a timeout interval is specified, the thread will be
|
41
|
+
# terminated without propagating the timeout exception.
|
42
|
+
#
|
43
|
+
# @param timeout [Number] timeout interval
|
44
|
+
# @return [any] thread's return value
|
65
45
|
def join(timeout = nil)
|
66
46
|
watcher = Fiber.current.auto_watcher
|
67
47
|
|
@@ -77,6 +57,16 @@ class ::Thread
|
|
77
57
|
alias_method :await, :join
|
78
58
|
|
79
59
|
alias_method :orig_raise, :raise
|
60
|
+
|
61
|
+
# call-seq:
|
62
|
+
# thread.raise
|
63
|
+
# thread.raise(exception_class)
|
64
|
+
# thread.raise(exception_instance)
|
65
|
+
#
|
66
|
+
# Raises an exception in the context of the thread. If no exception is given,
|
67
|
+
# a `RuntimeError` is raised.
|
68
|
+
#
|
69
|
+
# @param error [Exception, Class, nil] exception spec
|
80
70
|
def raise(error = nil)
|
81
71
|
Thread.pass until @main_fiber
|
82
72
|
error = RuntimeError.new if error.nil?
|
@@ -88,13 +78,22 @@ class ::Thread
|
|
88
78
|
end
|
89
79
|
|
90
80
|
alias_method :orig_kill, :kill
|
81
|
+
|
82
|
+
# Terminates the thread.
|
83
|
+
#
|
84
|
+
# @return [Thread] self
|
91
85
|
def kill
|
92
|
-
return if @terminated
|
86
|
+
return self if @terminated
|
93
87
|
|
94
88
|
raise Polyphony::Terminate
|
89
|
+
self
|
95
90
|
end
|
96
91
|
|
97
92
|
alias_method :orig_inspect, :inspect
|
93
|
+
|
94
|
+
# Returns a string representation of the thread for debugging purposes.
|
95
|
+
#
|
96
|
+
# @return [String] string representation
|
98
97
|
def inspect
|
99
98
|
return orig_inspect if self == Thread.main
|
100
99
|
|
@@ -103,20 +102,86 @@ class ::Thread
|
|
103
102
|
end
|
104
103
|
alias_method :to_s, :inspect
|
105
104
|
|
105
|
+
# Returns the source location of the thread's block.
|
106
|
+
#
|
107
|
+
# @return [String] source location
|
106
108
|
def location
|
107
109
|
@block.source_location.join(':')
|
108
110
|
end
|
109
111
|
|
110
|
-
|
111
|
-
|
112
|
+
# Sends a message to the thread's main fiber.
|
113
|
+
#
|
114
|
+
# @param msg [any] message
|
115
|
+
# @return [Fiber] main fiber
|
116
|
+
def <<(msg)
|
117
|
+
main_fiber << msg
|
112
118
|
end
|
113
119
|
alias_method :send, :<<
|
114
120
|
|
121
|
+
# Sets the idle GC period for the thread's backend.
|
122
|
+
#
|
123
|
+
# @param period [Number] GC period in seconds
|
124
|
+
# @return [Number] GC period
|
115
125
|
def idle_gc_period=(period)
|
116
126
|
backend.idle_gc_period = period
|
117
127
|
end
|
118
128
|
|
129
|
+
# Sets the idle handler for the thread's backend.
|
130
|
+
#
|
131
|
+
# @param &block [Proc] idle handler
|
132
|
+
# @return [Proc] idle handler
|
119
133
|
def on_idle(&block)
|
120
134
|
backend.idle_proc = block
|
121
135
|
end
|
136
|
+
|
137
|
+
private
|
138
|
+
|
139
|
+
# Runs the thread's block, handling any uncaught exceptions.
|
140
|
+
#
|
141
|
+
# @return [void]
|
142
|
+
def execute
|
143
|
+
# backend must be created in the context of the new thread, therefore it
|
144
|
+
# cannot be created in Thread#initialize
|
145
|
+
raise_error = false
|
146
|
+
begin
|
147
|
+
@backend = Polyphony::Backend.new
|
148
|
+
rescue Exception => e
|
149
|
+
raise_error = true
|
150
|
+
raise e
|
151
|
+
end
|
152
|
+
setup
|
153
|
+
@ready = true
|
154
|
+
result = @block.(*@args)
|
155
|
+
rescue Polyphony::MoveOn, Polyphony::Terminate => e
|
156
|
+
result = e.value
|
157
|
+
rescue Exception => e
|
158
|
+
raise_error ? (raise e) : (result = e)
|
159
|
+
ensure
|
160
|
+
@ready = true
|
161
|
+
finalize(result)
|
162
|
+
end
|
163
|
+
|
164
|
+
# Finalizes the thread.
|
165
|
+
#
|
166
|
+
# @param result [any] thread's return value
|
167
|
+
# @return [void]
|
168
|
+
def finalize(result)
|
169
|
+
unless Fiber.current.children.empty?
|
170
|
+
Fiber.current.shutdown_all_children
|
171
|
+
end
|
172
|
+
@finalization_mutex.synchronize do
|
173
|
+
@terminated = true
|
174
|
+
@result = result
|
175
|
+
signal_waiters(result)
|
176
|
+
end
|
177
|
+
@backend&.finalize
|
178
|
+
end
|
179
|
+
|
180
|
+
# Signals all fibers waiting for the thread to terminate.
|
181
|
+
#
|
182
|
+
# @param result [any] thread's return value
|
183
|
+
# @return [void]
|
184
|
+
def signal_waiters(result)
|
185
|
+
@join_wait_queue.each { |w| w.signal(result) }
|
186
|
+
end
|
122
187
|
end
|
@@ -2,8 +2,19 @@
|
|
2
2
|
|
3
3
|
require 'timeout'
|
4
4
|
|
5
|
-
#
|
5
|
+
# Timeout extensions
|
6
6
|
module ::Timeout
|
7
|
+
|
8
|
+
# Sets a timeout for the given block. This method provides an equivalent API
|
9
|
+
# to the stock Timeout API provided by Ruby. In case of a timeout, the block
|
10
|
+
# will be interrupted and an exception will be raised according to the given
|
11
|
+
# arguments.
|
12
|
+
#
|
13
|
+
# @param sec [Number] timeout period in seconds
|
14
|
+
# @param klass [Class] exception class
|
15
|
+
# @param message [String] exception message
|
16
|
+
# @param &block [Proc] code to run
|
17
|
+
# @return [any] block's return value
|
7
18
|
def self.timeout(sec, klass = Timeout::Error, message = 'execution expired', &block)
|
8
19
|
cancel_after(sec, with_exception: [klass, message], &block)
|
9
20
|
end
|
data/lib/polyphony/extensions.rb
CHANGED
@@ -3,6 +3,7 @@
|
|
3
3
|
require_relative './extensions/exception'
|
4
4
|
require_relative './extensions/fiber'
|
5
5
|
require_relative './extensions/io'
|
6
|
+
require_relative './extensions/object'
|
6
7
|
require_relative './extensions/kernel'
|
7
8
|
require_relative './extensions/process'
|
8
9
|
require_relative './extensions/thread'
|
data/lib/polyphony/net.rb
CHANGED
@@ -4,9 +4,25 @@ require_relative './extensions/socket'
|
|
4
4
|
require_relative './extensions/openssl'
|
5
5
|
|
6
6
|
module Polyphony
|
7
|
+
|
7
8
|
# A more elegant networking API
|
8
9
|
module Net
|
9
10
|
class << self
|
11
|
+
|
12
|
+
# call-seq:
|
13
|
+
# Polyphony::Net.tcp_connect(host, port) -> TCPSocket
|
14
|
+
# Polyphony::Net.tcp_connect(host, port, secure: true) -> SSLSocket
|
15
|
+
# Polyphony::Net.tcp_connect(host, port, secure_context: ctx) -> SSLSocket
|
16
|
+
#
|
17
|
+
# Create a TCP connection to the given host and port, returning the new
|
18
|
+
# socket. If `opts[:secure]` is true, or if an SSL context is given in
|
19
|
+
# `opts[:secure_context]`, a TLS handshake is performed, and an SSLSocket
|
20
|
+
# is returned.
|
21
|
+
#
|
22
|
+
# @param host [String] hostname
|
23
|
+
# @param port [Integer] port number
|
24
|
+
# @param opts [Hash] connection options
|
25
|
+
# @return [TCPSocket, SSLSocket] connected socket
|
10
26
|
def tcp_connect(host, port, opts = {})
|
11
27
|
socket = TCPSocket.new(host, port)
|
12
28
|
if opts[:secure_context] || opts[:secure]
|
@@ -16,6 +32,15 @@ module Polyphony
|
|
16
32
|
end
|
17
33
|
end
|
18
34
|
|
35
|
+
# Creates a server socket for accepting incoming connection on the given
|
36
|
+
# host and port. If `opts[:secure]` is true, or if an SSL context is given
|
37
|
+
# in `opts[:secure_context]`, a TLS handshake is performed, and an
|
38
|
+
# SSLSocket is returned.
|
39
|
+
#
|
40
|
+
# @param host [String] hostname
|
41
|
+
# @param port [Integer] port number
|
42
|
+
# @param opts [Hash] connection options
|
43
|
+
# @return [TCPServer, SSLServer] listening socket
|
19
44
|
def tcp_listen(host = nil, port = nil, opts = {})
|
20
45
|
host ||= '0.0.0.0'
|
21
46
|
raise 'Port number not specified' unless port
|
@@ -28,6 +53,14 @@ module Polyphony
|
|
28
53
|
end
|
29
54
|
end
|
30
55
|
|
56
|
+
private
|
57
|
+
|
58
|
+
# Creates a listening `Socket` instance.
|
59
|
+
#
|
60
|
+
# @param host [String] hostname
|
61
|
+
# @param port [Integer] port number
|
62
|
+
# @param opts [Hash] connection options
|
63
|
+
# @return [Socket] listening socket
|
31
64
|
def listening_socket_from_options(host, port, opts)
|
32
65
|
::Socket.new(:INET, :STREAM).tap do |s|
|
33
66
|
s.reuse_addr if opts[:reuse_addr]
|
@@ -39,6 +72,12 @@ module Polyphony
|
|
39
72
|
end
|
40
73
|
end
|
41
74
|
|
75
|
+
# Wraps the given socket with a SSLSocket and performs a TLS handshake.
|
76
|
+
#
|
77
|
+
# @param socket [Socket] plain socket
|
78
|
+
# @param context [SSLContext, nil] SSL context
|
79
|
+
# @param opts [Hash] connection options
|
80
|
+
# @return [SSLSocket] SSL socket
|
42
81
|
def secure_socket(socket, context, opts)
|
43
82
|
context ||= OpenSSL::SSL::SSLContext.new
|
44
83
|
setup_alpn(context, opts[:alpn_protocols]) if opts[:alpn_protocols]
|
@@ -51,6 +90,11 @@ module Polyphony
|
|
51
90
|
end
|
52
91
|
end
|
53
92
|
|
93
|
+
# Wraps the given socket with an SSLSocket.
|
94
|
+
#
|
95
|
+
# @param socket [Socket] plain socket
|
96
|
+
# @param context [SSLContext] SSL context
|
97
|
+
# @return [SSLSocket] SSL socket
|
54
98
|
def secure_socket_wrapper(socket, context)
|
55
99
|
if context
|
56
100
|
OpenSSL::SSL::SSLSocket.new(socket, context)
|
@@ -59,11 +103,26 @@ module Polyphony
|
|
59
103
|
end
|
60
104
|
end
|
61
105
|
|
106
|
+
# Wraps the given socket with an SSLServer, setting up ALPN from the given
|
107
|
+
# options.
|
108
|
+
#
|
109
|
+
# @param socket [Socket] plain socket
|
110
|
+
# @param context [SSLContext] SSL context
|
111
|
+
# @param opts [Hash] options
|
112
|
+
# @return [SSLServer] SSL socket
|
62
113
|
def secure_server(socket, context, opts)
|
63
114
|
setup_alpn(context, opts[:alpn_protocols]) if opts[:alpn_protocols]
|
64
115
|
OpenSSL::SSL::SSLServer.new(socket, context)
|
65
116
|
end
|
66
117
|
|
118
|
+
# Sets up ALPN negotiation for the given context. The ALPN handler for the
|
119
|
+
# context will select the first protocol from the list given by the client
|
120
|
+
# that appears in the list of given protocols, according to the specified
|
121
|
+
# order.
|
122
|
+
#
|
123
|
+
# @param context [SSLContext] SSL context
|
124
|
+
# @param protocols [Array] array of supported protocols
|
125
|
+
# @return [void]
|
67
126
|
def setup_alpn(context, protocols)
|
68
127
|
context.alpn_protocols = protocols
|
69
128
|
context.alpn_select_cb = lambda do |peer_protocols|
|
data/lib/polyphony/version.rb
CHANGED
data/lib/polyphony.rb
CHANGED
@@ -9,7 +9,6 @@ Thread.current.backend = Polyphony::Backend.new
|
|
9
9
|
|
10
10
|
require_relative './polyphony/extensions'
|
11
11
|
require_relative './polyphony/core/exceptions'
|
12
|
-
require_relative './polyphony/core/global_api'
|
13
12
|
require_relative './polyphony/core/resource_pool'
|
14
13
|
require_relative './polyphony/core/sync'
|
15
14
|
require_relative './polyphony/core/timer'
|
@@ -53,7 +52,6 @@ module Polyphony
|
|
53
52
|
|
54
53
|
def run_forked_block(&block)
|
55
54
|
Thread.current.setup
|
56
|
-
Fiber.current.setup_main_fiber
|
57
55
|
Thread.current.backend.post_fork
|
58
56
|
|
59
57
|
install_terminating_signal_handlers
|
data/test/test_backend.rb
CHANGED
@@ -191,7 +191,9 @@ class BackendTest < MiniTest::Test
|
|
191
191
|
Net = Polyphony::Net
|
192
192
|
|
193
193
|
def test_accept
|
194
|
-
server = Net.
|
194
|
+
server = Net.send(
|
195
|
+
:listening_socket_from_options, '127.0.0.1', 1234, reuse_addr: true
|
196
|
+
)
|
195
197
|
|
196
198
|
clients = []
|
197
199
|
server_fiber = spin_loop do
|
@@ -218,7 +220,9 @@ class BackendTest < MiniTest::Test
|
|
218
220
|
end
|
219
221
|
|
220
222
|
def test_accept_loop
|
221
|
-
server = Net.
|
223
|
+
server = Net.send(
|
224
|
+
:listening_socket_from_options, '127.0.0.1', 1235, reuse_addr: true
|
225
|
+
)
|
222
226
|
|
223
227
|
clients = []
|
224
228
|
server_fiber = spin do
|
data/test/test_global_api.rb
CHANGED
@@ -140,17 +140,6 @@ class MoveOnAfterTest < MiniTest::Test
|
|
140
140
|
assert_in_range 0.014..0.02, t1 - t0 if IS_LINUX
|
141
141
|
end
|
142
142
|
|
143
|
-
def test_move_on_after_without_block
|
144
|
-
t0 = Time.now
|
145
|
-
f = move_on_after(0.01, with_value: 'foo')
|
146
|
-
assert_kind_of Fiber, f
|
147
|
-
assert_equal Fiber.current, f.parent
|
148
|
-
v = sleep 1
|
149
|
-
t1 = Time.now
|
150
|
-
assert t1 - t0 < 0.1
|
151
|
-
assert_equal 'foo', v
|
152
|
-
end
|
153
|
-
|
154
143
|
def test_nested_move_on_after
|
155
144
|
skip unless IS_LINUX
|
156
145
|
|
@@ -190,18 +179,6 @@ class CancelAfterTest < MiniTest::Test
|
|
190
179
|
assert t1 - t0 < 0.1
|
191
180
|
end
|
192
181
|
|
193
|
-
def test_cancel_after_without_block
|
194
|
-
t0 = Time.now
|
195
|
-
f = cancel_after(0.01)
|
196
|
-
assert_kind_of Fiber, f
|
197
|
-
assert_equal Fiber.current, f.parent
|
198
|
-
assert_raises Polyphony::Cancel do
|
199
|
-
sleep 1
|
200
|
-
end
|
201
|
-
t1 = Time.now
|
202
|
-
assert t1 - t0 < 0.1
|
203
|
-
end
|
204
|
-
|
205
182
|
def test_cancel_after_with_reset
|
206
183
|
t0 = Time.now
|
207
184
|
cancel_after(0.01) do |f|
|
data/test/test_io.rb
CHANGED
@@ -309,13 +309,13 @@ class IOClassMethodsTest < MiniTest::Test
|
|
309
309
|
assert_equal BIN_DATA, s
|
310
310
|
end
|
311
311
|
|
312
|
-
def test_foreach
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
end
|
312
|
+
# def test_foreach
|
313
|
+
# skip 'IO.foreach is not yet implemented'
|
314
|
+
# lines = []
|
315
|
+
# IO.foreach(__FILE__) { |l| lines << l }
|
316
|
+
# assert_equal "# frozen_string_literal: true\n", lines[0]
|
317
|
+
# assert_equal "end\n", lines[-1]
|
318
|
+
# end
|
319
319
|
|
320
320
|
def test_read_class_method
|
321
321
|
s = IO.read(__FILE__)
|
data/test/test_queue.rb
CHANGED
@@ -21,6 +21,44 @@ class QueueTest < MiniTest::Test
|
|
21
21
|
assert_equal [1, 2, 3, 4], buf
|
22
22
|
end
|
23
23
|
|
24
|
+
def test_chained_push
|
25
|
+
@queue << 5 << 6 << 7
|
26
|
+
|
27
|
+
buf = []
|
28
|
+
3.times { buf << @queue.shift }
|
29
|
+
assert_equal [5, 6, 7], buf
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_push_aliases
|
33
|
+
@queue.push 1
|
34
|
+
@queue << 2
|
35
|
+
@queue.enq 3
|
36
|
+
|
37
|
+
buf = []
|
38
|
+
3.times { buf << @queue.shift }
|
39
|
+
assert_equal [1, 2, 3], buf
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_pop_aliases
|
43
|
+
@queue << 1 << 2 << 3
|
44
|
+
|
45
|
+
assert_equal 1, @queue.pop
|
46
|
+
assert_equal 2, @queue.deq
|
47
|
+
assert_equal 3, @queue.shift
|
48
|
+
|
49
|
+
@queue << 1 << 2 << 3
|
50
|
+
|
51
|
+
assert_equal 1, @queue.pop(false)
|
52
|
+
assert_equal 2, @queue.deq(false)
|
53
|
+
assert_equal 3, @queue.shift(false)
|
54
|
+
end
|
55
|
+
|
56
|
+
def test_nonblocking_pop
|
57
|
+
assert_raises(ThreadError) { @queue.pop(true) }
|
58
|
+
assert_raises(ThreadError) { @queue.deq(true) }
|
59
|
+
assert_raises(ThreadError) { @queue.shift(true) }
|
60
|
+
end
|
61
|
+
|
24
62
|
def test_unshift
|
25
63
|
@queue.push 1
|
26
64
|
@queue.push 2
|
@@ -112,22 +150,86 @@ class QueueTest < MiniTest::Test
|
|
112
150
|
|
113
151
|
def test_queue_size
|
114
152
|
assert_equal 0, @queue.size
|
153
|
+
assert_equal 0, @queue.length
|
115
154
|
|
116
155
|
@queue.push 1
|
117
156
|
|
118
157
|
assert_equal 1, @queue.size
|
158
|
+
assert_equal 1, @queue.length
|
119
159
|
|
120
160
|
@queue.push 2
|
121
161
|
|
122
162
|
assert_equal 2, @queue.size
|
163
|
+
assert_equal 2, @queue.length
|
123
164
|
|
124
165
|
@queue.shift
|
125
166
|
|
126
167
|
assert_equal 1, @queue.size
|
168
|
+
assert_equal 1, @queue.length
|
127
169
|
|
128
170
|
@queue.shift
|
129
171
|
|
130
172
|
assert_equal 0, @queue.size
|
173
|
+
assert_equal 0, @queue.length
|
174
|
+
end
|
175
|
+
|
176
|
+
def test_pending?
|
177
|
+
assert_equal false, @queue.pending?
|
178
|
+
|
179
|
+
buf = []
|
180
|
+
f = spin { buf << @queue.shift }
|
181
|
+
snooze
|
182
|
+
assert_equal true, @queue.pending?
|
183
|
+
|
184
|
+
@queue << 42
|
185
|
+
f.await
|
186
|
+
assert_equal [42], buf
|
187
|
+
assert_equal false, @queue.pending?
|
188
|
+
end
|
189
|
+
|
190
|
+
def test_num_waiting
|
191
|
+
assert_equal 0, @queue.num_waiting
|
192
|
+
|
193
|
+
f1 = spin { @queue.shift }
|
194
|
+
snooze # allow fiber to start
|
195
|
+
assert_equal 1, @queue.num_waiting
|
196
|
+
|
197
|
+
f2 = spin { @queue.shift }
|
198
|
+
snooze # allow fiber to start
|
199
|
+
assert_equal 2, @queue.num_waiting
|
200
|
+
|
201
|
+
@queue << 1
|
202
|
+
f1.await
|
203
|
+
assert_equal 1, @queue.num_waiting
|
204
|
+
|
205
|
+
@queue << 2
|
206
|
+
f2.await
|
207
|
+
assert_equal 0, @queue.num_waiting
|
208
|
+
end
|
209
|
+
|
210
|
+
def test_closed_queue
|
211
|
+
assert_equal false, @queue.closed?
|
212
|
+
|
213
|
+
buf = []
|
214
|
+
f = spin { buf << @queue.shift }
|
215
|
+
snooze # allow fiber to start
|
216
|
+
|
217
|
+
@queue.close
|
218
|
+
assert_equal true, @queue.closed?
|
219
|
+
cancel_after(1) { f.await }
|
220
|
+
assert_equal [nil], buf
|
221
|
+
|
222
|
+
assert_raises(ClosedQueueError) { @queue << 1 }
|
223
|
+
assert_raises(ClosedQueueError) { @queue.deq }
|
224
|
+
assert_raises(ThreadError) { @queue.pop(true) }
|
225
|
+
|
226
|
+
# test deq on closed non-empty queue
|
227
|
+
@queue = Polyphony::Queue.new
|
228
|
+
@queue << 42 << 43
|
229
|
+
@queue.close
|
230
|
+
|
231
|
+
assert_equal 42, @queue.deq(false)
|
232
|
+
assert_equal 43, @queue.deq(true)
|
131
233
|
end
|
132
234
|
end
|
133
235
|
|
@@ -246,4 +348,4 @@ class CappedQueueTest < MiniTest::Test
|
|
246
348
|
a.join
|
247
349
|
assert_equal [1, 2, 3, :d5, 4, :d8, 5], buffer
|
248
350
|
end
|
249
|
-
end
|
351
|
+
end
|
data/test/test_resource_pool.rb
CHANGED
data/test/test_signal.rb
CHANGED
@@ -33,24 +33,24 @@ class SignalTrapTest < Minitest::Test
|
|
33
33
|
Fiber.current.tag = :main
|
34
34
|
|
35
35
|
expected = [
|
36
|
-
[:
|
37
|
-
[:
|
38
|
-
[:
|
39
|
-
[:
|
40
|
-
[:
|
41
|
-
[:
|
42
|
-
[:
|
43
|
-
[:
|
44
|
-
[:
|
45
|
-
[:
|
46
|
-
[:
|
47
|
-
[:
|
36
|
+
[:block, :main],
|
37
|
+
[:enter_poll, :main],
|
38
|
+
[:spin, :oob],
|
39
|
+
[:schedule, :oob],
|
40
|
+
[:leave_poll, :main],
|
41
|
+
[:unblock, :oob],
|
42
|
+
[:terminate, :oob],
|
43
|
+
[:block, :oob],
|
44
|
+
[:enter_poll, :oob],
|
45
|
+
[:schedule, :main],
|
46
|
+
[:leave_poll, :oob],
|
47
|
+
[:unblock, :main]
|
48
48
|
]
|
49
49
|
if Thread.backend.kind == :libev
|
50
50
|
expected += [
|
51
|
-
[:
|
52
|
-
[:
|
53
|
-
[:
|
51
|
+
[:schedule, :main],
|
52
|
+
[:block, :main],
|
53
|
+
[:unblock, :main]
|
54
54
|
]
|
55
55
|
end
|
56
56
|
|
data/test/test_supervise.rb
CHANGED
@@ -269,4 +269,31 @@ class SuperviseTest < MiniTest::Test
|
|
269
269
|
snooze
|
270
270
|
assert_equal [[f1, :foo], [f2, :bar]], buffer
|
271
271
|
end
|
272
|
+
|
273
|
+
def test_detached_supervisor
|
274
|
+
buffer = []
|
275
|
+
|
276
|
+
s = nil
|
277
|
+
f = spin {
|
278
|
+
foo = spin do
|
279
|
+
sleep 0.1
|
280
|
+
ensure
|
281
|
+
buffer << :foo
|
282
|
+
end
|
283
|
+
bar = spin do
|
284
|
+
sleep 0.2
|
285
|
+
ensure
|
286
|
+
buffer << :bar
|
287
|
+
end
|
288
|
+
|
289
|
+
s = spin { supervise }.detach
|
290
|
+
Fiber.current.attach_all_children_to(s)
|
291
|
+
|
292
|
+
s.terminate(true)
|
293
|
+
}
|
294
|
+
|
295
|
+
f.await
|
296
|
+
s.await
|
297
|
+
assert_equal [:foo, :bar], buffer
|
298
|
+
end
|
272
299
|
end
|
data/test/test_thread.rb
CHANGED
@@ -132,7 +132,7 @@ class ThreadTest < MiniTest::Test
|
|
132
132
|
Thread.backend.trace_proc = proc {|*r| records << r }
|
133
133
|
suspend
|
134
134
|
assert_equal [
|
135
|
-
[:
|
135
|
+
[:block, Fiber.current, ["#{__FILE__}:#{__LINE__ - 2}:in `test_that_suspend_returns_immediately_if_no_watchers'"] + caller]
|
136
136
|
], records
|
137
137
|
ensure
|
138
138
|
Thread.backend.trace_proc = nil
|