rosarium 0.1.1 → 0.1.6

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
- SHA1:
3
- metadata.gz: 0caf50a0435f2f9bd47f702eb22de12e5ad672d2
4
- data.tar.gz: dd71f5aa5e34a2b6302dcf6d65d5aecd41825f2f
2
+ SHA256:
3
+ metadata.gz: 7953c9f3adbdebbd4299ddef80c064fe76f32fe74e8bd8abafd160d0f9b6d816
4
+ data.tar.gz: 80b937b9e1e8750f1bbac9a9913a0e129b5dc80048d97b3ea360774b02eb1e2e
5
5
  SHA512:
6
- metadata.gz: 3075ef2b1dcd8cdf1911e00fafb55c211d940420a18bafe356b703236e2ce888fe299898dfbc37d99f99374a56eb2fc91f60bdddc5a92d6df6e56fe0129ce25b
7
- data.tar.gz: d6575b8a7f78e44848d26e98b536e9bc08aec5db580af6523b04e10e541842256fe98b9ace1cf152adb17c64feddfd15e3e361aea1c92b33570a71242fd4a9ff
6
+ metadata.gz: 22821d81eaccb0357841982eb2095c57e58f786d535834600b5903719c180d91af364b9e8778ac1256236d19db95835262fd77b8c313bedafdf60ae0e04a7579
7
+ data.tar.gz: 34ac659ece6ea48a111db7dab0ea89f8c0adcf90cfcf0802aaecba89eec49b1e8831fb9d55147691c4b686e51c1e68e38164fcd6dd27b4464fd5363ca802bcc9
data/Gemfile CHANGED
@@ -1 +1,4 @@
1
+ # frozen_string_literal: true
2
+
1
3
  gem 'rspec', '~> 3.4'
4
+ gem 'rubocop', '~> 1.2'
data/Gemfile.lock CHANGED
@@ -1,25 +1,46 @@
1
1
  GEM
2
2
  specs:
3
- diff-lcs (1.2.5)
4
- rspec (3.4.0)
5
- rspec-core (~> 3.4.0)
6
- rspec-expectations (~> 3.4.0)
7
- rspec-mocks (~> 3.4.0)
8
- rspec-core (3.4.1)
9
- rspec-support (~> 3.4.0)
10
- rspec-expectations (3.4.0)
3
+ ast (2.4.1)
4
+ diff-lcs (1.4.4)
5
+ parallel (1.20.1)
6
+ parser (3.0.0.0)
7
+ ast (~> 2.4.1)
8
+ rainbow (3.0.0)
9
+ regexp_parser (1.8.2)
10
+ rexml (3.2.4)
11
+ rspec (3.9.0)
12
+ rspec-core (~> 3.9.0)
13
+ rspec-expectations (~> 3.9.0)
14
+ rspec-mocks (~> 3.9.0)
15
+ rspec-core (3.9.2)
16
+ rspec-support (~> 3.9.3)
17
+ rspec-expectations (3.9.2)
11
18
  diff-lcs (>= 1.2.0, < 2.0)
12
- rspec-support (~> 3.4.0)
13
- rspec-mocks (3.4.0)
19
+ rspec-support (~> 3.9.0)
20
+ rspec-mocks (3.9.1)
14
21
  diff-lcs (>= 1.2.0, < 2.0)
15
- rspec-support (~> 3.4.0)
16
- rspec-support (3.4.1)
22
+ rspec-support (~> 3.9.0)
23
+ rspec-support (3.9.3)
24
+ rubocop (1.2.0)
25
+ parallel (~> 1.10)
26
+ parser (>= 2.7.1.5)
27
+ rainbow (>= 2.2.2, < 4.0)
28
+ regexp_parser (>= 1.8)
29
+ rexml
30
+ rubocop-ast (>= 1.0.1)
31
+ ruby-progressbar (~> 1.7)
32
+ unicode-display_width (>= 1.4.0, < 2.0)
33
+ rubocop-ast (1.4.0)
34
+ parser (>= 2.7.1.5)
35
+ ruby-progressbar (1.11.0)
36
+ unicode-display_width (1.7.0)
17
37
 
18
38
  PLATFORMS
19
39
  ruby
20
40
 
21
41
  DEPENDENCIES
22
42
  rspec (~> 3.4)
43
+ rubocop (~> 1.2)
23
44
 
24
45
  BUNDLED WITH
25
- 1.11.2
46
+ 1.17.3
data/README.md CHANGED
@@ -66,9 +66,15 @@ then later, use the "deferred" to fulfill or reject the promise:
66
66
  ## Methods of promises:
67
67
 
68
68
  ```
69
- # One of: :pending, :resolving, :fulfilled, :rejected.
69
+ # One of: :pending, :fulfilled, :rejected.
70
70
  promise.state
71
71
 
72
+ # Equivalent to "state == :fulfilled"
73
+ promise.fulfilled?
74
+
75
+ # Equivalent to "state == :rejected"
76
+ promise.rejected?
77
+
72
78
  # Wait for the promise to be settled, then return its value (if fulfilled -
73
79
  # note the value may be nil), or nil (if rejected).
74
80
  promise.value
@@ -77,22 +83,13 @@ then later, use the "deferred" to fulfill or reject the promise:
77
83
  # or nil (if fulfilled).
78
84
  promise.reason
79
85
 
80
- # A hash describing the state of the promise. Always includes :state key;
81
- # may include :value or :reason.
82
- promise.inspect
83
-
84
- # true iff state == :fulfilled
85
- promise.fulfilled?
86
-
87
- # true iff state == :rejected
88
- promise.rejected?
89
-
90
86
  # Wait for the promise to be settled, then return its value (if fulfilled),
91
87
  # or raise with the rejection reason (if rejected).
92
88
  promise.value!
93
89
 
94
- # Wait for the promise to be settled
95
- promise.wait
90
+ # A hash describing the state of the promise. Always includes :state key;
91
+ # may include :value or :reason.
92
+ promise.inspect
96
93
  ```
97
94
 
98
95
  Chaining promises together:
@@ -117,7 +114,3 @@ Promise code (every time a ruby block appears in the above examples) is run
117
114
  via a fixed-size thread pool, currently set to 10 threads. Execution order is
118
115
  not defined.
119
116
 
120
- In comparison to the Promises/A+ spec <https://promisesaplus.com/>, these
121
- promises have an extra possible state, `:resolving`. You are encouraged to
122
- use `#fulfilled?` and `#rejected?` instead anyway.
123
-
data/lib/rosarium.rb CHANGED
@@ -1,5 +1,6 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative 'rosarium/fixed_thread_executor'
2
- require_relative 'rosarium/simple_promise'
3
4
  require_relative 'rosarium/deferred'
4
5
  require_relative 'rosarium/promise'
5
6
 
@@ -1,5 +1,6 @@
1
- module Rosarium
1
+ # frozen_string_literal: true
2
2
 
3
+ module Rosarium
3
4
  class Deferred
4
5
 
5
6
  def initialize(promise, resolver, rejecter)
@@ -8,9 +9,7 @@ module Rosarium
8
9
  @rejecter = rejecter
9
10
  end
10
11
 
11
- def promise
12
- @promise
13
- end
12
+ attr_reader :promise
14
13
 
15
14
  def resolve(value)
16
15
  @resolver.call(value)
@@ -21,5 +20,4 @@ module Rosarium
21
20
  end
22
21
 
23
22
  end
24
-
25
23
  end
@@ -1,5 +1,6 @@
1
- module Rosarium
1
+ # frozen_string_literal: true
2
2
 
3
+ module Rosarium
3
4
  class FixedThreadExecutor
4
5
 
5
6
  def initialize(max = 1)
@@ -14,7 +15,7 @@ module Rosarium
14
15
  @mutex.synchronize do
15
16
  @waiting << block
16
17
  if @executing < @max
17
- @executing = @executing + 1
18
+ @executing += 1
18
19
  t = Thread.new { execute_and_count_down }
19
20
  @threads.push t
20
21
  end
@@ -36,26 +37,23 @@ module Rosarium
36
37
  private
37
38
 
38
39
  def execute_and_count_down
39
- begin
40
- execute
41
- ensure
42
- @mutex.synchronize do
43
- @executing = @executing - 1
44
- end
40
+ execute
41
+ ensure
42
+ @mutex.synchronize do
43
+ @executing -= 1
45
44
  end
46
45
  end
47
46
 
48
47
  def execute
49
- while true
48
+ loop do
50
49
  block = @mutex.synchronize { @waiting.shift }
51
50
  block or break
52
51
  begin
53
52
  block.call
54
- rescue Exception => e
53
+ rescue Exception
55
54
  end
56
55
  end
57
56
  end
58
57
 
59
58
  end
60
-
61
59
  end
@@ -1,15 +1,30 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Rosarium
4
+ class Promise
2
5
 
3
- class Promise < SimplePromise
6
+ DEFAULT_ON_FULFILL = proc { |value| value }
7
+ DEFAULT_ON_REJECT = proc { |reason| raise reason }
4
8
 
5
- DEFAULT_ON_FULFILL = Proc.new {|value| value}
6
- DEFAULT_ON_REJECT = Proc.new {|reason| raise reason}
9
+ private_class_method :new
7
10
 
8
- def self.resolve(value)
9
- if value.kind_of? Promise
10
- return value
11
+ def self.defer
12
+ promise = new
13
+
14
+ resolver = ->(value) { promise.send(:try_settle, value, nil) }
15
+
16
+ rejecter = lambda do |reason|
17
+ raise "reason must be an Exception" unless reason.is_a?(Exception)
18
+
19
+ promise.send(:try_settle, nil, reason)
11
20
  end
12
21
 
22
+ Deferred.new(promise, resolver, rejecter)
23
+ end
24
+
25
+ def self.resolve(value)
26
+ return value if value.is_a? Promise
27
+
13
28
  deferred = defer
14
29
  deferred.resolve(value)
15
30
  deferred.promise
@@ -22,15 +37,7 @@ module Rosarium
22
37
  end
23
38
 
24
39
  def self.execute(&block)
25
- deferred = defer
26
- EXECUTOR.submit do
27
- begin
28
- deferred.resolve block.call
29
- rescue Exception => e
30
- deferred.reject e
31
- end
32
- end
33
- deferred.promise
40
+ @resolved.then(&block)
34
41
  end
35
42
 
36
43
  def self.all_settled(promises)
@@ -42,7 +49,7 @@ module Rosarium
42
49
  waiting_for = promises.count
43
50
  mutex = Mutex.new
44
51
 
45
- check = Proc.new do
52
+ check = proc do
46
53
  # Includes both fulfilled and rejected, so always hits zero eventually
47
54
  if mutex.synchronize { (waiting_for -= 1) == 0 }
48
55
  deferred.resolve promises
@@ -50,7 +57,7 @@ module Rosarium
50
57
  end
51
58
 
52
59
  promises.each do |promise|
53
- promise.then(check) { check.call }
60
+ promise.then(check, &check)
54
61
  end
55
62
 
56
63
  deferred.promise
@@ -65,16 +72,16 @@ module Rosarium
65
72
  waiting_for = promises.count
66
73
  mutex = Mutex.new
67
74
 
68
- do_reject = Proc.new {|reason| deferred.reject reason}
69
- do_fulfill = Proc.new do
75
+ do_reject = ->(reason) { deferred.reject(reason) }
76
+ do_fulfill = proc do
70
77
  # Only fulfilled (not rejected), so hits zero iff all promises were fulfilled
71
78
  if mutex.synchronize { (waiting_for -= 1) == 0 }
72
- deferred.resolve(promises.map &:value)
79
+ deferred.resolve(promises.map(&:value))
73
80
  end
74
81
  end
75
82
 
76
83
  promises.each do |promise|
77
- promise.then(do_reject) { do_fulfill.call }
84
+ promise.then(do_reject, &do_fulfill)
78
85
  end
79
86
 
80
87
  deferred.promise
@@ -86,17 +93,21 @@ module Rosarium
86
93
  on_fulfilled ||= DEFAULT_ON_FULFILL
87
94
  on_rejected ||= DEFAULT_ON_REJECT
88
95
 
89
- on_resolution do
90
- begin
91
- deferred.resolve(
92
- if fulfilled?
93
- on_fulfilled.call value
94
- else
95
- on_rejected.call reason
96
- end
97
- )
98
- rescue Exception => e
99
- deferred.reject e
96
+ when_settled do
97
+ EXECUTOR.submit do
98
+ begin
99
+ deferred.resolve(
100
+ if fulfilled?
101
+ # User-supplied code
102
+ on_fulfilled.call value
103
+ else
104
+ # User-supplied code
105
+ on_rejected.call reason
106
+ end
107
+ )
108
+ rescue Exception => e
109
+ deferred.reject e
110
+ end
100
111
  end
101
112
  end
102
113
 
@@ -107,9 +118,142 @@ module Rosarium
107
118
  self.then(block)
108
119
  end
109
120
 
110
- alias_method :catch, :rescue
111
- alias_method :on_error, :rescue
121
+ alias catch rescue
122
+ alias on_error rescue
112
123
 
113
- end
124
+ public
125
+
126
+ def initialize
127
+ @state = :pending
128
+ @resolving = false
129
+ @mutex = Mutex.new
130
+ @condition = ConditionVariable.new
131
+ @when_settled = []
132
+ end
133
+
134
+ def state
135
+ synchronize { @state }
136
+ end
137
+
138
+ def value
139
+ wait
140
+ synchronize { @value }
141
+ end
142
+
143
+ def reason
144
+ wait
145
+ synchronize { @reason }
146
+ end
147
+
148
+ def inspect
149
+ synchronize do
150
+ r = { state: @state }
151
+ r[:value] = @value if @state == :fulfilled
152
+ r[:reason] = @reason if @state == :rejected
153
+ r
154
+ end
155
+ end
156
+
157
+ def fulfilled?
158
+ state == :fulfilled
159
+ end
160
+
161
+ def rejected?
162
+ state == :rejected
163
+ end
164
+
165
+ def value!
166
+ wait
167
+ synchronize do
168
+ raise @reason if @state == :rejected
169
+ @value
170
+ end
171
+ end
114
172
 
173
+ private
174
+
175
+ def wait
176
+ when_settled do
177
+ synchronize { @condition.broadcast }
178
+ end
179
+
180
+ synchronize do
181
+ loop do
182
+ return if @state == :fulfilled || @state == :rejected
183
+ @condition.wait @mutex
184
+ end
185
+ end
186
+ end
187
+
188
+ def synchronize(&block)
189
+ @mutex.synchronize(&block)
190
+ end
191
+
192
+ def try_settle(value, reason)
193
+ callbacks = []
194
+ add_when_settled = false
195
+
196
+ synchronize do
197
+ if @state == :pending && !@resolving
198
+ if value.is_a? Promise
199
+ @resolving = true
200
+ add_when_settled = true
201
+ elsif reason.nil?
202
+ @value = value
203
+ @state = :fulfilled
204
+ callbacks.concat @when_settled
205
+ @when_settled.clear
206
+ else
207
+ @reason = reason
208
+ @state = :rejected
209
+ callbacks.concat @when_settled
210
+ @when_settled.clear
211
+ end
212
+ end
213
+ end
214
+
215
+ # rubocop:disable Style/IfUnlessModifier
216
+ if add_when_settled
217
+ value.when_settled { copy_settlement_from value }
218
+ end
219
+ # rubocop:enable Style/IfUnlessModifier
220
+
221
+ callbacks.each { |c| EXECUTOR.submit(&c) }
222
+ end
223
+
224
+ def copy_settlement_from(other)
225
+ callbacks = []
226
+
227
+ synchronize do
228
+ @value = other.value
229
+ @reason = other.reason
230
+ @state = other.state
231
+ @resolving = false
232
+ callbacks.concat @when_settled
233
+ @when_settled.clear
234
+ end
235
+
236
+ callbacks.each { |c| EXECUTOR.submit(&c) }
237
+ end
238
+
239
+ protected
240
+
241
+ def when_settled(&block)
242
+ immediate = synchronize do
243
+ if @state == :fulfilled || @state == :rejected
244
+ true
245
+ else
246
+ @when_settled << block
247
+ false
248
+ end
249
+ end
250
+
251
+ block.call if immediate
252
+
253
+ nil
254
+ end
255
+
256
+ @resolved = resolve(nil)
257
+
258
+ end
115
259
  end
@@ -63,7 +63,7 @@ describe "deferred promises" do
63
63
  d1.resolve(d2.promise)
64
64
  check_pending d1.promise
65
65
  d2.resolve 7
66
- d1.promise.wait
66
+ d1.promise.value
67
67
  check_fulfilled d1.promise, 7
68
68
  end
69
69
 
@@ -74,7 +74,7 @@ describe "deferred promises" do
74
74
  check_pending d1.promise
75
75
  e = an_error
76
76
  d2.reject e
77
- d1.promise.wait
77
+ d1.promise.value
78
78
  check_rejected d1.promise, e
79
79
  end
80
80
 
@@ -33,7 +33,7 @@ describe Rosarium::Promise do
33
33
  chained = deferred.promise.then { then_called = true }
34
34
  check_pending chained
35
35
  deferred.reject e
36
- chained.wait
36
+ chained.value
37
37
  check_rejected chained, e
38
38
  expect(then_called).to be_falsy
39
39
  end
@@ -45,7 +45,7 @@ describe Rosarium::Promise do
45
45
  got_args = nil
46
46
  chained = deferred.promise.then(Proc.new {|*args| got_args = args; raise e2 }) { raise "should never be called" }
47
47
  deferred.reject e
48
- chained.wait
48
+ chained.value
49
49
  check_rejected chained, e2
50
50
  expect(got_args).to eq([e])
51
51
  end
@@ -54,7 +54,7 @@ describe Rosarium::Promise do
54
54
  deferred = Rosarium::Promise.defer
55
55
  chained = deferred.promise.then(Proc.new {7}) { raise "should never be called" }
56
56
  deferred.reject an_error
57
- chained.wait
57
+ chained.value
58
58
  check_fulfilled chained, 7
59
59
  end
60
60
 
@@ -63,9 +63,16 @@ describe Rosarium::Promise do
63
63
  deferred = Rosarium::Promise.defer
64
64
  chained = deferred.promise.send(method) { 7 }
65
65
  deferred.reject an_error
66
- chained.wait
66
+ chained.value
67
67
  check_fulfilled chained, 7
68
68
  end
69
69
  end
70
70
 
71
+ it "uses the executor even if immediately callable" do
72
+ deferred = Rosarium::Promise.resolve(nil).then { sleep 0.1; 7 }
73
+ check_pending deferred
74
+ sleep 0.2
75
+ check_fulfilled deferred, 7
76
+ end
77
+
71
78
  end
@@ -14,16 +14,11 @@ describe "instantly-resolved promises" do
14
14
  it "creates a fulfilled promise" do
15
15
  t = Rosarium::Promise.resolve 7
16
16
  check_fulfilled t, 7
17
- expect(t).not_to respond_to(:fulfill)
18
- expect(t).not_to respond_to(:reject)
19
17
  end
20
18
 
21
19
  it "creates a rejected promise" do
22
- e = an_error
23
20
  t = Rosarium::Promise.reject an_error
24
21
  check_rejected t, an_error
25
- expect(t).not_to respond_to(:fulfill)
26
- expect(t).not_to respond_to(:reject)
27
22
  end
28
23
 
29
24
  it "creates an immediately-executable promise" do
@@ -61,7 +56,7 @@ describe "instantly-resolved promises" do
61
56
 
62
57
  e = an_error
63
58
  d2.reject e
64
- promise.wait
59
+ promise.value
65
60
  check_fulfilled promise, [ d1.promise, d2.promise ]
66
61
  end
67
62
 
@@ -82,7 +77,7 @@ describe "instantly-resolved promises" do
82
77
 
83
78
  e = an_error
84
79
  d3.reject e
85
- promise.wait
80
+ promise.value
86
81
  check_rejected promise, e
87
82
  end
88
83
 
@@ -96,7 +91,7 @@ describe "instantly-resolved promises" do
96
91
  check_pending promise
97
92
 
98
93
  d2.resolve 8
99
- promise.wait
94
+ promise.value
100
95
  check_fulfilled promise, [7,8]
101
96
  end
102
97
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rosarium
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rachel Evans
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-07-04 00:00:00.000000000 Z
11
+ date: 2021-02-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '3.4'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rubocop
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.2'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.2'
27
41
  description: |2
28
42
  Rosarium implements something that's a bit like Promises,
29
43
  inspired by the stability and ease of use of Q
@@ -41,7 +55,6 @@ files:
41
55
  - lib/rosarium/deferred.rb
42
56
  - lib/rosarium/fixed_thread_executor.rb
43
57
  - lib/rosarium/promise.rb
44
- - lib/rosarium/simple_promise.rb
45
58
  - spec/deferred_spec.rb
46
59
  - spec/fixed_thread_executor_spec.rb
47
60
  - spec/promise_methods_spec.rb
@@ -51,7 +64,7 @@ homepage: http://rve.org.uk/gems/rosarium
51
64
  licenses:
52
65
  - Apache-2.0
53
66
  metadata: {}
54
- post_install_message:
67
+ post_install_message:
55
68
  rdoc_options: []
56
69
  require_paths:
57
70
  - lib
@@ -59,16 +72,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
59
72
  requirements:
60
73
  - - ">="
61
74
  - !ruby/object:Gem::Version
62
- version: '0'
75
+ version: '2.4'
63
76
  required_rubygems_version: !ruby/object:Gem::Requirement
64
77
  requirements:
65
78
  - - ">="
66
79
  - !ruby/object:Gem::Version
67
80
  version: '0'
68
81
  requirements: []
69
- rubyforge_project:
70
- rubygems_version: 2.5.1
71
- signing_key:
82
+ rubygems_version: 3.0.8
83
+ signing_key:
72
84
  specification_version: 4
73
85
  summary: Promises, or something like them
74
86
  test_files: []
@@ -1,164 +0,0 @@
1
- module Rosarium
2
-
3
- class SimplePromise
4
-
5
- def self.defer
6
- promise = new
7
- resolver = promise.method :resolve
8
- rejecter = promise.method :reject
9
-
10
- class <<promise
11
- undef :resolve
12
- undef :reject
13
- end
14
-
15
- Deferred.new(promise, resolver, rejecter)
16
- end
17
-
18
- def initialize
19
- @state = :pending
20
- @resolving = false
21
- @mutex = Mutex.new
22
- @condition = ConditionVariable.new
23
- @on_resolution = []
24
- end
25
-
26
- def state
27
- synchronize { @state }
28
- end
29
-
30
- def value
31
- wait
32
- synchronize { @value }
33
- end
34
-
35
- def reason
36
- wait
37
- synchronize { @reason }
38
- end
39
-
40
- def inspect
41
- synchronize do
42
- r = { state: @state }
43
- r[:value] = @value if @state == :fulfilled
44
- r[:reason] = @reason if @state == :rejected
45
- r
46
- end
47
- end
48
-
49
- def fulfilled?
50
- state == :fulfilled
51
- end
52
-
53
- def rejected?
54
- state == :rejected
55
- end
56
-
57
- def value!
58
- wait
59
- synchronize do
60
- if @state == :rejected
61
- raise @reason
62
- else
63
- @value
64
- end
65
- end
66
- end
67
-
68
- def wait
69
- on_resolution do
70
- @mutex.synchronize { @condition.broadcast }
71
- end
72
-
73
- @mutex.synchronize do
74
- loop do
75
- return if @state == :fulfilled or @state == :rejected
76
- @condition.wait @mutex
77
- end
78
- end
79
- end
80
-
81
- private
82
-
83
- def synchronize
84
- @mutex.synchronize { yield }
85
- end
86
-
87
- public
88
-
89
- def resolve(value)
90
- _resolve(value, nil)
91
- end
92
-
93
- def reject(reason)
94
- raise "reason must be an Exception" unless reason.kind_of? Exception
95
- _resolve(nil, reason)
96
- end
97
-
98
- private
99
-
100
- def _resolve(value, reason)
101
- callbacks = []
102
- add_on_resolution = false
103
-
104
- synchronize do
105
- if @state == :pending and not @resolving
106
- if value.kind_of? SimplePromise
107
- @resolving = true
108
- add_on_resolution = true
109
- elsif reason.nil?
110
- @value = value
111
- @state = :fulfilled
112
- callbacks.concat @on_resolution
113
- @on_resolution.clear
114
- else
115
- @reason = reason
116
- @state = :rejected
117
- callbacks.concat @on_resolution
118
- @on_resolution.clear
119
- end
120
- end
121
- end
122
-
123
- if add_on_resolution
124
- value.on_resolution { copy_resolution_from value }
125
- end
126
-
127
- callbacks.each {|c| EXECUTOR.submit { c.call } }
128
- end
129
-
130
- def copy_resolution_from(other)
131
- callbacks = []
132
-
133
- synchronize do
134
- @value = other.value
135
- @reason = other.reason
136
- @state = other.state
137
- @resolving = false
138
- callbacks.concat @on_resolution
139
- @on_resolution.clear
140
- end
141
-
142
- callbacks.each {|c| EXECUTOR.submit { c.call } }
143
- end
144
-
145
- protected
146
-
147
- def on_resolution(&block)
148
- immediate = synchronize do
149
- if @state == :fulfilled or @state == :rejected
150
- true
151
- else
152
- @on_resolution << block
153
- false
154
- end
155
- end
156
-
157
- block.call if immediate
158
-
159
- nil
160
- end
161
-
162
- end
163
-
164
- end