async 1.9.0 → 1.9.1

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: 4525bee2a2f58fece634c89881975c3f326ecaaecf4b5495a53f93b6e99360f7
4
- data.tar.gz: e23e0f0caa1b383f695aec88aa567bc8daa9028907427ea371579fcd5adf94f6
3
+ metadata.gz: 82346eddb843773e16f9c2da955116a1418213921a51790050ea72b316a85098
4
+ data.tar.gz: 6b848d0c50780fc9a1013708aeff0de49858423f79d23039d6b795f6548705c5
5
5
  SHA512:
6
- metadata.gz: fd2b49c02f4ea805cfe5a3dad3b515adac78cc0edd2190c46f41b3c48460fc5e5d7daa0d3da5803a385552af69c1e557dc43a89f6a13a71e048a1a08c61c3a64
7
- data.tar.gz: bb875d2e36bebb80c4850cfa1c4afc938189b2ff559652085cd932659d1363ecc82529bfcedf95197a0b73984821ab49247ef093388131a125efc3e7e9706a98
6
+ metadata.gz: 4573fa61270cad3f20b436aa322830eb1a08c77533955b58ae7e422b1788aea34db1b829a6c9a4e23faef1cc7a0d8ea40f4a2915e9102dda4ad319d91c3ed3aa
7
+ data.tar.gz: 516c2056b418feea8e39cec3513a8fc2710be916f5b1d5450a3110e9ae8207dcdb7f7a1a36688a98d081086abcb8eddde6d1766df1b01e98abb0045b44eb964a
@@ -21,3 +21,10 @@
21
21
  require_relative "async/version"
22
22
  require_relative "async/logger"
23
23
  require_relative "async/reactor"
24
+
25
+ module Async
26
+ # Invoke `Reactor.run` with all arguments/block.
27
+ def self.run(*args, &block)
28
+ Reactor.run(*args, &block)
29
+ end
30
+ end
@@ -33,17 +33,39 @@ module Async
33
33
  # The maximum number of tasks that can acquire the semaphore.
34
34
  attr :limit
35
35
 
36
+ # Is the semaphore currently acquired?
37
+ def empty?
38
+ @count.zero?
39
+ end
40
+
36
41
  # Whether trying to acquire this semaphore would block.
37
42
  def blocking?
38
43
  @count >= @limit
39
44
  end
40
45
 
46
+ # Run an async task. Will wait until the semaphore is ready until spawning and running the task.
47
+ def async(*args)
48
+ parent = Task.current
49
+
50
+ wait
51
+
52
+ parent.async do |task|
53
+ @count += 1
54
+
55
+ begin
56
+ yield task, *args
57
+ ensure
58
+ self.release
59
+ end
60
+ end
61
+ end
62
+
41
63
  # Acquire the semaphore, block if we are at the limit.
42
64
  # If no block is provided, you must call release manually.
43
65
  # @yield when the semaphore can be acquired
44
66
  # @return the result of the block if invoked
45
67
  def acquire
46
- self.wait while blocking?
68
+ wait
47
69
 
48
70
  @count += 1
49
71
 
@@ -60,37 +82,21 @@ module Async
60
82
  def release
61
83
  @count -= 1
62
84
 
63
- self.signal
64
- end
65
-
66
- # Is anyone waiting?
67
- def empty?
68
- @waiting.empty?
69
- end
70
-
71
- # Wait on this semaphore.
72
- def wait
73
- @waiting << Fiber.current
74
- Task.yield
75
- end
76
-
77
- # Resume any waiting tasks.
78
- def signal(task: Task.current)
79
- task.reactor << self if @waiting.any?
80
- end
81
-
82
- # Whether this semaphore has work to do when being resumed.
83
- def alive?
84
- @waiting.any?
85
- end
86
-
87
- # Resume tasks waiting on the semaphore, up to the maximum to the available limit.
88
- def resume
89
85
  available = @waiting.pop(@limit - @count)
90
86
 
91
87
  available.each do |fiber|
92
88
  fiber.resume if fiber.alive?
93
89
  end
94
90
  end
91
+
92
+ private
93
+
94
+ # Wait until the semaphore becomes available.
95
+ def wait
96
+ while blocking?
97
+ @waiting << Fiber.current
98
+ Task.yield
99
+ end
100
+ end
95
101
  end
96
102
  end
@@ -19,5 +19,5 @@
19
19
  # THE SOFTWARE.
20
20
 
21
21
  module Async
22
- VERSION = "1.9.0"
22
+ VERSION = "1.9.1"
23
23
  end
@@ -20,26 +20,113 @@
20
20
 
21
21
  require 'async/semaphore'
22
22
 
23
+ require_relative 'condition_examples'
24
+
23
25
  RSpec.describe Async::Semaphore do
24
26
  include_context Async::RSpec::Reactor
25
27
 
26
- it 'should process work in batches' do
27
- semaphore = Async::Semaphore.new(4)
28
- current, maximum = 0, 0
28
+ context '#async' do
29
+ let(:repeats) {40}
30
+ let(:limit) {4}
29
31
 
30
- 100.times.map do
31
- reactor.async do |task|
32
- semaphore.acquire do
32
+ it 'should process work in batches' do
33
+ semaphore = Async::Semaphore.new(limit)
34
+ current, maximum = 0, 0
35
+
36
+ result = repeats.times.map do |i|
37
+ semaphore.async do |task|
33
38
  current += 1
34
39
  maximum = [current, maximum].max
35
- task.sleep(0.01)
40
+ task.sleep(rand * 0.1)
36
41
  current -= 1
42
+
43
+ i
37
44
  end
38
- end
39
- end.collect(&:result)
45
+ end.collect(&:result)
46
+
47
+ # Verify that the maximum number of concurrent tasks was the specificed limit:
48
+ expect(maximum).to be == limit
49
+
50
+ # Verify that the results were in the correct order:
51
+ expect(result).to be == (0...repeats).to_a
52
+ end
40
53
 
41
- expect(maximum).to be == 4
54
+ it 'only allows one task at a time' do
55
+ semaphore = Async::Semaphore.new(1)
56
+ order = []
57
+
58
+ 3.times.map do |i|
59
+ semaphore.async do |task|
60
+ order << i
61
+ task.sleep(0.1)
62
+ order << i
63
+ end
64
+ end.collect(&:result)
65
+
66
+ expect(order).to be == [0, 0, 1, 1, 2, 2]
67
+ end
68
+
69
+ it 'allows tasks to execute concurrently' do
70
+ semaphore = Async::Semaphore.new(3)
71
+ order = []
72
+
73
+ 3.times.map do |i|
74
+ semaphore.async do |task|
75
+ order << i
76
+ task.sleep(0.1)
77
+ order << i
78
+ end
79
+ end.collect(&:result)
80
+
81
+ expect(order).to be == [0, 1, 2, 0, 1, 2]
82
+ end
42
83
  end
43
84
 
44
- it_behaves_like Async::Condition
85
+ context '#count' do
86
+ it 'should count number of current acquisitions' do
87
+ expect(subject.count).to be == 0
88
+
89
+ subject.acquire do
90
+ expect(subject.count).to be == 1
91
+ end
92
+ end
93
+ end
94
+
95
+ context '#limit' do
96
+ it 'should have a default limit' do
97
+ expect(subject.limit).to be == 1
98
+ end
99
+ end
100
+
101
+ context '#empty?' do
102
+ it 'should be empty unless acquired' do
103
+ expect(subject).to be_empty
104
+
105
+ subject.acquire do
106
+ expect(subject).to_not be_empty
107
+ end
108
+ end
109
+ end
110
+
111
+ context '#blocking?' do
112
+ it 'will be blocking when acquired' do
113
+ expect(subject).to_not be_blocking
114
+
115
+ subject.acquire do
116
+ expect(subject).to be_blocking
117
+ end
118
+ end
119
+ end
120
+
121
+ context '#acquire/#release' do
122
+ it 'works when called without block' do
123
+ subject.acquire
124
+
125
+ expect(subject.count).to be == 1
126
+
127
+ subject.release
128
+
129
+ expect(subject.count).to be == 0
130
+ end
131
+ end
45
132
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: async
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.9.0
4
+ version: 1.9.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Samuel Williams