async 1.9.0 → 1.9.1

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: 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