async 1.6.0 → 1.7.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: 9cd2107a53d168c8c2d2cd1361d66d8f05df65ba0736b131ce718a17c3607bac
4
- data.tar.gz: 22872f2281ebc86ab5652cd53ca050d2470723d10820de8f90c2cde7a0513557
3
+ metadata.gz: f19a934a9510bb56c88e54ac305707a0cd68d5a74f925e56a2f6ca69c5a0bab9
4
+ data.tar.gz: 8c68511531f459a2c067550f369e9253d7c93064f054a08780a11f357d68a942
5
5
  SHA512:
6
- metadata.gz: cc961f8e16a6e5713c6bfe64593ad4877fc5522476e813481b51a5f9dfec272b163d7828ddb6360c37c0696be021535a61e371816338c83fdb85d9eb4dfa07c7
7
- data.tar.gz: 61362063d6dfe7162bb1f9f1c7d396513b6042a8e64acac831928b0c0849a6564ab4aeccb4c4b0cb47801255e143876a5ef4f7f8cb5b8fb3a870c06e1a70311c
6
+ metadata.gz: e1387cc04e3d358d9b39dc5f2fb59e457b173f04b3381c603a11d495f987f29c41753c6addae55fcb203e6e1a3f96bf929e45cafddf624fd374fc85b6bc8e33f
7
+ data.tar.gz: e76e0e5dc3c67f6d77209f501a58ca1c97dbc8f96d40081ec55d082e0f0be63e7ecd344ba4adfcbaadcc910590d1abdf4a4eab0bd4a0ab8310f5afbe5a55db7d
data/async.gemspec CHANGED
@@ -23,7 +23,7 @@ Gem::Specification.new do |spec|
23
23
 
24
24
  spec.required_ruby_version = ">= 2.2.7"
25
25
 
26
- spec.add_runtime_dependency "nio4r", "~> 2.0"
26
+ spec.add_runtime_dependency "nio4r", "~> 2.3"
27
27
  spec.add_runtime_dependency "timers", "~> 4.1"
28
28
 
29
29
  spec.add_development_dependency "async-rspec", "~> 1.1"
@@ -0,0 +1,86 @@
1
+ # Copyright, 2018, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
20
+
21
+ require 'nio'
22
+
23
+ module Async
24
+ class DebugSelector
25
+ class MonitorProxy
26
+ def initialize(monitor, selector)
27
+ @monitor = monitor
28
+ @selector = selector
29
+ end
30
+
31
+ def close
32
+ @selector.deregister(@monitor.io)
33
+ @monitor.close
34
+ end
35
+
36
+ def method_missing(*args, &block)
37
+ @monitor.send(*args)
38
+ end
39
+
40
+ def respond_to?(*args)
41
+ @monitor.respond_to?(*args)
42
+ end
43
+ end
44
+
45
+ def initialize(selector)
46
+ @selector = selector
47
+ @monitors = {}
48
+ end
49
+
50
+ def register(io, interests)
51
+ $stderr.puts "Registering #{io.inspect} for #{interests}."
52
+
53
+ if monitor = @monitors[io.fileno]
54
+ raise RuntimeError, "Trying to register monitor for #{io.inspect} but it was already registered as #{monitor.io.inspect}!"
55
+ end
56
+
57
+ @monitors[io.fileno] = io
58
+
59
+ MonitorProxy.new(@selector.register(io, interests), self)
60
+ end
61
+
62
+ def deregister(io)
63
+ $stderr.puts "Deregistering #{io.inspect}."
64
+
65
+ unless @monitors.delete(io.fileno)
66
+ raise RuntimeError, "Trying to remove monitor for #{io.inspect} but it was not registered!"
67
+ end
68
+ end
69
+
70
+ def wakeup
71
+ @selector.wakeup
72
+ end
73
+
74
+ def close
75
+ if @monitors.any?
76
+ $stderr.puts "Trying to close selector with active monitors: #{@monitors.values.inspect}!"
77
+ end
78
+
79
+ @selector.close
80
+ end
81
+
82
+ def select(*args)
83
+ @selector.select(*args)
84
+ end
85
+ end
86
+ end
data/lib/async/reactor.rb CHANGED
@@ -30,7 +30,17 @@ module Async
30
30
  # Raised if a timeout occurs on a specific Fiber. Handled gracefully by {Task}.
31
31
  class TimeoutError < RuntimeError
32
32
  end
33
-
33
+
34
+ class MonitorError < RuntimeError
35
+ def initialize(monitor)
36
+ super "Event detected on IO #{monitor.io.inspect} (#{monitor.interests} -> #{monitor.readiness}) without corresponding fiber!"
37
+
38
+ @monitor = monitor
39
+ end
40
+
41
+ attr :monitor
42
+ end
43
+
34
44
  # An asynchronous, cooperatively scheduled event reactor.
35
45
  class Reactor < Node
36
46
  extend Forwardable
@@ -165,20 +175,21 @@ module Async
165
175
  interval = 0
166
176
  end
167
177
 
168
- # Async.logger.debug{"[#{self} Pre] Updating #{@children.count} children..."}
178
+ # Async.logger.debug(self) {"Updating #{@children.count} children..."}
169
179
  # As timeouts may have been updated, and caused fibers to complete, we should check this.
170
180
 
171
181
  # If there is nothing to do, then finish:
172
- # Async.logger.debug{"[#{self}] @children.empty? = #{@children.empty?} && interval #{interval.inspect}"}
173
182
  if !interval && self.finished?
174
183
  return initial_task
175
184
  end
176
185
 
177
- # Async.logger.debug{"Selecting with #{@children.count} fibers interval = #{interval.inspect}..."}
186
+ # Async.logger.debug(self) {"Selecting with #{@children.count} fibers interval = #{interval.inspect}..."}
178
187
  if monitors = @selector.select(interval)
179
188
  monitors.each do |monitor|
180
189
  if fiber = monitor.value
181
190
  fiber.resume # if fiber.alive?
191
+ else
192
+ raise MonitorError.new(monitor)
182
193
  end
183
194
  end
184
195
  end
data/lib/async/version.rb CHANGED
@@ -19,5 +19,5 @@
19
19
  # THE SOFTWARE.
20
20
 
21
21
  module Async
22
- VERSION = "1.6.0"
22
+ VERSION = "1.7.0"
23
23
  end
data/lib/async/wrapper.rb CHANGED
@@ -42,10 +42,12 @@ module Async
42
42
  # Bind this wrapper to a different reactor. Assign nil to convert to an unbound wrapper (can be used from any reactor/task but with slightly increased overhead.)
43
43
  # Binding to a reactor is purely a performance consideration. Generally, I don't like APIs that exist only due to optimisations. This is borderline, so consider this functionality semi-private.
44
44
  def reactor= reactor
45
- @monitor.close if @monitor
45
+ if @monitor
46
+ @monitor.close
47
+ @monitor = nil
48
+ end
46
49
 
47
50
  @reactor = reactor
48
- @monitor = nil
49
51
  end
50
52
 
51
53
  # Wait for the io to become readable.
@@ -74,8 +76,7 @@ module Async
74
76
  begin
75
77
  wait_for(@reactor, @monitor, duration)
76
78
  ensure
77
- @monitor.remove_interest(@monitor.interests)
78
- @monitor.value = nil
79
+ @monitor.interests = nil
79
80
  end
80
81
  else
81
82
  reactor = Task.current.reactor
@@ -91,7 +92,10 @@ module Async
91
92
 
92
93
  # Close the io and monitor.
93
94
  def close
94
- @monitor.close if @monitor
95
+ if @monitor
96
+ @monitor.close
97
+ @monitor = nil
98
+ end
95
99
 
96
100
  @io.close
97
101
  end
@@ -19,6 +19,8 @@ RSpec.describe Async::Wrapper do
19
19
  input.wait_readable
20
20
  input.io.read(1)
21
21
  end
22
+
23
+ input.reactor = nil
22
24
  end
23
25
  end
24
26
 
@@ -46,6 +46,24 @@ RSpec.describe Async::Wrapper do
46
46
  input.close
47
47
  output.close
48
48
  end
49
+
50
+ it "can wait for readability in multiple tasks" do
51
+ reactor.async do
52
+ input.wait_writable(1)
53
+ input.io.write('Hello World')
54
+ end
55
+
56
+ output.reactor = reactor
57
+
58
+ 2.times do
59
+ reactor.async do
60
+ expect(output.wait_readable(1)).to be_truthy
61
+ end.wait
62
+ end
63
+
64
+ input.close
65
+ output.close
66
+ end
49
67
  end
50
68
 
51
69
  describe '#reactor=' do
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: 1.6.0
4
+ version: 1.7.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: 2018-04-16 00:00:00.000000000 Z
11
+ date: 2018-04-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: nio4r
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '2.0'
19
+ version: '2.3'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '2.0'
26
+ version: '2.3'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: timers
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -120,6 +120,7 @@ files:
120
120
  - lib/async.rb
121
121
  - lib/async/clock.rb
122
122
  - lib/async/condition.rb
123
+ - lib/async/debug.rb
123
124
  - lib/async/logger.rb
124
125
  - lib/async/measure.rb
125
126
  - lib/async/node.rb