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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8e01be4ac327bd569ca94ab2dee892e146d09dcba53d07602b01b77362c2601d
4
- data.tar.gz: cef6ce9421a1f1d8ce9848390e998c251d2972de5915ed47459f8c5352620aca
3
+ metadata.gz: 8c73625346e61dc5ca64794c5fe70343437cf18e2f019a1fb7bba89d76eaaba6
4
+ data.tar.gz: 3fb2fbe3c6b50642b8ad3650b7829119c7f5b41ee8837569571e01a30364a4cf
5
5
  SHA512:
6
- metadata.gz: d3d5506e50c9d3ea3259a87a90943565071317f11d277b08608bbfc35ce5b85441e0521950716cd37f20d56a2591544a376624e9bf8d029c338ce748ecef6c9b
7
- data.tar.gz: 45114022177aba3e1caf2680cb49754b1ad511487a4327f66851c30fca4a832255545169c275c1dd387aff6c7c241a3286a5241f77de6d4c89b759fc50ec264e
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
@@ -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
- @io.write(@write_buffer)
165
- @write_buffer.clear
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
- flush
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
- flush
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
@@ -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, "IGNORE")
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
- # Block the calling task until the signal occurs.
59
- def trap
60
- task = Task.current
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
- while true
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)
@@ -20,6 +20,6 @@
20
20
 
21
21
  module Async
22
22
  module IO
23
- VERSION = "1.25.0"
23
+ VERSION = "1.26.0"
24
24
  end
25
25
  end
@@ -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
- let(:io) {StringIO.new}
25
- let(:stream) {Async::IO::Stream.new(io)}
26
- subject {described_class.new(stream, "\n")}
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(stream)}
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(io.string).to be == "Hello World\n"
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
- io.puts "Hello World"
47
- io.seek(0)
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
- io << "Hello\nWorld\n"
62
- io.seek(0)
67
+ remote.write "Hello\nWorld\n"
68
+ remote.close
63
69
  end
64
70
 
65
71
  it "should read multiple lines" do
@@ -35,7 +35,7 @@ RSpec.describe Async::IO::Trap do
35
35
  trapped = false
36
36
 
37
37
  waiting_task = reactor.async do
38
- subject.trap do
38
+ subject.wait do
39
39
  trapped = true
40
40
  break
41
41
  end
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.25.0
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-08-11 00:00:00.000000000 Z
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.3
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.