concurrent-ruby-edge 0.2.4 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -124,7 +124,7 @@ module Concurrent
124
124
  #
125
125
  # An iterator to loop through the set.
126
126
  #
127
- # @yield [Object] each item in the set
127
+ # @yield [item] each item in the set
128
128
  # @yieldparam [Object] item the item you to remove from the set
129
129
  #
130
130
  # @return [Object] self: the linked set on which each was called
@@ -0,0 +1,116 @@
1
+ module Concurrent
2
+
3
+ class LockFreeQueue < Synchronization::Object
4
+
5
+ class Node < Synchronization::Object
6
+ attr_atomic :successor
7
+
8
+ def initialize(item, successor)
9
+ super()
10
+ # published through queue, no need to be volatile or final
11
+ @Item = item
12
+ self.successor = successor
13
+ end
14
+
15
+ def item
16
+ @Item
17
+ end
18
+ end
19
+
20
+ safe_initialization!
21
+
22
+ attr_atomic :head, :tail
23
+
24
+ def initialize
25
+ super()
26
+ dummy_node = Node.new(:dummy, nil)
27
+
28
+ self.head = dummy_node
29
+ self.tail = dummy_node
30
+ end
31
+
32
+ def push(item)
33
+ # allocate a new node with the item embedded
34
+ new_node = Node.new(item, nil)
35
+
36
+ # keep trying until the operation succeeds
37
+ while true
38
+ current_tail_node = tail
39
+ current_tail_successor = current_tail_node.successor
40
+
41
+ # if our stored tail is still the current tail
42
+ if current_tail_node == tail
43
+ # if that tail was really the last node
44
+ if current_tail_successor.nil?
45
+ # if we can update the previous successor of tail to point to this new node
46
+ if current_tail_node.compare_and_set_successor(nil, new_node)
47
+ # then update tail to point to this node as well
48
+ compare_and_set_tail(current_tail_node, new_node)
49
+ # and return
50
+ return true
51
+ # else, start the loop over
52
+ end
53
+ else
54
+ # in this case, the tail ref we had wasn't the real tail
55
+ # so we try to set its successor as the real tail, then start the loop again
56
+ compare_and_set_tail(current_tail_node, current_tail_successor)
57
+ end
58
+ end
59
+ end
60
+ end
61
+
62
+ def pop
63
+ # retry until some value can be returned
64
+ while true
65
+ # the value in @head is just a dummy node that always sits in that position,
66
+ # the real 'head' is in its successor
67
+ current_dummy_node = head
68
+ current_tail_node = tail
69
+
70
+ current_head_node = current_dummy_node.successor
71
+
72
+ # if our local head is still consistent with the head node, continue
73
+ # otherwise, start over
74
+ if current_dummy_node == head
75
+ # if either the queue is empty, or falling behind
76
+ if current_dummy_node == current_tail_node
77
+ # if there's nothing after the 'dummy' head node
78
+ if current_head_node.nil?
79
+ # just return nil
80
+ return nil
81
+ else
82
+ # here the head element succeeding head is not nil, but the head and tail are equal
83
+ # so tail is falling behind, update it, then start over
84
+ compare_and_set_tail(current_tail_node, current_head_node)
85
+ end
86
+
87
+ # the queue isn't empty
88
+ # if we can set the dummy head to the 'real' head, we're free to return the value in that real head, success
89
+ elsif compare_and_set_head(current_dummy_node, current_head_node)
90
+ # grab the item from the popped node
91
+ item = current_head_node.item
92
+
93
+ # return it, success!
94
+ return item
95
+ end
96
+ end
97
+ end
98
+ end
99
+
100
+ # approximate
101
+ def size
102
+ successor = head.successor
103
+ count = 0
104
+
105
+ while true
106
+ break if successor.nil?
107
+
108
+ current_node = successor
109
+ successor = current_node.successor
110
+ count += 1
111
+ end
112
+
113
+ count
114
+ end
115
+ end
116
+ end
@@ -1,100 +1,122 @@
1
1
  module Concurrent
2
- module Edge
3
- class LockFreeStack < Synchronization::Object
2
+ class LockFreeStack < Synchronization::Object
4
3
 
5
- safe_initialization!
4
+ safe_initialization!
6
5
 
7
- class Node
8
- attr_reader :value, :next_node
6
+ class Node
7
+ # TODO (pitr-ch 20-Dec-2016): Could be unified with Stack class?
9
8
 
10
- def initialize(value, next_node)
11
- @value = value
12
- @next_node = next_node
13
- end
9
+ attr_reader :value, :next_node
10
+ # allow to nil-ify to free GC when the entry is no longer relevant, not synchronised
11
+ attr_writer :value
14
12
 
15
- singleton_class.send :alias_method, :[], :new
13
+ def initialize(value, next_node)
14
+ @value = value
15
+ @next_node = next_node
16
16
  end
17
17
 
18
- class Empty < Node
19
- def next_node
20
- self
21
- end
18
+ singleton_class.send :alias_method, :[], :new
19
+ end
20
+
21
+ class Empty < Node
22
+ def next_node
23
+ self
22
24
  end
25
+ end
23
26
 
24
- EMPTY = Empty[nil, nil]
27
+ EMPTY = Empty[nil, nil]
25
28
 
26
- private(*attr_atomic(:head))
29
+ private(*attr_atomic(:head))
27
30
 
28
- def initialize
29
- super()
30
- self.head = EMPTY
31
- end
31
+ def self.of1(value)
32
+ new Node[value, EMPTY]
33
+ end
32
34
 
33
- def empty?
34
- head.equal? EMPTY
35
- end
35
+ def self.of2(value1, value2)
36
+ new Node[value1, Node[value2, EMPTY]]
37
+ end
36
38
 
37
- def compare_and_push(head, value)
38
- compare_and_set_head head, Node[value, head]
39
- end
39
+ def initialize(head = EMPTY)
40
+ super()
41
+ self.head = head
42
+ end
40
43
 
41
- def push(value)
42
- while true
43
- current_head = head
44
- return self if compare_and_set_head current_head, Node[value, current_head]
45
- end
46
- end
44
+ def empty?(head = self.head)
45
+ head.equal? EMPTY
46
+ end
47
47
 
48
- def peek
49
- head
50
- end
48
+ def compare_and_push(head, value)
49
+ compare_and_set_head head, Node[value, head]
50
+ end
51
51
 
52
- def compare_and_pop(head)
53
- compare_and_set_head head, head.next_node
52
+ def push(value)
53
+ while true
54
+ current_head = head
55
+ return self if compare_and_set_head current_head, Node[value, current_head]
54
56
  end
57
+ end
55
58
 
56
- def pop
57
- while true
58
- current_head = head
59
- return current_head.value if compare_and_set_head current_head, current_head.next_node
60
- end
61
- end
59
+ def peek
60
+ head
61
+ end
62
+
63
+ def compare_and_pop(head)
64
+ compare_and_set_head head, head.next_node
65
+ end
62
66
 
63
- def compare_and_clear(head)
64
- compare_and_set_head head, EMPTY
67
+ def pop
68
+ while true
69
+ current_head = head
70
+ return current_head.value if compare_and_set_head current_head, current_head.next_node
65
71
  end
72
+ end
66
73
 
67
- include Enumerable
74
+ def compare_and_clear(head)
75
+ compare_and_set_head head, EMPTY
76
+ end
68
77
 
69
- def each(head = nil)
70
- return to_enum(:each, head) unless block_given?
71
- it = head || peek
72
- until it.equal?(EMPTY)
73
- yield it.value
74
- it = it.next_node
75
- end
76
- self
78
+ include Enumerable
79
+
80
+ def each(head = nil)
81
+ return to_enum(:each, head) unless block_given?
82
+ it = head || peek
83
+ until it.equal?(EMPTY)
84
+ yield it.value
85
+ it = it.next_node
77
86
  end
87
+ self
88
+ end
78
89
 
79
- def clear
80
- while true
81
- current_head = head
82
- return false if current_head == EMPTY
83
- return true if compare_and_set_head current_head, EMPTY
84
- end
90
+ def clear
91
+ while true
92
+ current_head = head
93
+ return false if current_head == EMPTY
94
+ return true if compare_and_set_head current_head, EMPTY
85
95
  end
96
+ end
86
97
 
87
- def clear_each(&block)
88
- while true
89
- current_head = head
90
- return self if current_head == EMPTY
91
- if compare_and_set_head current_head, EMPTY
92
- each current_head, &block
93
- return self
94
- end
98
+ def clear_if(head)
99
+ compare_and_set_head head, EMPTY
100
+ end
101
+
102
+ def replace_if(head, new_head)
103
+ compare_and_set_head head, new_head
104
+ end
105
+
106
+ def clear_each(&block)
107
+ while true
108
+ current_head = head
109
+ return self if current_head == EMPTY
110
+ if compare_and_set_head current_head, EMPTY
111
+ each current_head, &block
112
+ return self
95
113
  end
96
114
  end
115
+ end
97
116
 
117
+ # @return [String] Short string representation.
118
+ def to_s
119
+ format '<#%s:0x%x %s>', self.class, object_id << 1, to_a.to_s
98
120
  end
99
121
  end
100
122
  end
@@ -0,0 +1,54 @@
1
+ module Concurrent
2
+ module Promises
3
+ module FactoryMethods
4
+
5
+ # @!visibility private
6
+
7
+ module OldChannelIntegration
8
+
9
+ # @!visibility private
10
+
11
+ # only proof of concept
12
+ # @return [Future]
13
+ def select(*channels)
14
+ # TODO (pitr-ch 26-Mar-2016): re-do, has to be non-blocking
15
+ future do
16
+ # noinspection RubyArgCount
17
+ Channel.select do |s|
18
+ channels.each do |ch|
19
+ s.take(ch) { |value| [value, ch] }
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+
26
+ include OldChannelIntegration
27
+ end
28
+
29
+ class Future < AbstractEventFuture
30
+
31
+ # @!visibility private
32
+
33
+ module OldChannelIntegration
34
+
35
+ # @!visibility private
36
+
37
+ # Zips with selected value form the suplied channels
38
+ # @return [Future]
39
+ def then_select(*channels)
40
+ future = Concurrent::Promises.select(*channels)
41
+ ZipFuturesPromise.new_blocked_by2(self, future, @DefaultExecutor).future
42
+ end
43
+
44
+ # @note may block
45
+ # @note only proof of concept
46
+ def then_put(channel)
47
+ on_fulfillment_using(:io, channel) { |value, channel| channel.put value }
48
+ end
49
+ end
50
+
51
+ include OldChannelIntegration
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,2080 @@
1
+ require 'concurrent/synchronization'
2
+ require 'concurrent/atomic/atomic_boolean'
3
+ require 'concurrent/atomic/atomic_fixnum'
4
+ require 'concurrent/edge/lock_free_stack'
5
+ require 'concurrent/errors'
6
+
7
+ module Concurrent
8
+
9
+
10
+ # {include:file:doc/promises-main.md}
11
+ module Promises
12
+
13
+ # TODO (pitr-ch 23-Dec-2016): move out
14
+ # @!visibility private
15
+ module ReInclude
16
+ def included(base)
17
+ included_into << [:include, base]
18
+ super(base)
19
+ end
20
+
21
+ def extended(base)
22
+ included_into << [:extend, base]
23
+ super(base)
24
+ end
25
+
26
+ def include(*modules)
27
+ super(*modules)
28
+ modules.reverse.each do |module_being_included|
29
+ included_into.each do |method, mod|
30
+ mod.send method, module_being_included
31
+ end
32
+ end
33
+ end
34
+
35
+ private
36
+
37
+ def included_into
38
+ @included_into ||= []
39
+ end
40
+ end
41
+
42
+ # @!macro [new] promises.param.default_executor
43
+ # @param [Executor, :io, :fast] default_executor Instance of an executor or a name of the
44
+ # global executor. Default executor propagates to chained futures unless overridden with
45
+ # executor parameter or changed with {AbstractEventFuture#with_default_executor}.
46
+ #
47
+ # @!macro [new] promises.param.executor
48
+ # @param [Executor, :io, :fast] executor Instance of an executor or a name of the
49
+ # global executor. The task is executed on it, default executor remains unchanged.
50
+ #
51
+ # @!macro [new] promises.param.args
52
+ # @param [Object] args arguments which are passed to the task when it's executed.
53
+ # (It might be prepended with other arguments, see the @yeild section).
54
+ #
55
+ # @!macro [new] promises.shortcut.on
56
+ # Shortcut of {#$0_on} with default `:io` executor supplied.
57
+ # @see #$0_on
58
+ #
59
+ # @!macro [new] promises.shortcut.using
60
+ # Shortcut of {#$0_using} with default `:io` executor supplied.
61
+ # @see #$0_using
62
+ #
63
+ # @!macro [new] promise.param.task-future
64
+ # @yieldreturn will become result of the returned Future.
65
+ # Its returned value becomes {Future#value} fulfilling it,
66
+ # raised exception becomes {Future#reason} rejecting it.
67
+ #
68
+ # @!macro [new] promise.param.callback
69
+ # @yieldreturn is forgotten.
70
+
71
+ # Container of all {Future}, {Event} factory methods. They are never constructed directly with
72
+ # new.
73
+ module FactoryMethods
74
+ extend ReInclude
75
+
76
+ # @!macro promises.shortcut.on
77
+ # @return [ResolvableEvent]
78
+ def resolvable_event
79
+ resolvable_event_on :io
80
+ end
81
+
82
+ # Created resolvable event, user is responsible for resolving the event once by
83
+ # {Promises::ResolvableEvent#resolve}.
84
+ #
85
+ # @!macro promises.param.default_executor
86
+ # @return [ResolvableEvent]
87
+ def resolvable_event_on(default_executor = :io)
88
+ ResolvableEventPromise.new(default_executor).future
89
+ end
90
+
91
+ # @!macro promises.shortcut.on
92
+ # @return [ResolvableFuture]
93
+ def resolvable_future
94
+ resolvable_future_on :io
95
+ end
96
+
97
+ # Creates resolvable future, user is responsible for resolving the future once by
98
+ # {Promises::ResolvableFuture#resolve}, {Promises::ResolvableFuture#fulfill},
99
+ # or {Promises::ResolvableFuture#reject}
100
+ #
101
+ # @!macro promises.param.default_executor
102
+ # @return [ResolvableFuture]
103
+ def resolvable_future_on(default_executor = :io)
104
+ ResolvableFuturePromise.new(default_executor).future
105
+ end
106
+
107
+ # @!macro promises.shortcut.on
108
+ # @return [Future]
109
+ def future(*args, &task)
110
+ future_on(:io, *args, &task)
111
+ end
112
+
113
+ # @!macro [new] promises.future-on1
114
+ # Constructs new Future which will be resolved after block is evaluated on default executor.
115
+ # Evaluation begins immediately.
116
+ #
117
+ # @!macro [new] promises.future-on2
118
+ # @!macro promises.param.default_executor
119
+ # @!macro promises.param.args
120
+ # @yield [*args] to the task.
121
+ # @!macro promise.param.task-future
122
+ # @return [Future]
123
+ def future_on(default_executor, *args, &task)
124
+ ImmediateEventPromise.new(default_executor).future.then(*args, &task)
125
+ end
126
+
127
+ # Creates resolved future with will be either fulfilled with the given value or rejection with
128
+ # the given reason.
129
+ #
130
+ # @!macro promises.param.default_executor
131
+ # @return [Future]
132
+ def resolved_future(fulfilled, value, reason, default_executor = :io)
133
+ ImmediateFuturePromise.new(default_executor, fulfilled, value, reason).future
134
+ end
135
+
136
+ # Creates resolved future with will be fulfilled with the given value.
137
+ #
138
+ # @!macro promises.param.default_executor
139
+ # @return [Future]
140
+ def fulfilled_future(value, default_executor = :io)
141
+ resolved_future true, value, nil, default_executor
142
+ end
143
+
144
+ # Creates resolved future with will be rejected with the given reason.
145
+ #
146
+ # @!macro promises.param.default_executor
147
+ # @return [Future]
148
+ def rejected_future(reason, default_executor = :io)
149
+ resolved_future false, nil, reason, default_executor
150
+ end
151
+
152
+ # Creates resolved event.
153
+ #
154
+ # @!macro promises.param.default_executor
155
+ # @return [Event]
156
+ def resolved_event(default_executor = :io)
157
+ ImmediateEventPromise.new(default_executor).event
158
+ end
159
+
160
+ # General constructor. Behaves differently based on the argument's type. It's provided for convenience
161
+ # but it's better to be explicit.
162
+ #
163
+ # @see rejected_future, resolved_event, fulfilled_future
164
+ # @!macro promises.param.default_executor
165
+ # @return [Event, Future]
166
+ #
167
+ # @overload create(nil, default_executor = :io)
168
+ # @param [nil] nil
169
+ # @return [Event] resolved event.
170
+ #
171
+ # @overload create(a_future, default_executor = :io)
172
+ # @param [Future] a_future
173
+ # @return [Future] a future which will be resolved when a_future is.
174
+ #
175
+ # @overload create(an_event, default_executor = :io)
176
+ # @param [Event] an_event
177
+ # @return [Event] an event which will be resolved when an_event is.
178
+ #
179
+ # @overload create(exception, default_executor = :io)
180
+ # @param [Exception] exception
181
+ # @return [Future] a rejected future with the exception as its reason.
182
+ #
183
+ # @overload create(value, default_executor = :io)
184
+ # @param [Object] value when none of the above overloads fits
185
+ # @return [Future] a fulfilled future with the value.
186
+ def create(argument = nil, default_executor = :io)
187
+ case argument
188
+ when AbstractEventFuture
189
+ # returning wrapper would change nothing
190
+ argument
191
+ when Exception
192
+ rejected_future argument, default_executor
193
+ when nil
194
+ resolved_event default_executor
195
+ else
196
+ fulfilled_future argument, default_executor
197
+ end
198
+ end
199
+
200
+ # @!macro promises.shortcut.on
201
+ # @return [Future]
202
+ def delay(*args, &task)
203
+ delay_on :io, *args, &task
204
+ end
205
+
206
+ # @!macro promises.future-on1
207
+ # The task will be evaluated only after the future is touched, see {AbstractEventFuture#touch}
208
+ #
209
+ # @!macro promises.future-on2
210
+ def delay_on(default_executor, *args, &task)
211
+ DelayPromise.new(default_executor).event.chain(*args, &task)
212
+ end
213
+
214
+ # @!macro promises.shortcut.on
215
+ # @return [Future]
216
+ def schedule(intended_time, *args, &task)
217
+ schedule_on :io, intended_time, *args, &task
218
+ end
219
+
220
+ # @!macro promises.future-on1
221
+ # The task is planned for execution in intended_time.
222
+ #
223
+ # @!macro promises.future-on2
224
+ # @!macro [new] promises.param.intended_time
225
+ # @param [Numeric, Time] intended_time `Numeric` means to run in `intended_time` seconds.
226
+ # `Time` means to run on `intended_time`.
227
+ def schedule_on(default_executor, intended_time, *args, &task)
228
+ ScheduledPromise.new(default_executor, intended_time).event.chain(*args, &task)
229
+ end
230
+
231
+ # @!macro promises.shortcut.on
232
+ # @return [Future]
233
+ def zip_futures(*futures_and_or_events)
234
+ zip_futures_on :io, *futures_and_or_events
235
+ end
236
+
237
+ # Creates new future which is resolved after all futures_and_or_events are resolved.
238
+ # Its value is array of zipped future values. Its reason is array of reasons for rejection.
239
+ # If there is an error it rejects.
240
+ # @!macro [new] promises.event-conversion
241
+ # If event is supplied, which does not have value and can be only resolved, it's
242
+ # represented as `:fulfilled` with value `nil`.
243
+ #
244
+ # @!macro promises.param.default_executor
245
+ # @param [AbstractEventFuture] futures_and_or_events
246
+ # @return [Future]
247
+ def zip_futures_on(default_executor, *futures_and_or_events)
248
+ ZipFuturesPromise.new_blocked_by(futures_and_or_events, default_executor).future
249
+ end
250
+
251
+ alias_method :zip, :zip_futures
252
+
253
+ # @!macro promises.shortcut.on
254
+ # @return [Event]
255
+ def zip_events(*futures_and_or_events)
256
+ zip_events_on :io, *futures_and_or_events
257
+ end
258
+
259
+ # Creates new event which is resolved after all futures_and_or_events are resolved.
260
+ # (Future is resolved when fulfilled or rejected.)
261
+ #
262
+ # @!macro promises.param.default_executor
263
+ # @param [AbstractEventFuture] futures_and_or_events
264
+ # @return [Event]
265
+ def zip_events_on(default_executor, *futures_and_or_events)
266
+ ZipEventsPromise.new_blocked_by(futures_and_or_events, default_executor).event
267
+ end
268
+
269
+ # @!macro promises.shortcut.on
270
+ # @return [Future]
271
+ def any_resolved_future(*futures_and_or_events)
272
+ any_resolved_future_on :io, *futures_and_or_events
273
+ end
274
+
275
+ alias_method :any, :any_resolved_future
276
+
277
+ # Creates new future which is resolved after first futures_and_or_events is resolved.
278
+ # Its result equals result of the first resolved future.
279
+ # @!macro [new] promises.any-touch
280
+ # If resolved it does not propagate {AbstractEventFuture#touch}, leaving delayed
281
+ # futures un-executed if they are not required any more.
282
+ # @!macro promises.event-conversion
283
+ #
284
+ # @!macro promises.param.default_executor
285
+ # @param [AbstractEventFuture] futures_and_or_events
286
+ # @return [Future]
287
+ def any_resolved_future_on(default_executor, *futures_and_or_events)
288
+ AnyResolvedFuturePromise.new_blocked_by(futures_and_or_events, default_executor).future
289
+ end
290
+
291
+ # @!macro promises.shortcut.on
292
+ # @return [Future]
293
+ def any_fulfilled_future(*futures_and_or_events)
294
+ any_fulfilled_future_on :io, *futures_and_or_events
295
+ end
296
+
297
+ # Creates new future which is resolved after first of futures_and_or_events is fulfilled.
298
+ # Its result equals result of the first resolved future or if all futures_and_or_events reject,
299
+ # it has reason of the last resolved future.
300
+ # @!macro promises.any-touch
301
+ # @!macro promises.event-conversion
302
+ #
303
+ # @!macro promises.param.default_executor
304
+ # @param [AbstractEventFuture] futures_and_or_events
305
+ # @return [Future]
306
+ def any_fulfilled_future_on(default_executor, *futures_and_or_events)
307
+ AnyFulfilledFuturePromise.new_blocked_by(futures_and_or_events, default_executor).future
308
+ end
309
+
310
+ # @!macro promises.shortcut.on
311
+ # @return [Future]
312
+ def any_event(*futures_and_or_events)
313
+ any_event_on :io, *futures_and_or_events
314
+ end
315
+
316
+ # Creates new event which becomes resolved after first of the futures_and_or_events resolves.
317
+ # @!macro promises.any-touch
318
+ #
319
+ # @!macro promises.param.default_executor
320
+ # @param [AbstractEventFuture] futures_and_or_events
321
+ # @return [Event]
322
+ def any_event_on(default_executor, *futures_and_or_events)
323
+ AnyResolvedEventPromise.new_blocked_by(futures_and_or_events, default_executor).event
324
+ end
325
+
326
+ # TODO consider adding first(count, *futures)
327
+ # TODO consider adding zip_by(slice, *futures) processing futures in slices
328
+ end
329
+
330
+ module InternalStates
331
+ # @private
332
+ class State
333
+ def resolved?
334
+ raise NotImplementedError
335
+ end
336
+
337
+ def to_sym
338
+ raise NotImplementedError
339
+ end
340
+ end
341
+
342
+ private_constant :State
343
+
344
+ # @private
345
+ class Pending < State
346
+ def resolved?
347
+ false
348
+ end
349
+
350
+ def to_sym
351
+ :pending
352
+ end
353
+ end
354
+
355
+ private_constant :Pending
356
+
357
+ # @private
358
+ class ResolvedWithResult < State
359
+ def resolved?
360
+ true
361
+ end
362
+
363
+ def to_sym
364
+ :resolved
365
+ end
366
+
367
+ def result
368
+ [fulfilled?, value, reason]
369
+ end
370
+
371
+ def fulfilled?
372
+ raise NotImplementedError
373
+ end
374
+
375
+ def value
376
+ raise NotImplementedError
377
+ end
378
+
379
+ def reason
380
+ raise NotImplementedError
381
+ end
382
+
383
+ def apply
384
+ raise NotImplementedError
385
+ end
386
+ end
387
+
388
+ private_constant :ResolvedWithResult
389
+
390
+ # @private
391
+ class Fulfilled < ResolvedWithResult
392
+
393
+ def initialize(value)
394
+ @Value = value
395
+ end
396
+
397
+ def fulfilled?
398
+ true
399
+ end
400
+
401
+ def apply(args, block)
402
+ block.call value, *args
403
+ end
404
+
405
+ def value
406
+ @Value
407
+ end
408
+
409
+ def reason
410
+ nil
411
+ end
412
+
413
+ def to_sym
414
+ :fulfilled
415
+ end
416
+ end
417
+
418
+ private_constant :Fulfilled
419
+
420
+ # @private
421
+ class FulfilledArray < Fulfilled
422
+ def apply(args, block)
423
+ block.call(*value, *args)
424
+ end
425
+ end
426
+
427
+ private_constant :FulfilledArray
428
+
429
+ # @private
430
+ class Rejected < ResolvedWithResult
431
+ def initialize(reason)
432
+ @Reason = reason
433
+ end
434
+
435
+ def fulfilled?
436
+ false
437
+ end
438
+
439
+ def value
440
+ nil
441
+ end
442
+
443
+ def reason
444
+ @Reason
445
+ end
446
+
447
+ def to_sym
448
+ :rejected
449
+ end
450
+
451
+ def apply(args, block)
452
+ block.call reason, *args
453
+ end
454
+ end
455
+
456
+ private_constant :Rejected
457
+
458
+ # @private
459
+ class PartiallyRejected < ResolvedWithResult
460
+ def initialize(value, reason)
461
+ super()
462
+ @Value = value
463
+ @Reason = reason
464
+ end
465
+
466
+ def fulfilled?
467
+ false
468
+ end
469
+
470
+ def to_sym
471
+ :rejected
472
+ end
473
+
474
+ def value
475
+ @Value
476
+ end
477
+
478
+ def reason
479
+ @Reason
480
+ end
481
+
482
+ def apply(args, block)
483
+ block.call(*reason, *args)
484
+ end
485
+ end
486
+
487
+ private_constant :PartiallyRejected
488
+
489
+ PENDING = Pending.new
490
+ RESOLVED = Fulfilled.new(nil)
491
+
492
+ def RESOLVED.to_sym
493
+ :resolved
494
+ end
495
+
496
+ private_constant :PENDING, :RESOLVED
497
+ end
498
+
499
+ private_constant :InternalStates
500
+
501
+ # Common ancestor of {Event} and {Future} classes, many shared methods are defined here.
502
+ class AbstractEventFuture < Synchronization::Object
503
+ safe_initialization!
504
+ private(*attr_atomic(:internal_state) - [:internal_state])
505
+
506
+ include InternalStates
507
+
508
+ def initialize(promise, default_executor)
509
+ super()
510
+ @Lock = Mutex.new
511
+ @Condition = ConditionVariable.new
512
+ @Promise = promise
513
+ @DefaultExecutor = default_executor
514
+ @Callbacks = LockFreeStack.new
515
+ # noinspection RubyArgCount
516
+ @Waiters = AtomicFixnum.new 0
517
+ self.internal_state = PENDING
518
+ end
519
+
520
+ private :initialize
521
+
522
+ # @!macro [new] promises.shortcut.event-future
523
+ # @see Event#$0
524
+ # @see Future#$0
525
+
526
+ # @!macro [new] promises.param.timeout
527
+ # @param [Numeric] timeout the maximum time in second to wait.
528
+
529
+ # @!macro [new] promises.warn.blocks
530
+ # @note This function potentially blocks current thread until the Future is resolved.
531
+ # Be careful it can deadlock. Try to chain instead.
532
+
533
+ # Returns its state.
534
+ # @return [Symbol]
535
+ #
536
+ # @overload an_event.state
537
+ # @return [:pending, :resolved]
538
+ # @overload a_future.state
539
+ # Both :fulfilled, :rejected implies :resolved.
540
+ # @return [:pending, :fulfilled, :rejected]
541
+ def state
542
+ internal_state.to_sym
543
+ end
544
+
545
+ # Is it in pending state?
546
+ # @return [Boolean]
547
+ def pending?(state = internal_state)
548
+ !state.resolved?
549
+ end
550
+
551
+ # Is it in resolved state?
552
+ # @return [Boolean]
553
+ def resolved?(state = internal_state)
554
+ state.resolved?
555
+ end
556
+
557
+ # Propagates touch. Requests all the delayed futures, which it depends on, to be
558
+ # executed. This method is called by any other method requiring resolved state, like {#wait}.
559
+ # @return [self]
560
+ def touch
561
+ @Promise.touch
562
+ self
563
+ end
564
+
565
+ # @!macro [new] promises.touches
566
+ # Calls {AbstractEventFuture#touch}.
567
+
568
+ # @!macro [new] promises.method.wait
569
+ # Wait (block the Thread) until receiver is {#resolved?}.
570
+ # @!macro promises.touches
571
+ #
572
+ # @!macro promises.warn.blocks
573
+ # @!macro promises.param.timeout
574
+ # @return [Future, true, false] self implies timeout was not used, true implies timeout was used
575
+ # and it was resolved, false implies it was not resolved within timeout.
576
+ def wait(timeout = nil)
577
+ result = wait_until_resolved(timeout)
578
+ timeout ? result : self
579
+ end
580
+
581
+ # Returns default executor.
582
+ # @return [Executor] default executor
583
+ # @see #with_default_executor
584
+ # @see FactoryMethods#future_on
585
+ # @see FactoryMethods#resolvable_future
586
+ # @see FactoryMethods#any_fulfilled_future_on
587
+ # @see similar
588
+ def default_executor
589
+ @DefaultExecutor
590
+ end
591
+
592
+ # @!macro promises.shortcut.on
593
+ # @return [Future]
594
+ def chain(*args, &task)
595
+ chain_on @DefaultExecutor, *args, &task
596
+ end
597
+
598
+ # Chains the task to be executed asynchronously on executor after it is resolved.
599
+ #
600
+ # @!macro promises.param.executor
601
+ # @!macro promises.param.args
602
+ # @return [Future]
603
+ # @!macro promise.param.task-future
604
+ #
605
+ # @overload an_event.chain_on(executor, *args, &task)
606
+ # @yield [*args] to the task.
607
+ # @overload a_future.chain_on(executor, *args, &task)
608
+ # @yield [fulfilled, value, reason, *args] to the task.
609
+ # @yieldparam [true, false] fulfilled
610
+ # @yieldparam [Object] value
611
+ # @yieldparam [Exception] reason
612
+ def chain_on(executor, *args, &task)
613
+ ChainPromise.new_blocked_by1(self, @DefaultExecutor, executor, args, &task).future
614
+ end
615
+
616
+ # @return [String] Short string representation.
617
+ def to_s
618
+ format '<#%s:0x%x %s>', self.class, object_id << 1, state
619
+ end
620
+
621
+ alias_method :inspect, :to_s
622
+
623
+ # Resolves the resolvable when receiver is resolved.
624
+ #
625
+ # @param [Resolvable] resolvable
626
+ # @return [self]
627
+ def chain_resolvable(resolvable)
628
+ on_resolution! { resolvable.resolve_with internal_state }
629
+ end
630
+
631
+ alias_method :tangle, :chain_resolvable
632
+
633
+ # @!macro promises.shortcut.using
634
+ # @return [self]
635
+ def on_resolution(*args, &callback)
636
+ on_resolution_using @DefaultExecutor, *args, &callback
637
+ end
638
+
639
+ # Stores the callback to be executed synchronously on resolving thread after it is
640
+ # resolved.
641
+ #
642
+ # @!macro promises.param.args
643
+ # @!macro promise.param.callback
644
+ # @return [self]
645
+ #
646
+ # @overload an_event.on_resolution!(*args, &callback)
647
+ # @yield [*args] to the callback.
648
+ # @overload a_future.on_resolution!(*args, &callback)
649
+ # @yield [fulfilled, value, reason, *args] to the callback.
650
+ # @yieldparam [true, false] fulfilled
651
+ # @yieldparam [Object] value
652
+ # @yieldparam [Exception] reason
653
+ def on_resolution!(*args, &callback)
654
+ add_callback :callback_on_resolution, args, callback
655
+ end
656
+
657
+ # Stores the callback to be executed asynchronously on executor after it is resolved.
658
+ #
659
+ # @!macro promises.param.executor
660
+ # @!macro promises.param.args
661
+ # @!macro promise.param.callback
662
+ # @return [self]
663
+ #
664
+ # @overload an_event.on_resolution_using(executor, *args, &callback)
665
+ # @yield [*args] to the callback.
666
+ # @overload a_future.on_resolution_using(executor, *args, &callback)
667
+ # @yield [fulfilled, value, reason, *args] to the callback.
668
+ # @yieldparam [true, false] fulfilled
669
+ # @yieldparam [Object] value
670
+ # @yieldparam [Exception] reason
671
+ def on_resolution_using(executor, *args, &callback)
672
+ add_callback :async_callback_on_resolution, executor, args, callback
673
+ end
674
+
675
+ # @!macro [new] promises.method.with_default_executor
676
+ # Crates new object with same class with the executor set as its new default executor.
677
+ # Any futures depending on it will use the new default executor.
678
+ # @!macro promises.shortcut.event-future
679
+ # @abstract
680
+ # @return [AbstractEventFuture]
681
+ def with_default_executor(executor)
682
+ raise NotImplementedError
683
+ end
684
+
685
+ # @!visibility private
686
+ def resolve_with(state, raise_on_reassign = true)
687
+ if compare_and_set_internal_state(PENDING, state)
688
+ # go to synchronized block only if there were waiting threads
689
+ @Lock.synchronize { @Condition.broadcast } unless @Waiters.value == 0
690
+ call_callbacks state
691
+ else
692
+ return rejected_resolution(raise_on_reassign, state)
693
+ end
694
+ self
695
+ end
696
+
697
+ # For inspection.
698
+ # @!visibility private
699
+ # @return [Array<AbstractPromise>]
700
+ def blocks
701
+ @Callbacks.each_with_object([]) do |(method, args), promises|
702
+ promises.push(args[0]) if method == :callback_notify_blocked
703
+ end
704
+ end
705
+
706
+ # For inspection.
707
+ # @!visibility private
708
+ def callbacks
709
+ @Callbacks.each.to_a
710
+ end
711
+
712
+ # For inspection.
713
+ # @!visibility private
714
+ def promise
715
+ @Promise
716
+ end
717
+
718
+ # For inspection.
719
+ # @!visibility private
720
+ def touched?
721
+ promise.touched?
722
+ end
723
+
724
+ # For inspection.
725
+ # @!visibility private
726
+ def waiting_threads
727
+ @Waiters.each.to_a
728
+ end
729
+
730
+ # @!visibility private
731
+ def add_callback(method, *args)
732
+ state = internal_state
733
+ if resolved?(state)
734
+ call_callback method, state, args
735
+ else
736
+ @Callbacks.push [method, args]
737
+ state = internal_state
738
+ # take back if it was resolved in the meanwhile
739
+ call_callbacks state if resolved?(state)
740
+ end
741
+ self
742
+ end
743
+
744
+ private
745
+
746
+ # @return [Boolean]
747
+ def wait_until_resolved(timeout)
748
+ return true if resolved?
749
+
750
+ touch
751
+
752
+ @Lock.synchronize do
753
+ @Waiters.increment
754
+ begin
755
+ unless resolved?
756
+ @Condition.wait @Lock, timeout
757
+ end
758
+ ensure
759
+ # JRuby may raise ConcurrencyError
760
+ @Waiters.decrement
761
+ end
762
+ end
763
+ resolved?
764
+ end
765
+
766
+ def call_callback(method, state, args)
767
+ self.send method, state, *args
768
+ end
769
+
770
+ def call_callbacks(state)
771
+ method, args = @Callbacks.pop
772
+ while method
773
+ call_callback method, state, args
774
+ method, args = @Callbacks.pop
775
+ end
776
+ end
777
+
778
+ def with_async(executor, *args, &block)
779
+ Concurrent.executor(executor).post(*args, &block)
780
+ end
781
+
782
+ def async_callback_on_resolution(state, executor, args, callback)
783
+ with_async(executor, state, args, callback) do |st, ar, cb|
784
+ callback_on_resolution st, ar, cb
785
+ end
786
+ end
787
+
788
+ def callback_notify_blocked(state, promise, index)
789
+ promise.on_blocker_resolution self, index
790
+ end
791
+ end
792
+
793
+ # Represents an event which will happen in future (will be resolved). The event is either
794
+ # pending or resolved. It should be always resolved. Use {Future} to communicate rejections and
795
+ # cancellation.
796
+ class Event < AbstractEventFuture
797
+
798
+ alias_method :then, :chain
799
+
800
+
801
+ # @!macro [new] promises.method.zip
802
+ # Creates a new event or a future which will be resolved when receiver and other are.
803
+ # Returns an event if receiver and other are events, otherwise returns a future.
804
+ # If just one of the parties is Future then the result
805
+ # of the returned future is equal to the result of the supplied future. If both are futures
806
+ # then the result is as described in {FactoryMethods#zip_futures_on}.
807
+ #
808
+ # @return [Future, Event]
809
+ def zip(other)
810
+ if other.is_a?(Future)
811
+ ZipFutureEventPromise.new_blocked_by2(other, self, @DefaultExecutor).future
812
+ else
813
+ ZipEventEventPromise.new_blocked_by2(self, other, @DefaultExecutor).event
814
+ end
815
+ end
816
+
817
+ alias_method :&, :zip
818
+
819
+ # Creates a new event which will be resolved when the first of receiver, `event_or_future`
820
+ # resolves.
821
+ #
822
+ # @return [Event]
823
+ def any(event_or_future)
824
+ AnyResolvedEventPromise.new_blocked_by2(self, event_or_future, @DefaultExecutor).event
825
+ end
826
+
827
+ alias_method :|, :any
828
+
829
+ # Creates new event dependent on receiver which will not evaluate until touched, see {#touch}.
830
+ # In other words, it inserts delay into the chain of Futures making rest of it lazy evaluated.
831
+ #
832
+ # @return [Event]
833
+ def delay
834
+ event = DelayPromise.new(@DefaultExecutor).event
835
+ ZipEventEventPromise.new_blocked_by2(self, event, @DefaultExecutor).event
836
+ end
837
+
838
+ # @!macro [new] promise.method.schedule
839
+ # Creates new event dependent on receiver scheduled to execute on/in intended_time.
840
+ # In time is interpreted from the moment the receiver is resolved, therefore it inserts
841
+ # delay into the chain.
842
+ #
843
+ # @!macro promises.param.intended_time
844
+ # @return [Event]
845
+ def schedule(intended_time)
846
+ chain do
847
+ event = ScheduledPromise.new(@DefaultExecutor, intended_time).event
848
+ ZipEventEventPromise.new_blocked_by2(self, event, @DefaultExecutor).event
849
+ end.flat_event
850
+ end
851
+
852
+ # Converts event to a future. The future is fulfilled when the event is resolved, the future may never fail.
853
+ #
854
+ # @return [Future]
855
+ def to_future
856
+ future = Promises.resolvable_future
857
+ ensure
858
+ chain_resolvable(future)
859
+ end
860
+
861
+ # Returns self, since this is event
862
+ # @return [Event]
863
+ def to_event
864
+ self
865
+ end
866
+
867
+ # @!macro promises.method.with_default_executor
868
+ # @return [Event]
869
+ def with_default_executor(executor)
870
+ EventWrapperPromise.new_blocked_by1(self, executor).event
871
+ end
872
+
873
+ private
874
+
875
+ def rejected_resolution(raise_on_reassign, state)
876
+ Concurrent::MultipleAssignmentError.new('Event can be resolved only once') if raise_on_reassign
877
+ return false
878
+ end
879
+
880
+ def callback_on_resolution(state, args, callback)
881
+ callback.call *args
882
+ end
883
+ end
884
+
885
+ # Represents a value which will become available in future. May reject with a reason instead,
886
+ # e.g. when the tasks raises an exception.
887
+ class Future < AbstractEventFuture
888
+
889
+ # Is it in fulfilled state?
890
+ # @return [Boolean]
891
+ def fulfilled?(state = internal_state)
892
+ state.resolved? && state.fulfilled?
893
+ end
894
+
895
+ # Is it in rejected state?
896
+ # @return [Boolean]
897
+ def rejected?(state = internal_state)
898
+ state.resolved? && !state.fulfilled?
899
+ end
900
+
901
+ # @!macro [new] promises.warn.nil
902
+ # @note Make sure returned `nil` is not confused with timeout, no value when rejected,
903
+ # no reason when fulfilled, etc.
904
+ # Use more exact methods if needed, like {#wait}, {#value!}, {#result}, etc.
905
+
906
+ # @!macro [new] promises.method.value
907
+ # Return value of the future.
908
+ # @!macro promises.touches
909
+ #
910
+ # @!macro promises.warn.blocks
911
+ # @!macro promises.warn.nil
912
+ # @!macro promises.param.timeout
913
+ # @return [Object, nil] the value of the Future when fulfilled, nil on timeout or rejection.
914
+ def value(timeout = nil)
915
+ internal_state.value if wait_until_resolved timeout
916
+ end
917
+
918
+ # Returns reason of future's rejection.
919
+ # @!macro promises.touches
920
+ #
921
+ # @!macro promises.warn.blocks
922
+ # @!macro promises.warn.nil
923
+ # @!macro promises.param.timeout
924
+ # @return [Exception, nil] nil on timeout or fulfillment.
925
+ def reason(timeout = nil)
926
+ internal_state.reason if wait_until_resolved timeout
927
+ end
928
+
929
+ # Returns triplet fulfilled?, value, reason.
930
+ # @!macro promises.touches
931
+ #
932
+ # @!macro promises.warn.blocks
933
+ # @!macro promises.param.timeout
934
+ # @return [Array(Boolean, Object, Exception), nil] triplet of fulfilled?, value, reason, or nil
935
+ # on timeout.
936
+ def result(timeout = nil)
937
+ internal_state.result if wait_until_resolved timeout
938
+ end
939
+
940
+ # @!macro promises.method.wait
941
+ # @raise [Exception] {#reason} on rejection
942
+ def wait!(timeout = nil)
943
+ result = wait_until_resolved!(timeout)
944
+ timeout ? result : self
945
+ end
946
+
947
+ # @!macro promises.method.value
948
+ # @return [Object, nil] the value of the Future when fulfilled, nil on timeout.
949
+ # @raise [Exception] {#reason} on rejection
950
+ def value!(timeout = nil)
951
+ internal_state.value if wait_until_resolved! timeout
952
+ end
953
+
954
+ # Allows rejected Future to be risen with `raise` method.
955
+ # @example
956
+ # raise Promises.rejected_future(StandardError.new("boom"))
957
+ # @raise [StandardError] when raising not rejected future
958
+ # @return [Exception]
959
+ def exception(*args)
960
+ raise Concurrent::Error, 'it is not rejected' unless rejected?
961
+ reason = Array(internal_state.reason).compact
962
+ if reason.size > 1
963
+ Concurrent::MultipleErrors.new reason
964
+ else
965
+ reason[0].exception(*args)
966
+ end
967
+ end
968
+
969
+ # @!macro promises.shortcut.on
970
+ # @return [Future]
971
+ def then(*args, &task)
972
+ then_on @DefaultExecutor, *args, &task
973
+ end
974
+
975
+ # Chains the task to be executed asynchronously on executor after it fulfills. Does not run
976
+ # the task if it rejects. It will resolve though, triggering any dependent futures.
977
+ #
978
+ # @!macro promises.param.executor
979
+ # @!macro promises.param.args
980
+ # @!macro promise.param.task-future
981
+ # @return [Future]
982
+ # @yield [value, *args] to the task.
983
+ def then_on(executor, *args, &task)
984
+ ThenPromise.new_blocked_by1(self, @DefaultExecutor, executor, args, &task).future
985
+ end
986
+
987
+ # @!macro promises.shortcut.on
988
+ # @return [Future]
989
+ def rescue(*args, &task)
990
+ rescue_on @DefaultExecutor, *args, &task
991
+ end
992
+
993
+ # Chains the task to be executed asynchronously on executor after it rejects. Does not run
994
+ # the task if it fulfills. It will resolve though, triggering any dependent futures.
995
+ #
996
+ # @!macro promises.param.executor
997
+ # @!macro promises.param.args
998
+ # @!macro promise.param.task-future
999
+ # @return [Future]
1000
+ # @yield [reason, *args] to the task.
1001
+ def rescue_on(executor, *args, &task)
1002
+ RescuePromise.new_blocked_by1(self, @DefaultExecutor, executor, args, &task).future
1003
+ end
1004
+
1005
+ # @!macro promises.method.zip
1006
+ # @return [Future]
1007
+ def zip(other)
1008
+ if other.is_a?(Future)
1009
+ ZipFuturesPromise.new_blocked_by2(self, other, @DefaultExecutor).future
1010
+ else
1011
+ ZipFutureEventPromise.new_blocked_by2(self, other, @DefaultExecutor).future
1012
+ end
1013
+ end
1014
+
1015
+ alias_method :&, :zip
1016
+
1017
+ # Creates a new event which will be resolved when the first of receiver, `event_or_future`
1018
+ # resolves. Returning future will have value nil if event_or_future is event and resolves
1019
+ # first.
1020
+ #
1021
+ # @return [Future]
1022
+ def any(event_or_future)
1023
+ AnyResolvedFuturePromise.new_blocked_by2(self, event_or_future, @DefaultExecutor).future
1024
+ end
1025
+
1026
+ alias_method :|, :any
1027
+
1028
+ # Creates new future dependent on receiver which will not evaluate until touched, see {#touch}.
1029
+ # In other words, it inserts delay into the chain of Futures making rest of it lazy evaluated.
1030
+ #
1031
+ # @return [Future]
1032
+ def delay
1033
+ event = DelayPromise.new(@DefaultExecutor).event
1034
+ ZipFutureEventPromise.new_blocked_by2(self, event, @DefaultExecutor).future
1035
+ end
1036
+
1037
+ # @!macro promise.method.schedule
1038
+ # @return [Future]
1039
+ def schedule(intended_time)
1040
+ chain do
1041
+ event = ScheduledPromise.new(@DefaultExecutor, intended_time).event
1042
+ ZipFutureEventPromise.new_blocked_by2(self, event, @DefaultExecutor).future
1043
+ end.flat
1044
+ end
1045
+
1046
+ # @!macro promises.method.with_default_executor
1047
+ # @return [Future]
1048
+ def with_default_executor(executor)
1049
+ FutureWrapperPromise.new_blocked_by1(self, executor).future
1050
+ end
1051
+
1052
+ # Creates new future which will have result of the future returned by receiver. If receiver
1053
+ # rejects it will have its rejection.
1054
+ #
1055
+ # @param [Integer] level how many levels of futures should flatten
1056
+ # @return [Future]
1057
+ def flat_future(level = 1)
1058
+ FlatFuturePromise.new_blocked_by1(self, level, @DefaultExecutor).future
1059
+ end
1060
+
1061
+ alias_method :flat, :flat_future
1062
+
1063
+ # Creates new event which will be resolved when the returned event by receiver is.
1064
+ # Be careful if the receiver rejects it will just resolve since Event does not hold reason.
1065
+ #
1066
+ # @return [Event]
1067
+ def flat_event
1068
+ FlatEventPromise.new_blocked_by1(self, @DefaultExecutor).event
1069
+ end
1070
+
1071
+ # @!macro promises.shortcut.using
1072
+ # @return [self]
1073
+ def on_fulfillment(*args, &callback)
1074
+ on_fulfillment_using @DefaultExecutor, *args, &callback
1075
+ end
1076
+
1077
+ # Stores the callback to be executed synchronously on resolving thread after it is
1078
+ # fulfilled. Does nothing on rejection.
1079
+ #
1080
+ # @!macro promises.param.args
1081
+ # @!macro promise.param.callback
1082
+ # @return [self]
1083
+ # @yield [value, *args] to the callback.
1084
+ def on_fulfillment!(*args, &callback)
1085
+ add_callback :callback_on_fulfillment, args, callback
1086
+ end
1087
+
1088
+ # Stores the callback to be executed asynchronously on executor after it is
1089
+ # fulfilled. Does nothing on rejection.
1090
+ #
1091
+ # @!macro promises.param.executor
1092
+ # @!macro promises.param.args
1093
+ # @!macro promise.param.callback
1094
+ # @return [self]
1095
+ # @yield [value, *args] to the callback.
1096
+ def on_fulfillment_using(executor, *args, &callback)
1097
+ add_callback :async_callback_on_fulfillment, executor, args, callback
1098
+ end
1099
+
1100
+ # @!macro promises.shortcut.using
1101
+ # @return [self]
1102
+ def on_rejection(*args, &callback)
1103
+ on_rejection_using @DefaultExecutor, *args, &callback
1104
+ end
1105
+
1106
+ # Stores the callback to be executed synchronously on resolving thread after it is
1107
+ # rejected. Does nothing on fulfillment.
1108
+ #
1109
+ # @!macro promises.param.args
1110
+ # @!macro promise.param.callback
1111
+ # @return [self]
1112
+ # @yield [reason, *args] to the callback.
1113
+ def on_rejection!(*args, &callback)
1114
+ add_callback :callback_on_rejection, args, callback
1115
+ end
1116
+
1117
+ # Stores the callback to be executed asynchronously on executor after it is
1118
+ # rejected. Does nothing on fulfillment.
1119
+ #
1120
+ # @!macro promises.param.executor
1121
+ # @!macro promises.param.args
1122
+ # @!macro promise.param.callback
1123
+ # @return [self]
1124
+ # @yield [reason, *args] to the callback.
1125
+ def on_rejection_using(executor, *args, &callback)
1126
+ add_callback :async_callback_on_rejection, executor, args, callback
1127
+ end
1128
+
1129
+ # Allows to use futures as green threads. The receiver has to evaluate to a future which
1130
+ # represents what should be done next. It basically flattens indefinitely until non Future
1131
+ # values is returned which becomes result of the returned future. Any encountered exception
1132
+ # will become reason of the returned future.
1133
+ #
1134
+ # @return [Future]
1135
+ # @example
1136
+ # body = lambda do |v|
1137
+ # v += 1
1138
+ # v < 5 ? Promises.future(v, &body) : v
1139
+ # end
1140
+ # Promises.future(0, &body).run.value! # => 5
1141
+ def run
1142
+ RunFuturePromise.new_blocked_by1(self, @DefaultExecutor).future
1143
+ end
1144
+
1145
+ # @!visibility private
1146
+ def apply(args, block)
1147
+ internal_state.apply args, block
1148
+ end
1149
+
1150
+ # Converts future to event which is resolved when future is resolved by fulfillment or rejection.
1151
+ #
1152
+ # @return [Event]
1153
+ def to_event
1154
+ event = Promises.resolvable_event
1155
+ ensure
1156
+ chain_resolvable(event)
1157
+ end
1158
+
1159
+ # Returns self, since this is a future
1160
+ # @return [Future]
1161
+ def to_future
1162
+ self
1163
+ end
1164
+
1165
+ private
1166
+
1167
+ def rejected_resolution(raise_on_reassign, state)
1168
+ if raise_on_reassign
1169
+ raise Concurrent::MultipleAssignmentError.new(
1170
+ "Future can be resolved only once. It's #{result}, trying to set #{state.result}.",
1171
+ current_result: result, new_result: state.result)
1172
+ end
1173
+ return false
1174
+ end
1175
+
1176
+ def wait_until_resolved!(timeout = nil)
1177
+ result = wait_until_resolved(timeout)
1178
+ raise self if rejected?
1179
+ result
1180
+ end
1181
+
1182
+ def async_callback_on_fulfillment(state, executor, args, callback)
1183
+ with_async(executor, state, args, callback) do |st, ar, cb|
1184
+ callback_on_fulfillment st, ar, cb
1185
+ end
1186
+ end
1187
+
1188
+ def async_callback_on_rejection(state, executor, args, callback)
1189
+ with_async(executor, state, args, callback) do |st, ar, cb|
1190
+ callback_on_rejection st, ar, cb
1191
+ end
1192
+ end
1193
+
1194
+ def callback_on_fulfillment(state, args, callback)
1195
+ state.apply args, callback if state.fulfilled?
1196
+ end
1197
+
1198
+ def callback_on_rejection(state, args, callback)
1199
+ state.apply args, callback unless state.fulfilled?
1200
+ end
1201
+
1202
+ def callback_on_resolution(state, args, callback)
1203
+ callback.call state.result, *args
1204
+ end
1205
+
1206
+ end
1207
+
1208
+ # Marker module of Future, Event resolved manually by user.
1209
+ module Resolvable
1210
+ end
1211
+
1212
+ # A Event which can be resolved by user.
1213
+ class ResolvableEvent < Event
1214
+ include Resolvable
1215
+
1216
+
1217
+ # @!macro [new] raise_on_reassign
1218
+ # @raise [MultipleAssignmentError] when already resolved and raise_on_reassign is true.
1219
+
1220
+ # @!macro [new] promise.param.raise_on_reassign
1221
+ # @param [Boolean] raise_on_reassign should method raise exception if already resolved
1222
+ # @return [self, false] false is returner when raise_on_reassign is false and the receiver
1223
+ # is already resolved.
1224
+ #
1225
+
1226
+ # Makes the event resolved, which triggers all dependent futures.
1227
+ #
1228
+ # @!macro promise.param.raise_on_reassign
1229
+ def resolve(raise_on_reassign = true)
1230
+ resolve_with RESOLVED, raise_on_reassign
1231
+ end
1232
+
1233
+ # Creates new event wrapping receiver, effectively hiding the resolve method.
1234
+ #
1235
+ # @return [Event]
1236
+ def with_hidden_resolvable
1237
+ @with_hidden_resolvable ||= EventWrapperPromise.new_blocked_by1(self, @DefaultExecutor).event
1238
+ end
1239
+ end
1240
+
1241
+ # A Future which can be resolved by user.
1242
+ class ResolvableFuture < Future
1243
+ include Resolvable
1244
+
1245
+ # Makes the future resolved with result of triplet `fulfilled?`, `value`, `reason`,
1246
+ # which triggers all dependent futures.
1247
+ #
1248
+ # @!macro promise.param.raise_on_reassign
1249
+ def resolve(fulfilled = true, value = nil, reason = nil, raise_on_reassign = true)
1250
+ resolve_with(fulfilled ? Fulfilled.new(value) : Rejected.new(reason), raise_on_reassign)
1251
+ end
1252
+
1253
+ # Makes the future fulfilled with `value`,
1254
+ # which triggers all dependent futures.
1255
+ #
1256
+ # @!macro promise.param.raise_on_reassign
1257
+ def fulfill(value, raise_on_reassign = true)
1258
+ promise.fulfill(value, raise_on_reassign)
1259
+ end
1260
+
1261
+ # Makes the future rejected with `reason`,
1262
+ # which triggers all dependent futures.
1263
+ #
1264
+ # @!macro promise.param.raise_on_reassign
1265
+ def reject(reason, raise_on_reassign = true)
1266
+ promise.reject(reason, raise_on_reassign)
1267
+ end
1268
+
1269
+ # Evaluates the block and sets its result as future's value fulfilling, if the block raises
1270
+ # an exception the future rejects with it.
1271
+ # @yield [*args] to the block.
1272
+ # @yieldreturn [Object] value
1273
+ # @return [self]
1274
+ def evaluate_to(*args, &block)
1275
+ # FIXME (pitr-ch 13-Jun-2016): add raise_on_reassign
1276
+ promise.evaluate_to(*args, block)
1277
+ end
1278
+
1279
+ # Evaluates the block and sets its result as future's value fulfilling, if the block raises
1280
+ # an exception the future rejects with it.
1281
+ # @yield [*args] to the block.
1282
+ # @yieldreturn [Object] value
1283
+ # @return [self]
1284
+ # @raise [Exception] also raise reason on rejection.
1285
+ def evaluate_to!(*args, &block)
1286
+ promise.evaluate_to!(*args, block)
1287
+ end
1288
+
1289
+ # Creates new future wrapping receiver, effectively hiding the resolve method and similar.
1290
+ #
1291
+ # @return [Future]
1292
+ def with_hidden_resolvable
1293
+ @with_hidden_resolvable ||= FutureWrapperPromise.new_blocked_by1(self, @DefaultExecutor).future
1294
+ end
1295
+ end
1296
+
1297
+ # @abstract
1298
+ # @private
1299
+ class AbstractPromise < Synchronization::Object
1300
+ safe_initialization!
1301
+ include InternalStates
1302
+
1303
+ def initialize(future)
1304
+ super()
1305
+ @Future = future
1306
+ end
1307
+
1308
+ def future
1309
+ @Future
1310
+ end
1311
+
1312
+ alias_method :event, :future
1313
+
1314
+ def default_executor
1315
+ future.default_executor
1316
+ end
1317
+
1318
+ def state
1319
+ future.state
1320
+ end
1321
+
1322
+ def touch
1323
+ end
1324
+
1325
+ alias_method :inspect, :to_s
1326
+
1327
+ def delayed
1328
+ nil
1329
+ end
1330
+
1331
+ private
1332
+
1333
+ def resolve_with(new_state, raise_on_reassign = true)
1334
+ @Future.resolve_with(new_state, raise_on_reassign)
1335
+ end
1336
+
1337
+ # @return [Future]
1338
+ def evaluate_to(*args, block)
1339
+ resolve_with Fulfilled.new(block.call(*args))
1340
+ rescue Exception => error
1341
+ # TODO (pitr-ch 30-Jul-2016): figure out what should be rescued, there is an issue about it
1342
+ resolve_with Rejected.new(error)
1343
+ end
1344
+ end
1345
+
1346
+ class ResolvableEventPromise < AbstractPromise
1347
+ def initialize(default_executor)
1348
+ super ResolvableEvent.new(self, default_executor)
1349
+ end
1350
+ end
1351
+
1352
+ class ResolvableFuturePromise < AbstractPromise
1353
+ def initialize(default_executor)
1354
+ super ResolvableFuture.new(self, default_executor)
1355
+ end
1356
+
1357
+ def fulfill(value, raise_on_reassign)
1358
+ resolve_with Fulfilled.new(value), raise_on_reassign
1359
+ end
1360
+
1361
+ def reject(reason, raise_on_reassign)
1362
+ resolve_with Rejected.new(reason), raise_on_reassign
1363
+ end
1364
+
1365
+ public :evaluate_to
1366
+
1367
+ def evaluate_to!(*args, block)
1368
+ evaluate_to(*args, block).wait!
1369
+ end
1370
+ end
1371
+
1372
+ # @abstract
1373
+ class InnerPromise < AbstractPromise
1374
+ end
1375
+
1376
+ # @abstract
1377
+ class BlockedPromise < InnerPromise
1378
+
1379
+ private_class_method :new
1380
+
1381
+ def self.new_blocked_by1(blocker, *args, &block)
1382
+ blocker_delayed = blocker.promise.delayed
1383
+ delayed = blocker_delayed ? LockFreeStack.new.push(blocker_delayed) : nil
1384
+ promise = new(delayed, 1, *args, &block)
1385
+ ensure
1386
+ blocker.add_callback :callback_notify_blocked, promise, 0
1387
+ end
1388
+
1389
+ def self.new_blocked_by2(blocker1, blocker2, *args, &block)
1390
+ blocker_delayed1 = blocker1.promise.delayed
1391
+ blocker_delayed2 = blocker2.promise.delayed
1392
+ # TODO (pitr-ch 23-Dec-2016): use arrays when we know it will not grow (only flat adds delay)
1393
+ delayed = if blocker_delayed1
1394
+ if blocker_delayed2
1395
+ LockFreeStack.of2(blocker_delayed1, blocker_delayed2)
1396
+ else
1397
+ LockFreeStack.of1(blocker_delayed1)
1398
+ end
1399
+ else
1400
+ blocker_delayed2 ? LockFreeStack.of1(blocker_delayed2) : nil
1401
+ end
1402
+ promise = new(delayed, 2, *args, &block)
1403
+ ensure
1404
+ blocker1.add_callback :callback_notify_blocked, promise, 0
1405
+ blocker2.add_callback :callback_notify_blocked, promise, 1
1406
+ end
1407
+
1408
+ def self.new_blocked_by(blockers, *args, &block)
1409
+ delayed = blockers.reduce(nil, &method(:add_delayed))
1410
+ promise = new(delayed, blockers.size, *args, &block)
1411
+ ensure
1412
+ blockers.each_with_index { |f, i| f.add_callback :callback_notify_blocked, promise, i }
1413
+ end
1414
+
1415
+ def self.add_delayed(delayed, blocker)
1416
+ blocker_delayed = blocker.promise.delayed
1417
+ if blocker_delayed
1418
+ delayed = unless delayed
1419
+ LockFreeStack.of1(blocker_delayed)
1420
+ else
1421
+ delayed.push(blocker_delayed)
1422
+ end
1423
+ end
1424
+ delayed
1425
+ end
1426
+
1427
+ def initialize(delayed, blockers_count, future)
1428
+ super(future)
1429
+ # noinspection RubyArgCount
1430
+ @Touched = AtomicBoolean.new false
1431
+ @Delayed = delayed
1432
+ # noinspection RubyArgCount
1433
+ @Countdown = AtomicFixnum.new blockers_count
1434
+ end
1435
+
1436
+ def on_blocker_resolution(future, index)
1437
+ countdown = process_on_blocker_resolution(future, index)
1438
+ resolvable = resolvable?(countdown, future, index)
1439
+
1440
+ on_resolvable(future, index) if resolvable
1441
+ end
1442
+
1443
+ def delayed
1444
+ @Delayed
1445
+ end
1446
+
1447
+ def touch
1448
+ clear_propagate_touch if @Touched.make_true
1449
+ end
1450
+
1451
+ def touched?
1452
+ @Touched.value
1453
+ end
1454
+
1455
+ # for inspection only
1456
+ def blocked_by
1457
+ blocked_by = []
1458
+ ObjectSpace.each_object(AbstractEventFuture) { |o| blocked_by.push o if o.blocks.include? self }
1459
+ blocked_by
1460
+ end
1461
+
1462
+ private
1463
+
1464
+ def clear_propagate_touch
1465
+ @Delayed.clear_each { |o| propagate_touch o } if @Delayed
1466
+ end
1467
+
1468
+ def propagate_touch(stack_or_element = @Delayed)
1469
+ if stack_or_element.is_a? LockFreeStack
1470
+ stack_or_element.each { |element| propagate_touch element }
1471
+ else
1472
+ stack_or_element.touch unless stack_or_element.nil? # if still present
1473
+ end
1474
+ end
1475
+
1476
+ # @return [true,false] if resolvable
1477
+ def resolvable?(countdown, future, index)
1478
+ countdown.zero?
1479
+ end
1480
+
1481
+ def process_on_blocker_resolution(future, index)
1482
+ @Countdown.decrement
1483
+ end
1484
+
1485
+ def on_resolvable(resolved_future, index)
1486
+ raise NotImplementedError
1487
+ end
1488
+ end
1489
+
1490
+ # @abstract
1491
+ class BlockedTaskPromise < BlockedPromise
1492
+ def initialize(delayed, blockers_count, default_executor, executor, args, &task)
1493
+ raise ArgumentError, 'no block given' unless block_given?
1494
+ super delayed, 1, Future.new(self, default_executor)
1495
+ @Executor = executor
1496
+ @Task = task
1497
+ @Args = args
1498
+ end
1499
+
1500
+ def executor
1501
+ @Executor
1502
+ end
1503
+ end
1504
+
1505
+ class ThenPromise < BlockedTaskPromise
1506
+ private
1507
+
1508
+ def initialize(delayed, blockers_count, default_executor, executor, args, &task)
1509
+ super delayed, blockers_count, default_executor, executor, args, &task
1510
+ end
1511
+
1512
+ def on_resolvable(resolved_future, index)
1513
+ if resolved_future.fulfilled?
1514
+ Concurrent.executor(@Executor).post(resolved_future, @Args, @Task) do |future, args, task|
1515
+ evaluate_to lambda { future.apply args, task }
1516
+ end
1517
+ else
1518
+ resolve_with resolved_future.internal_state
1519
+ end
1520
+ end
1521
+ end
1522
+
1523
+ class RescuePromise < BlockedTaskPromise
1524
+ private
1525
+
1526
+ def initialize(delayed, blockers_count, default_executor, executor, args, &task)
1527
+ super delayed, blockers_count, default_executor, executor, args, &task
1528
+ end
1529
+
1530
+ def on_resolvable(resolved_future, index)
1531
+ if resolved_future.rejected?
1532
+ Concurrent.executor(@Executor).post(resolved_future, @Args, @Task) do |future, args, task|
1533
+ evaluate_to lambda { future.apply args, task }
1534
+ end
1535
+ else
1536
+ resolve_with resolved_future.internal_state
1537
+ end
1538
+ end
1539
+ end
1540
+
1541
+ class ChainPromise < BlockedTaskPromise
1542
+ private
1543
+
1544
+ def on_resolvable(resolved_future, index)
1545
+ if Future === resolved_future
1546
+ Concurrent.executor(@Executor).post(resolved_future, @Args, @Task) do |future, args, task|
1547
+ evaluate_to(*future.result, *args, task)
1548
+ end
1549
+ else
1550
+ Concurrent.executor(@Executor).post(@Args, @Task) do |args, task|
1551
+ evaluate_to *args, task
1552
+ end
1553
+ end
1554
+ end
1555
+ end
1556
+
1557
+ # will be immediately resolved
1558
+ class ImmediateEventPromise < InnerPromise
1559
+ def initialize(default_executor)
1560
+ super Event.new(self, default_executor).resolve_with(RESOLVED)
1561
+ end
1562
+ end
1563
+
1564
+ class ImmediateFuturePromise < InnerPromise
1565
+ def initialize(default_executor, fulfilled, value, reason)
1566
+ super Future.new(self, default_executor).
1567
+ resolve_with(fulfilled ? Fulfilled.new(value) : Rejected.new(reason))
1568
+ end
1569
+ end
1570
+
1571
+ class AbstractFlatPromise < BlockedPromise
1572
+
1573
+ private
1574
+
1575
+ def on_resolvable(resolved_future, index)
1576
+ resolve_with resolved_future.internal_state
1577
+ end
1578
+
1579
+ def resolvable?(countdown, future, index)
1580
+ !@Future.internal_state.resolved? && super(countdown, future, index)
1581
+ end
1582
+
1583
+ def add_delayed_of(future)
1584
+ if touched?
1585
+ propagate_touch future.promise.delayed
1586
+ else
1587
+ BlockedPromise.add_delayed @Delayed, future
1588
+ clear_propagate_touch if touched?
1589
+ end
1590
+ end
1591
+
1592
+ end
1593
+
1594
+ class FlatEventPromise < AbstractFlatPromise
1595
+
1596
+ private
1597
+
1598
+ def initialize(delayed, blockers_count, default_executor)
1599
+ super delayed, 2, Event.new(self, default_executor)
1600
+ end
1601
+
1602
+ def process_on_blocker_resolution(future, index)
1603
+ countdown = super(future, index)
1604
+ if countdown.nonzero?
1605
+ internal_state = future.internal_state
1606
+
1607
+ unless internal_state.fulfilled?
1608
+ resolve_with RESOLVED
1609
+ return countdown
1610
+ end
1611
+
1612
+ value = internal_state.value
1613
+ case value
1614
+ when Future, Event
1615
+ add_delayed_of value
1616
+ value.add_callback :callback_notify_blocked, self, nil
1617
+ countdown
1618
+ else
1619
+ resolve_with RESOLVED
1620
+ end
1621
+ end
1622
+ countdown
1623
+ end
1624
+
1625
+ end
1626
+
1627
+ class FlatFuturePromise < AbstractFlatPromise
1628
+
1629
+ private
1630
+
1631
+ def initialize(delayed, blockers_count, levels, default_executor)
1632
+ raise ArgumentError, 'levels has to be higher than 0' if levels < 1
1633
+ # flat promise may result to a future having delayed futures, therefore we have to have empty stack
1634
+ # to be able to add new delayed futures
1635
+ super delayed || LockFreeStack.new, 1 + levels, Future.new(self, default_executor)
1636
+ end
1637
+
1638
+ def process_on_blocker_resolution(future, index)
1639
+ countdown = super(future, index)
1640
+ if countdown.nonzero?
1641
+ internal_state = future.internal_state
1642
+
1643
+ unless internal_state.fulfilled?
1644
+ resolve_with internal_state
1645
+ return countdown
1646
+ end
1647
+
1648
+ value = internal_state.value
1649
+ case value
1650
+ when Future
1651
+ add_delayed_of value
1652
+ value.add_callback :callback_notify_blocked, self, nil
1653
+ countdown
1654
+ when Event
1655
+ evaluate_to(lambda { raise TypeError, 'cannot flatten to Event' })
1656
+ else
1657
+ evaluate_to(lambda { raise TypeError, "returned value #{value.inspect} is not a Future" })
1658
+ end
1659
+ end
1660
+ countdown
1661
+ end
1662
+
1663
+ end
1664
+
1665
+ class RunFuturePromise < AbstractFlatPromise
1666
+
1667
+ private
1668
+
1669
+ def initialize(delayed, blockers_count, default_executor)
1670
+ super delayed, 1, Future.new(self, default_executor)
1671
+ end
1672
+
1673
+ def process_on_blocker_resolution(future, index)
1674
+ internal_state = future.internal_state
1675
+
1676
+ unless internal_state.fulfilled?
1677
+ resolve_with internal_state
1678
+ return 0
1679
+ end
1680
+
1681
+ value = internal_state.value
1682
+ case value
1683
+ when Future
1684
+ add_delayed_of value
1685
+ value.add_callback :callback_notify_blocked, self, nil
1686
+ else
1687
+ resolve_with internal_state
1688
+ end
1689
+
1690
+ 1
1691
+ end
1692
+ end
1693
+
1694
+ class ZipEventEventPromise < BlockedPromise
1695
+ def initialize(delayed, blockers_count, default_executor)
1696
+ super delayed, 2, Event.new(self, default_executor)
1697
+ end
1698
+
1699
+ private
1700
+
1701
+ def on_resolvable(resolved_future, index)
1702
+ resolve_with RESOLVED
1703
+ end
1704
+ end
1705
+
1706
+ class ZipFutureEventPromise < BlockedPromise
1707
+ def initialize(delayed, blockers_count, default_executor)
1708
+ super delayed, 2, Future.new(self, default_executor)
1709
+ @result = nil
1710
+ end
1711
+
1712
+ private
1713
+
1714
+ def process_on_blocker_resolution(future, index)
1715
+ # first blocking is future, take its result
1716
+ @result = future.internal_state if index == 0
1717
+ # super has to be called after above to piggyback on volatile @Countdown
1718
+ super future, index
1719
+ end
1720
+
1721
+ def on_resolvable(resolved_future, index)
1722
+ resolve_with @result
1723
+ end
1724
+ end
1725
+
1726
+ class EventWrapperPromise < BlockedPromise
1727
+ def initialize(delayed, blockers_count, default_executor)
1728
+ super delayed, 1, Event.new(self, default_executor)
1729
+ end
1730
+
1731
+ private
1732
+
1733
+ def on_resolvable(resolved_future, index)
1734
+ resolve_with RESOLVED
1735
+ end
1736
+ end
1737
+
1738
+ class FutureWrapperPromise < BlockedPromise
1739
+ def initialize(delayed, blockers_count, default_executor)
1740
+ super delayed, 1, Future.new(self, default_executor)
1741
+ end
1742
+
1743
+ private
1744
+
1745
+ def on_resolvable(resolved_future, index)
1746
+ resolve_with resolved_future.internal_state
1747
+ end
1748
+ end
1749
+
1750
+ class ZipFuturesPromise < BlockedPromise
1751
+
1752
+ private
1753
+
1754
+ def initialize(delayed, blockers_count, default_executor)
1755
+ super(delayed, blockers_count, Future.new(self, default_executor))
1756
+ @Resolutions = ::Array.new(blockers_count)
1757
+
1758
+ on_resolvable nil, nil if blockers_count == 0
1759
+ end
1760
+
1761
+ def process_on_blocker_resolution(future, index)
1762
+ # TODO (pitr-ch 18-Dec-2016): Can we assume that array will never break under parallel access when never re-sized?
1763
+ @Resolutions[index] = future.internal_state # has to be set before countdown in super
1764
+ super future, index
1765
+ end
1766
+
1767
+ def on_resolvable(resolved_future, index)
1768
+ all_fulfilled = true
1769
+ values = Array.new(@Resolutions.size)
1770
+ reasons = Array.new(@Resolutions.size)
1771
+
1772
+ @Resolutions.each_with_index do |internal_state, i|
1773
+ fulfilled, values[i], reasons[i] = internal_state.result
1774
+ all_fulfilled &&= fulfilled
1775
+ end
1776
+
1777
+ if all_fulfilled
1778
+ resolve_with FulfilledArray.new(values)
1779
+ else
1780
+ resolve_with PartiallyRejected.new(values, reasons)
1781
+ end
1782
+ end
1783
+ end
1784
+
1785
+ class ZipEventsPromise < BlockedPromise
1786
+
1787
+ private
1788
+
1789
+ def initialize(delayed, blockers_count, default_executor)
1790
+ super delayed, blockers_count, Event.new(self, default_executor)
1791
+
1792
+ on_resolvable nil, nil if blockers_count == 0
1793
+ end
1794
+
1795
+ def on_resolvable(resolved_future, index)
1796
+ resolve_with RESOLVED
1797
+ end
1798
+ end
1799
+
1800
+ # @abstract
1801
+ class AbstractAnyPromise < BlockedPromise
1802
+ end
1803
+
1804
+ class AnyResolvedFuturePromise < AbstractAnyPromise
1805
+
1806
+ private
1807
+
1808
+ def initialize(delayed, blockers_count, default_executor)
1809
+ super delayed, blockers_count, Future.new(self, default_executor)
1810
+ end
1811
+
1812
+ def resolvable?(countdown, future, index)
1813
+ true
1814
+ end
1815
+
1816
+ def on_resolvable(resolved_future, index)
1817
+ resolve_with resolved_future.internal_state, false
1818
+ end
1819
+ end
1820
+
1821
+ class AnyResolvedEventPromise < AbstractAnyPromise
1822
+
1823
+ private
1824
+
1825
+ def initialize(delayed, blockers_count, default_executor)
1826
+ super delayed, blockers_count, Event.new(self, default_executor)
1827
+ end
1828
+
1829
+ def resolvable?(countdown, future, index)
1830
+ true
1831
+ end
1832
+
1833
+ def on_resolvable(resolved_future, index)
1834
+ resolve_with RESOLVED, false
1835
+ end
1836
+ end
1837
+
1838
+ class AnyFulfilledFuturePromise < AnyResolvedFuturePromise
1839
+
1840
+ private
1841
+
1842
+ def resolvable?(countdown, future, index)
1843
+ future.fulfilled? ||
1844
+ # inlined super from BlockedPromise
1845
+ countdown.zero?
1846
+ end
1847
+ end
1848
+
1849
+ class DelayPromise < InnerPromise
1850
+
1851
+ def initialize(default_executor)
1852
+ super event = Event.new(self, default_executor)
1853
+ @Delayed = LockFreeStack.new.push self
1854
+ # TODO (pitr-ch 20-Dec-2016): implement directly without callback?
1855
+ event.on_resolution!(@Delayed.peek) { |stack_node| stack_node.value = nil }
1856
+ end
1857
+
1858
+ def touch
1859
+ @Future.resolve_with RESOLVED
1860
+ end
1861
+
1862
+ def delayed
1863
+ @Delayed
1864
+ end
1865
+
1866
+ end
1867
+
1868
+ class ScheduledPromise < InnerPromise
1869
+ def intended_time
1870
+ @IntendedTime
1871
+ end
1872
+
1873
+ def inspect
1874
+ "#{to_s[0..-2]} intended_time: #{@IntendedTime}>"
1875
+ end
1876
+
1877
+ private
1878
+
1879
+ def initialize(default_executor, intended_time)
1880
+ super Event.new(self, default_executor)
1881
+
1882
+ @IntendedTime = intended_time
1883
+
1884
+ in_seconds = begin
1885
+ now = Time.now
1886
+ schedule_time = if @IntendedTime.is_a? Time
1887
+ @IntendedTime
1888
+ else
1889
+ now + @IntendedTime
1890
+ end
1891
+ [0, schedule_time.to_f - now.to_f].max
1892
+ end
1893
+
1894
+ Concurrent.global_timer_set.post(in_seconds) do
1895
+ @Future.resolve_with RESOLVED
1896
+ end
1897
+ end
1898
+ end
1899
+
1900
+ extend FactoryMethods
1901
+
1902
+ private_constant :AbstractPromise,
1903
+ :ResolvableEventPromise,
1904
+ :ResolvableFuturePromise,
1905
+ :InnerPromise,
1906
+ :BlockedPromise,
1907
+ :BlockedTaskPromise,
1908
+ :ThenPromise,
1909
+ :RescuePromise,
1910
+ :ChainPromise,
1911
+ :ImmediateEventPromise,
1912
+ :ImmediateFuturePromise,
1913
+ :AbstractFlatPromise,
1914
+ :FlatFuturePromise,
1915
+ :FlatEventPromise,
1916
+ :RunFuturePromise,
1917
+ :ZipEventEventPromise,
1918
+ :ZipFutureEventPromise,
1919
+ :EventWrapperPromise,
1920
+ :FutureWrapperPromise,
1921
+ :ZipFuturesPromise,
1922
+ :ZipEventsPromise,
1923
+ :AbstractAnyPromise,
1924
+ :AnyResolvedFuturePromise,
1925
+ :AnyFulfilledFuturePromise,
1926
+ :AnyResolvedEventPromise,
1927
+ :DelayPromise,
1928
+ :ScheduledPromise
1929
+
1930
+
1931
+ end
1932
+ end
1933
+
1934
+ # TODO try stealing pool, each thread has it's own queue
1935
+ # TODO (pitr-ch 18-Dec-2016): doc macro debug method
1936
+ # TODO (pitr-ch 18-Dec-2016): add macro noting that debug methods may change api without warning
1937
+
1938
+
1939
+ module Concurrent
1940
+ module Promises
1941
+
1942
+ class Future < AbstractEventFuture
1943
+
1944
+ module ActorIntegration
1945
+ # Asks the actor with its value.
1946
+ # @return [Future] new future with the response form the actor
1947
+ def then_ask(actor)
1948
+ self.then { |v| actor.ask(v) }.flat
1949
+ end
1950
+ end
1951
+
1952
+ include ActorIntegration
1953
+ end
1954
+
1955
+ class Channel < Concurrent::Synchronization::Object
1956
+ safe_initialization!
1957
+
1958
+ # Default size of the Channel, makes it accept unlimited number of messages.
1959
+ UNLIMITED = Object.new
1960
+ UNLIMITED.singleton_class.class_eval do
1961
+ include Comparable
1962
+
1963
+ def <=>(other)
1964
+ 1
1965
+ end
1966
+
1967
+ def to_s
1968
+ 'unlimited'
1969
+ end
1970
+ end
1971
+
1972
+ # A channel to pass messages between promises. The size is limited to support back pressure.
1973
+ # @param [Integer, UNLIMITED] size the maximum number of messages stored in the channel.
1974
+ def initialize(size = UNLIMITED)
1975
+ super()
1976
+ @Size = size
1977
+ # TODO (pitr-ch 26-Dec-2016): replace with lock-free implementation
1978
+ @Mutex = Mutex.new
1979
+ @Probes = []
1980
+ @Messages = []
1981
+ @PendingPush = []
1982
+ end
1983
+
1984
+
1985
+ # Returns future which will fulfill when the message is added to the channel. Its value is the message.
1986
+ # @param [Object] message
1987
+ # @return [Future]
1988
+ def push(message)
1989
+ @Mutex.synchronize do
1990
+ while true
1991
+ if @Probes.empty?
1992
+ if @Size > @Messages.size
1993
+ @Messages.push message
1994
+ return Promises.fulfilled_future message
1995
+ else
1996
+ pushed = Promises.resolvable_future
1997
+ @PendingPush.push [message, pushed]
1998
+ return pushed.with_hidden_resolvable
1999
+ end
2000
+ else
2001
+ probe = @Probes.shift
2002
+ if probe.fulfill [self, message], false
2003
+ return Promises.fulfilled_future(message)
2004
+ end
2005
+ end
2006
+ end
2007
+ end
2008
+ end
2009
+
2010
+ # Returns a future witch will become fulfilled with a value from the channel when one is available.
2011
+ # @param [ResolvableFuture] probe the future which will be fulfilled with a channel value
2012
+ # @return [Future] the probe, its value will be the message when available.
2013
+ def pop(probe = Concurrent::Promises.resolvable_future)
2014
+ # TODO (pitr-ch 26-Dec-2016): improve performance
2015
+ pop_for_select(probe).then(&:last)
2016
+ end
2017
+
2018
+ # @!visibility private
2019
+ def pop_for_select(probe = Concurrent::Promises.resolvable_future)
2020
+ @Mutex.synchronize do
2021
+ if @Messages.empty?
2022
+ @Probes.push probe
2023
+ else
2024
+ message = @Messages.shift
2025
+ probe.fulfill [self, message]
2026
+
2027
+ unless @PendingPush.empty?
2028
+ message, pushed = @PendingPush.shift
2029
+ @Messages.push message
2030
+ pushed.fulfill message
2031
+ end
2032
+ end
2033
+ end
2034
+ probe
2035
+ end
2036
+
2037
+ # @return [String] Short string representation.
2038
+ def to_s
2039
+ format '<#%s:0x%x size:%s>', self.class, object_id << 1, @Size
2040
+ end
2041
+
2042
+ alias_method :inspect, :to_s
2043
+ end
2044
+
2045
+ class Future < AbstractEventFuture
2046
+ module NewChannelIntegration
2047
+
2048
+ # @param [Channel] channel to push to.
2049
+ # @return [Future] a future which is fulfilled after the message is pushed to the channel.
2050
+ # May take a moment if the channel is full.
2051
+ def then_push_channel(channel)
2052
+ self.then { |value| channel.push value }.flat_future
2053
+ end
2054
+
2055
+ # TODO (pitr-ch 26-Dec-2016): does it make sense to have rescue an chain variants as well, check other integrations as well
2056
+ end
2057
+
2058
+ include NewChannelIntegration
2059
+ end
2060
+
2061
+ module FactoryMethods
2062
+
2063
+ module NewChannelIntegration
2064
+
2065
+ # Selects a channel which is ready to be read from.
2066
+ # @param [Channel] channels
2067
+ # @return [Future] a future which is fulfilled with pair [channel, message] when one of the channels is
2068
+ # available for reading
2069
+ def select_channel(*channels)
2070
+ probe = Promises.resolvable_future
2071
+ channels.each { |ch| ch.pop_for_select probe }
2072
+ probe
2073
+ end
2074
+ end
2075
+
2076
+ include NewChannelIntegration
2077
+ end
2078
+
2079
+ end
2080
+ end