async 0.13.0 → 0.14.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.
@@ -19,5 +19,5 @@
19
19
  # THE SOFTWARE.
20
20
 
21
21
  module Async
22
- VERSION = "0.13.0"
22
+ VERSION = "0.14.0"
23
23
  end
@@ -22,58 +22,74 @@ module Async
22
22
  # Represents an asynchronous IO within a reactor.
23
23
  class Wrapper
24
24
  # @param io the native object to wrap.
25
- # @param task [Task] the task that is managing this wrapper.
26
- def initialize(io, task)
25
+ # @param reactor [Reactor] the reactor that is managing this wrapper, or not specified, it's looked up by way of {Task.current}.
26
+ # @param bound [Boolean] whether the underlying socket will be closed if the wrapper is closed.
27
+ def initialize(io, reactor = nil)
27
28
  @io = io
28
- @task = task
29
+
30
+ @reactor = reactor || Task.current.reactor
29
31
  @monitor = nil
30
32
  end
31
33
 
32
34
  # The underlying native `io`.
33
35
  attr :io
34
36
 
35
- # The task this wrapper is associated with.
36
- attr :task
37
+ # The reactor this wrapper is associated with.
38
+ attr :reactor
37
39
 
38
40
  # Wait for the io to become readable.
39
- def wait_readable
40
- wait_any(:r)
41
+ def wait_readable(duration = nil)
42
+ wait_any(:r, duration)
41
43
  end
42
44
 
43
45
  # Wait for the io to become writable.
44
- def wait_writable
45
- wait_any(:w)
46
+ def wait_writable(duration = nil)
47
+ wait_any(:w, duration)
46
48
  end
47
49
 
48
50
  # Wait fo the io to become either readable or writable.
49
51
  # @param interests [:r | :w | :rw] what events to wait for.
50
- def wait_any(interests = :rw)
51
- monitor(interests) do
52
- Task.yield
53
- end
52
+ # @param duration [Float] timeout after the given duration if not `nil`.
53
+ def wait_any(interests = :rw, duration = nil)
54
+ monitor(interests, duration)
54
55
  end
55
56
 
56
57
  # Close the monitor.
57
58
  def close
58
- @monitor.close if @monitor
59
- @monitor = nil
59
+ close_monitor
60
+
61
+ @io.close if @io
60
62
  end
61
63
 
62
64
  private
63
65
 
66
+ def close_monitor
67
+ if @monitor
68
+ @monitor.close
69
+ @monitor = nil
70
+ end
71
+ end
72
+
64
73
  # Monitor the io for the given events
65
- # @param interests [:r | :w | :rw] what events to wait for.
66
- def monitor(interests)
74
+ def monitor(interests, duration = nil)
67
75
  unless @monitor
68
- @monitor = @task.register(@io, interests)
76
+ @monitor = @reactor.register(@io, interests)
69
77
  else
70
78
  @monitor.interests = interests
71
79
  end
72
80
 
73
81
  @monitor.value = Fiber.current
74
82
 
75
- yield
83
+ # If the user requested an explicit timeout for this operation:
84
+ if duration
85
+ @reactor.timeout(duration) do
86
+ Task.yield
87
+ end
88
+ else
89
+ Task.yield
90
+ end
76
91
 
92
+ return true
77
93
  ensure
78
94
  @monitor.value = nil if @monitor
79
95
  end
@@ -21,7 +21,7 @@
21
21
  require 'async/condition'
22
22
 
23
23
  RSpec.describe Async::Condition do
24
- include_context 'reactor'
24
+ include_context Async::RSpec::Reactor
25
25
 
26
26
  it 'should continue after condition is signalled' do
27
27
  task = reactor.async do
@@ -44,6 +44,23 @@ RSpec.describe Async::Node do
44
44
  end
45
45
  end
46
46
 
47
+ describe '#print_hierarchy' do
48
+ let(:buffer) {StringIO.new}
49
+ let(:output) {buffer.string}
50
+ let(:lines) {output.lines}
51
+
52
+ let!(:child) {Async::Node.new(subject)}
53
+
54
+ it "can print hierarchy to bufffer" do
55
+ subject.print_hierarchy(buffer)
56
+
57
+ expect(lines.count).to be 2
58
+
59
+ expect(lines[0]).to be =~ /#<Async::Node:0x\h+>\n/
60
+ expect(lines[1]).to be =~ /\t#<Async::Node:0x\h+>\n/
61
+ end
62
+ end
63
+
47
64
  describe '#consume' do
48
65
  let(:middle) {Async::Node.new(subject)}
49
66
  let(:bottom) {Async::Node.new(middle)}
@@ -56,4 +73,24 @@ RSpec.describe Async::Node do
56
73
  expect(bottom.parent).to be middle
57
74
  end
58
75
  end
76
+
77
+ describe '#annotate' do
78
+ let(:annotation) {'reticulating splines'}
79
+
80
+ it "should have no annotation by default" do
81
+ expect(subject.annotation).to be_nil
82
+ end
83
+
84
+ it 'should output annotation when invoking #to_s' do
85
+ subject.annotate(annotation) do
86
+ expect(subject.to_s).to include(annotation)
87
+ end
88
+ end
89
+
90
+ it 'can assign annotation' do
91
+ subject.annotate(annotation)
92
+
93
+ expect(subject.annotation).to be == annotation
94
+ end
95
+ end
59
96
  end
@@ -20,7 +20,7 @@
20
20
 
21
21
  RSpec.describe Async::Reactor do
22
22
  describe '::run (in existing reactor)' do
23
- include_context "reactor"
23
+ include_context Async::RSpec::Reactor
24
24
 
25
25
  it "should nest reactor" do
26
26
  outer_reactor = Async::Task.current.reactor
@@ -41,7 +41,12 @@ RSpec.describe Async::Reactor do
41
41
  state = :stopped
42
42
  end
43
43
 
44
- subject.stop
44
+ subject.async do |task|
45
+ task.sleep(0.1)
46
+ task.reactor.stop
47
+ end
48
+
49
+ subject.run
45
50
 
46
51
  expect(state).to be == :started
47
52
  end
@@ -62,4 +67,20 @@ RSpec.describe Async::Reactor do
62
67
 
63
68
  expect{reactor.run}.to raise_error(RuntimeError, /closed/)
64
69
  end
70
+
71
+ describe '#async' do
72
+ include_context Async::RSpec::Reactor
73
+
74
+ it "can pass in arguments" do
75
+ reactor.async(:arg) do |task, arg|
76
+ expect(arg).to be == :arg
77
+ end.wait
78
+ end
79
+ end
80
+
81
+ describe '#to_s' do
82
+ it "shows stopped=" do
83
+ expect(subject.to_s).to include "stopped"
84
+ end
85
+ end
65
86
  end
@@ -23,6 +23,39 @@ require 'benchmark'
23
23
  RSpec.describe Async::Task do
24
24
  let(:reactor) {Async::Reactor.new}
25
25
 
26
+ describe '#run' do
27
+ it "can't be invoked twice" do
28
+ task = reactor.async do |task|
29
+ end
30
+
31
+ expect{task.run}.to raise_error(RuntimeError, /already running/)
32
+ end
33
+ end
34
+
35
+ describe '#async' do
36
+ it "can start child async tasks" do
37
+ child = nil
38
+
39
+ parent = reactor.async do |task|
40
+ child = task.async do
41
+ task.sleep(1)
42
+ end
43
+ end
44
+
45
+ expect(parent).to_not be_nil
46
+ expect(child).to_not be_nil
47
+ expect(child.parent).to be parent
48
+ end
49
+
50
+ it "can pass in arguments" do
51
+ reactor.async do |task|
52
+ task.async(:arg) do |task, arg|
53
+ expect(arg).to be == :arg
54
+ end.wait
55
+ end.wait
56
+ end
57
+ end
58
+
26
59
  describe '#stop' do
27
60
  it "can be stopped" do
28
61
  state = nil
@@ -95,7 +128,9 @@ RSpec.describe Async::Task do
95
128
  reactor.run
96
129
  end
97
130
 
98
- expect(time).to be_within(50).percent_of(duration)
131
+ # This is too unstable on travis.
132
+ # expect(time).to be_within(50).percent_of(duration)
133
+ expect(time).to be >= duration
99
134
  expect(state).to be == :finished
100
135
  end
101
136
  end
@@ -182,4 +217,21 @@ RSpec.describe Async::Task do
182
217
  expect(innocent_task).to be_finished
183
218
  end
184
219
  end
220
+
221
+ describe '#to_s' do
222
+ it "should show running" do
223
+ apples_task = reactor.async do |task|
224
+ task.sleep(0.1)
225
+ end
226
+
227
+ expect(apples_task.to_s).to include "running"
228
+ end
229
+
230
+ it "should show complete" do
231
+ apples_task = reactor.async do |task|
232
+ end
233
+
234
+ expect(apples_task.to_s).to include "complete"
235
+ end
236
+ end
185
237
  end
@@ -18,17 +18,33 @@
18
18
  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
19
  # THE SOFTWARE.
20
20
 
21
- require_relative 'socket'
21
+ require 'benchmark'
22
22
 
23
- module Async
24
- # Asynchronous UDP socket wrapper.
25
- class UDPSocket < IPSocket
26
- wraps ::UDPSocket
23
+ RSpec.describe Async::Wrapper do
24
+ let(:pipe) {IO.pipe}
25
+ let(:input) {Async::Wrapper.new(pipe.last)}
26
+ let(:output) {Async::Wrapper.new(pipe.first)}
27
+
28
+ describe '#wait_*' do
29
+ include_context Async::RSpec::Reactor
27
30
 
28
- # We pass `send` through directly, but in theory it might block. Internally, it uses sendto.
29
- def_delegators :@io, :send
31
+ it "can wait for writability" do
32
+ expect(input.wait_writable(1)).to be_truthy
33
+
34
+ input.close
35
+ output.close
36
+ end
30
37
 
31
- wrap_blocking_method :recvfrom, :recvfrom_nonblock
38
+ it "can wait for readability" do
39
+ reactor.async do
40
+ input.wait_writable(1)
41
+ input.io.write('Hello World')
42
+ end
43
+
44
+ expect(output.wait_readable(1)).to be_truthy
45
+
46
+ input.close
47
+ output.close
48
+ end
32
49
  end
33
50
  end
34
-
@@ -18,42 +18,9 @@ end
18
18
 
19
19
  require "bundler/setup"
20
20
  require "async"
21
- require "async/tcp_socket"
22
- require "async/udp_socket"
23
21
 
24
- RSpec.shared_context "closes all io" do
25
- def current_ios(gc: GC.start)
26
- all_ios = ObjectSpace.each_object(IO).to_a.sort_by(&:object_id)
27
-
28
- # We are not interested in ios that have been closed already:
29
- return all_ios.reject{|io| io.closed?}
30
- end
31
-
32
- # We use around(:each) because it's the highest priority.
33
- around(:each) do |example|
34
- @system_ios = current_ios
35
-
36
- result = example.run
37
-
38
- expect(current_ios).to be == @system_ios
39
-
40
- result
41
- end
42
- end
43
-
44
- RSpec.shared_context "reactor" do
45
- let(:reactor) {Async::Task.current.reactor}
46
-
47
- around(:each) do |example|
48
- Async::Reactor.run do
49
- result = example.run
50
-
51
- return result if result.is_a? Exception
52
- end
53
- end
54
-
55
- include_context "closes all io"
56
- end
22
+ # Shared rspec helpers:
23
+ require "async/rspec"
57
24
 
58
25
  RSpec.configure do |config|
59
26
  # Enable flags like --only-failures and --next-failure
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: async
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.13.0
4
+ version: 0.14.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: 2017-04-17 00:00:00.000000000 Z
11
+ date: 2017-05-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: nio4r
@@ -39,47 +39,47 @@ dependencies:
39
39
  - !ruby/object:Gem::Version
40
40
  version: '4.1'
41
41
  - !ruby/object:Gem::Dependency
42
- name: bundler
42
+ name: async-rspec
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '1.3'
47
+ version: '1.0'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: '1.3'
54
+ version: '1.0'
55
55
  - !ruby/object:Gem::Dependency
56
- name: process-daemon
56
+ name: bundler
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: 1.0.0
61
+ version: '1.3'
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: 1.0.0
68
+ version: '1.3'
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: rspec
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
73
  - - "~>"
74
74
  - !ruby/object:Gem::Version
75
- version: 3.4.0
75
+ version: '3.4'
76
76
  type: :development
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
- version: 3.4.0
82
+ version: '3.4'
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: rake
85
85
  requirement: !ruby/object:Gem::Requirement
@@ -113,19 +113,12 @@ files:
113
113
  - README.md
114
114
  - Rakefile
115
115
  - async.gemspec
116
- - examples/aio.rb
117
- - examples/echo.rb
118
116
  - lib/async.rb
119
117
  - lib/async/condition.rb
120
- - lib/async/io.rb
121
118
  - lib/async/logger.rb
122
119
  - lib/async/node.rb
123
120
  - lib/async/reactor.rb
124
- - lib/async/socket.rb
125
121
  - lib/async/task.rb
126
- - lib/async/tcp_socket.rb
127
- - lib/async/udp_socket.rb
128
- - lib/async/unix_socket.rb
129
122
  - lib/async/version.rb
130
123
  - lib/async/wrapper.rb
131
124
  - spec/async/condition_spec.rb
@@ -133,9 +126,7 @@ files:
133
126
  - spec/async/reactor/nested_spec.rb
134
127
  - spec/async/reactor_spec.rb
135
128
  - spec/async/task_spec.rb
136
- - spec/async/tcp_socket_spec.rb
137
- - spec/async/udp_socket_spec.rb
138
- - spec/async/unix_socket_spec.rb
129
+ - spec/async/wrapper_spec.rb
139
130
  - spec/spec_helper.rb
140
131
  homepage: https://github.com/socketry/async
141
132
  licenses:
@@ -147,9 +138,9 @@ require_paths:
147
138
  - lib
148
139
  required_ruby_version: !ruby/object:Gem::Requirement
149
140
  requirements:
150
- - - ">="
141
+ - - "~>"
151
142
  - !ruby/object:Gem::Version
152
- version: 2.0.0
143
+ version: '2.0'
153
144
  required_rubygems_version: !ruby/object:Gem::Requirement
154
145
  requirements:
155
146
  - - ">="
@@ -167,7 +158,5 @@ test_files:
167
158
  - spec/async/reactor/nested_spec.rb
168
159
  - spec/async/reactor_spec.rb
169
160
  - spec/async/task_spec.rb
170
- - spec/async/tcp_socket_spec.rb
171
- - spec/async/udp_socket_spec.rb
172
- - spec/async/unix_socket_spec.rb
161
+ - spec/async/wrapper_spec.rb
173
162
  - spec/spec_helper.rb