async-io 1.25.0 → 1.26.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/examples/echo/client.rb +22 -0
- data/examples/echo/server.rb +57 -0
- data/lib/async/io/stream.rb +33 -5
- data/lib/async/io/trap.rb +23 -6
- data/lib/async/io/version.rb +1 -1
- data/spec/async/io/generic_spec.rb +1 -1
- data/spec/async/io/protocol/line_spec.rb +15 -9
- data/spec/async/io/trap_spec.rb +1 -1
- metadata +5 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8c73625346e61dc5ca64794c5fe70343437cf18e2f019a1fb7bba89d76eaaba6
|
4
|
+
data.tar.gz: 3fb2fbe3c6b50642b8ad3650b7829119c7f5b41ee8837569571e01a30364a4cf
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: aca0fe4641f3066d1eb564b788a31ce5644a318281725342c8ee98f581d8af110c0c9b302e4d9b6e5816802234213d62da53dc34f0fc2715c173e9de79e03ad0
|
7
|
+
data.tar.gz: f318d5debbba3f6cad57d948cc3a16fb716c4bb79f5a84ec0cca5eee3edaadad983103c8188e3b6d83bb4db14393941c1750ba7955903efd8a7f79321e51d6e2
|
@@ -0,0 +1,22 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$LOAD_PATH.unshift File.expand_path("../../lib", __dir__)
|
4
|
+
|
5
|
+
require 'async'
|
6
|
+
require 'async/io/trap'
|
7
|
+
require 'async/io/host_endpoint'
|
8
|
+
require 'async/io/stream'
|
9
|
+
|
10
|
+
endpoint = Async::IO::Endpoint.tcp('localhost', 4578)
|
11
|
+
|
12
|
+
Async do |task|
|
13
|
+
endpoint.connect do |peer|
|
14
|
+
stream = Async::IO::Stream.new(peer)
|
15
|
+
|
16
|
+
while true
|
17
|
+
task.sleep 1
|
18
|
+
stream.puts "Hello World!"
|
19
|
+
puts stream.gets.inspect
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$LOAD_PATH.unshift File.expand_path("../../lib", __dir__)
|
4
|
+
|
5
|
+
require 'async'
|
6
|
+
require 'async/io/trap'
|
7
|
+
require 'async/io/host_endpoint'
|
8
|
+
require 'async/io/stream'
|
9
|
+
|
10
|
+
endpoint = Async::IO::Endpoint.tcp('localhost', 4578)
|
11
|
+
|
12
|
+
interrupt = Async::IO::Trap.new(:INT)
|
13
|
+
|
14
|
+
Async do |top|
|
15
|
+
interrupt.install!
|
16
|
+
|
17
|
+
endpoint.bind do |server, task|
|
18
|
+
Async.logger.info(server) {"Accepting connections on #{server.local_address.inspect}"}
|
19
|
+
|
20
|
+
task.async do |subtask|
|
21
|
+
interrupt.wait
|
22
|
+
|
23
|
+
Async.logger.info(server) {"Closing server socket..."}
|
24
|
+
server.close
|
25
|
+
|
26
|
+
interrupt.default!
|
27
|
+
|
28
|
+
Async.logger.info(server) {"Waiting for connections to close..."}
|
29
|
+
subtask.sleep(4)
|
30
|
+
|
31
|
+
Async.logger.info(server) do |buffer|
|
32
|
+
buffer.puts "Stopping all tasks..."
|
33
|
+
task.print_hierarchy(buffer)
|
34
|
+
buffer.puts "", "Reactor Hierarchy"
|
35
|
+
task.reactor.print_hierarchy(buffer)
|
36
|
+
end
|
37
|
+
|
38
|
+
task.stop
|
39
|
+
end
|
40
|
+
|
41
|
+
server.listen(128)
|
42
|
+
|
43
|
+
server.accept_each do |peer|
|
44
|
+
stream = Async::IO::Stream.new(peer)
|
45
|
+
|
46
|
+
while chunk = stream.read_partial
|
47
|
+
Async.logger.debug(self) {chunk.inspect}
|
48
|
+
stream.write(chunk)
|
49
|
+
stream.flush
|
50
|
+
|
51
|
+
Async.logger.info(server) do |buffer|
|
52
|
+
task.reactor.print_hierarchy(buffer)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
data/lib/async/io/stream.rb
CHANGED
@@ -38,10 +38,13 @@ module Async
|
|
38
38
|
end
|
39
39
|
end
|
40
40
|
|
41
|
-
def initialize(io, block_size: BLOCK_SIZE, maximum_read_size: MAXIMUM_READ_SIZE, sync: true)
|
41
|
+
def initialize(io, block_size: BLOCK_SIZE, maximum_read_size: MAXIMUM_READ_SIZE, sync: true, deferred: nil)
|
42
42
|
@io = io
|
43
43
|
@eof = false
|
44
44
|
|
45
|
+
@deferred = deferred
|
46
|
+
@pending = 0
|
47
|
+
|
45
48
|
# We don't want Ruby to do any IO buffering.
|
46
49
|
@io.sync = sync
|
47
50
|
|
@@ -55,6 +58,8 @@ module Async
|
|
55
58
|
@input_buffer = Buffer.new
|
56
59
|
end
|
57
60
|
|
61
|
+
attr_accessor :reactor
|
62
|
+
|
58
63
|
attr :io
|
59
64
|
attr :block_size
|
60
65
|
|
@@ -161,11 +166,34 @@ module Async
|
|
161
166
|
# Flushes buffered data to the stream.
|
162
167
|
def flush
|
163
168
|
unless @write_buffer.empty?
|
164
|
-
@
|
165
|
-
|
169
|
+
if @deferred and task = Task.current
|
170
|
+
if @pending.zero?
|
171
|
+
task.reactor << self
|
172
|
+
end
|
173
|
+
|
174
|
+
@pending += 1
|
175
|
+
else
|
176
|
+
Async.logger.debug(self) {"Flushing immediate write (#{@write_buffer.bytesize} bytes)..."}
|
177
|
+
|
178
|
+
@io.write(@write_buffer)
|
179
|
+
@write_buffer.clear
|
180
|
+
@pending = 0
|
181
|
+
end
|
166
182
|
end
|
167
183
|
end
|
168
184
|
|
185
|
+
def alive?
|
186
|
+
@pending > 0
|
187
|
+
end
|
188
|
+
|
189
|
+
def resume
|
190
|
+
Async.logger.debug(self) {"Flushing #{@pending} writes (#{@write_buffer.bytesize} bytes)..."}
|
191
|
+
|
192
|
+
@io.write(@write_buffer)
|
193
|
+
@write_buffer.clear
|
194
|
+
@pending = 0
|
195
|
+
end
|
196
|
+
|
169
197
|
def gets(separator = $/, **options)
|
170
198
|
read_until(separator, **options)
|
171
199
|
end
|
@@ -191,7 +219,7 @@ module Async
|
|
191
219
|
end
|
192
220
|
|
193
221
|
def close_write
|
194
|
-
|
222
|
+
resume unless @write_buffer.empty?
|
195
223
|
ensure
|
196
224
|
@io.close_write
|
197
225
|
end
|
@@ -201,7 +229,7 @@ module Async
|
|
201
229
|
return if @io.closed?
|
202
230
|
|
203
231
|
begin
|
204
|
-
|
232
|
+
resume unless @write_buffer.empty?
|
205
233
|
rescue
|
206
234
|
# We really can't do anything here unless we want #close to raise exceptions.
|
207
235
|
ensure
|
data/lib/async/io/trap.rb
CHANGED
@@ -36,7 +36,11 @@ module Async
|
|
36
36
|
|
37
37
|
# Ignore the trap within the current process. Can be invoked before forking and/or invoking `install!` to assert default behaviour.
|
38
38
|
def ignore!
|
39
|
-
Signal.trap(@name,
|
39
|
+
Signal.trap(@name, :IGNORE)
|
40
|
+
end
|
41
|
+
|
42
|
+
def default!
|
43
|
+
Signal.trap(@name, :DEFAULT)
|
40
44
|
end
|
41
45
|
|
42
46
|
# Install the trap into the current process. Thread safe.
|
@@ -55,17 +59,21 @@ module Async
|
|
55
59
|
return true
|
56
60
|
end
|
57
61
|
|
58
|
-
#
|
59
|
-
|
60
|
-
|
62
|
+
# Wait until the signal occurs. If a block is given, execute in a loop.
|
63
|
+
# @yield [Async::Task] the current task.
|
64
|
+
def wait(task: Task.current, &block)
|
61
65
|
task.annotate("waiting for signal #{@name}")
|
62
66
|
|
63
67
|
notification = Notification.new
|
64
68
|
@notifications << notification
|
65
69
|
|
66
|
-
|
70
|
+
if block_given?
|
71
|
+
while true
|
72
|
+
notification.wait
|
73
|
+
yield task
|
74
|
+
end
|
75
|
+
else
|
67
76
|
notification.wait
|
68
|
-
yield
|
69
77
|
end
|
70
78
|
ensure
|
71
79
|
if notification
|
@@ -74,6 +82,15 @@ module Async
|
|
74
82
|
end
|
75
83
|
end
|
76
84
|
|
85
|
+
# Deprecated.
|
86
|
+
alias trap wait
|
87
|
+
|
88
|
+
def async(parent: Task.current, &block)
|
89
|
+
parent.async do |task|
|
90
|
+
self.trap(task: task, &block)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
77
94
|
# Signal all waiting tasks that the trap occurred.
|
78
95
|
# @return [void]
|
79
96
|
def trigger(signal_number = nil)
|
data/lib/async/io/version.rb
CHANGED
@@ -28,7 +28,7 @@ RSpec.describe Async::IO::Generic do
|
|
28
28
|
|
29
29
|
CONSOLE_METHODS = [:beep, :cooked, :cooked!, :cursor, :cursor=, :echo=, :echo?,:getch, :getpass, :goto, :iflush, :ioflush, :noecho, :oflush,:pressed?, :raw, :raw!, :winsize, :winsize=]
|
30
30
|
# On TruffleRuby, IO#encode_with needs to be defined for YAML.dump as a public method, allow it
|
31
|
-
ignore = [:encode_with]
|
31
|
+
ignore = [:encode_with, :check_winsize_changed, :clear_screen, :console_mode, :console_mode=, :cursor_down, :cursor_left, :cursor_right, :cursor_up, :erase_line, :erase_screen, :goto_column, :scroll_backward, :scroll_forward]
|
32
32
|
|
33
33
|
it_should_behave_like Async::IO::Generic, [
|
34
34
|
:bytes, :chars, :codepoints, :each, :each_byte, :each_char, :each_codepoint, :each_line, :getbyte, :getc, :gets, :lineno, :lineno=, :lines, :print, :printf, :putc, :puts, :readbyte, :readchar, :readline, :readlines, :ungetbyte, :ungetc
|
@@ -19,14 +19,19 @@
|
|
19
19
|
# THE SOFTWARE.
|
20
20
|
|
21
21
|
require 'async/io/protocol/line'
|
22
|
+
require 'async/io/socket'
|
22
23
|
|
23
24
|
RSpec.describe Async::IO::Protocol::Line do
|
24
|
-
|
25
|
-
|
26
|
-
|
25
|
+
include_context Async::RSpec::Reactor
|
26
|
+
|
27
|
+
let(:pipe) {@pipe = Async::IO::Socket.pair(Socket::AF_UNIX, Socket::SOCK_STREAM)}
|
28
|
+
let(:remote) {pipe.first}
|
29
|
+
subject {described_class.new(Async::IO::Stream.new(pipe.last, deferred: true), "\n")}
|
30
|
+
|
31
|
+
after(:each) {@pipe&.each(&:close)}
|
27
32
|
|
28
33
|
context "default line ending" do
|
29
|
-
subject {described_class.new(
|
34
|
+
subject {described_class.new(nil)}
|
30
35
|
|
31
36
|
it "should have default eol terminator" do
|
32
37
|
expect(subject.eol).to_not be_nil
|
@@ -36,15 +41,16 @@ RSpec.describe Async::IO::Protocol::Line do
|
|
36
41
|
describe '#write_lines' do
|
37
42
|
it "should write line" do
|
38
43
|
subject.write_lines "Hello World"
|
44
|
+
subject.close
|
39
45
|
|
40
|
-
expect(
|
46
|
+
expect(remote.read).to be == "Hello World\n"
|
41
47
|
end
|
42
48
|
end
|
43
49
|
|
44
50
|
describe '#read_line' do
|
45
51
|
before(:each) do
|
46
|
-
|
47
|
-
|
52
|
+
remote.write "Hello World\n"
|
53
|
+
remote.close
|
48
54
|
end
|
49
55
|
|
50
56
|
it "should read one line" do
|
@@ -58,8 +64,8 @@ RSpec.describe Async::IO::Protocol::Line do
|
|
58
64
|
|
59
65
|
describe '#read_lines' do
|
60
66
|
before(:each) do
|
61
|
-
|
62
|
-
|
67
|
+
remote.write "Hello\nWorld\n"
|
68
|
+
remote.close
|
63
69
|
end
|
64
70
|
|
65
71
|
it "should read multiple lines" do
|
data/spec/async/io/trap_spec.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: async-io
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.26.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Samuel Williams
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-
|
11
|
+
date: 2019-10-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: async
|
@@ -129,6 +129,8 @@ files:
|
|
129
129
|
- examples/allocations/read_chunks.rb
|
130
130
|
- examples/chat/client.rb
|
131
131
|
- examples/chat/server.rb
|
132
|
+
- examples/echo/client.rb
|
133
|
+
- examples/echo/server.rb
|
132
134
|
- examples/issues/broken_ssl.rb
|
133
135
|
- examples/issues/pipes.rb
|
134
136
|
- examples/millions/client.rb
|
@@ -206,7 +208,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
206
208
|
- !ruby/object:Gem::Version
|
207
209
|
version: '0'
|
208
210
|
requirements: []
|
209
|
-
rubygems_version: 3.0.
|
211
|
+
rubygems_version: 3.0.4
|
210
212
|
signing_key:
|
211
213
|
specification_version: 4
|
212
214
|
summary: Provides support for asynchonous TCP, UDP, UNIX and SSL sockets.
|