async-io 1.25.0 → 1.26.0

Sign up to get free protection for your applications and to get access to all the features.
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.