concurrent-ruby-edge 0.2.0.pre2 → 0.2.0.pre3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +7 -5
- data/lib/concurrent/channel.rb +272 -6
- data/lib/concurrent/channel/buffer.rb +9 -0
- data/lib/concurrent/channel/buffer/base.rb +197 -0
- data/lib/concurrent/channel/buffer/buffered.rb +127 -0
- data/lib/concurrent/channel/buffer/dropping.rb +53 -0
- data/lib/concurrent/channel/buffer/sliding.rb +54 -0
- data/lib/concurrent/channel/buffer/ticker.rb +80 -0
- data/lib/concurrent/channel/buffer/timer.rb +78 -0
- data/lib/concurrent/channel/buffer/unbuffered.rb +151 -0
- data/lib/concurrent/channel/selector.rb +70 -0
- data/lib/concurrent/channel/selector/after_clause.rb +27 -0
- data/lib/concurrent/channel/selector/default_clause.rb +19 -0
- data/lib/concurrent/channel/selector/error_clause.rb +21 -0
- data/lib/concurrent/channel/selector/put_clause.rb +26 -0
- data/lib/concurrent/channel/selector/take_clause.rb +24 -0
- data/lib/concurrent/channel/tick.rb +55 -0
- data/lib/concurrent/edge/future.rb +23 -41
- data/lib/concurrent/edge/lock_free_linked_set.rb +2 -1
- metadata +20 -11
- data/lib/concurrent/channel/blocking_ring_buffer.rb +0 -82
- data/lib/concurrent/channel/buffered_channel.rb +0 -89
- data/lib/concurrent/channel/channel.rb +0 -19
- data/lib/concurrent/channel/ring_buffer.rb +0 -65
- data/lib/concurrent/channel/unbuffered_channel.rb +0 -39
- data/lib/concurrent/channel/waitable_list.rb +0 -48
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'concurrent/maybe'
|
2
|
+
|
3
|
+
require 'concurrent/channel/selector/after_clause'
|
4
|
+
require 'concurrent/channel/selector/default_clause'
|
5
|
+
require 'concurrent/channel/selector/put_clause'
|
6
|
+
require 'concurrent/channel/selector/take_clause'
|
7
|
+
|
8
|
+
module Concurrent
|
9
|
+
class Channel
|
10
|
+
|
11
|
+
# @!visibility private
|
12
|
+
class Selector
|
13
|
+
|
14
|
+
def initialize
|
15
|
+
@clauses = []
|
16
|
+
@error_handler = nil
|
17
|
+
end
|
18
|
+
|
19
|
+
def case(channel, action, message = nil, &block)
|
20
|
+
if [:take, :poll, :receive, :~].include?(action)
|
21
|
+
take(channel, &block)
|
22
|
+
elsif [:put, :offer, :send, :<<].include?(action)
|
23
|
+
put(channel, message, &block)
|
24
|
+
else
|
25
|
+
raise ArgumentError.new('invalid action')
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def take(channel, &block)
|
30
|
+
raise ArgumentError.new('no block given') unless block_given?
|
31
|
+
@clauses << TakeClause.new(channel, block)
|
32
|
+
end
|
33
|
+
alias_method :receive, :take
|
34
|
+
|
35
|
+
def put(channel, message, &block)
|
36
|
+
@clauses << PutClause.new(channel, message, block)
|
37
|
+
end
|
38
|
+
alias_method :send, :put
|
39
|
+
|
40
|
+
def after(seconds, &block)
|
41
|
+
@clauses << AfterClause.new(seconds, block)
|
42
|
+
end
|
43
|
+
alias_method :timeout, :after
|
44
|
+
|
45
|
+
def default(&block)
|
46
|
+
raise ArgumentError.new('no block given') unless block_given?
|
47
|
+
@clauses << DefaultClause.new(block)
|
48
|
+
end
|
49
|
+
|
50
|
+
def error(&block)
|
51
|
+
raise ArgumentError.new('no block given') unless block_given?
|
52
|
+
raise ArgumentError.new('only one error handler allowed') if @error_handler
|
53
|
+
@error_handler = block
|
54
|
+
end
|
55
|
+
|
56
|
+
def execute
|
57
|
+
loop do
|
58
|
+
done = @clauses.each do |clause|
|
59
|
+
result = clause.execute
|
60
|
+
break result if result.just?
|
61
|
+
end
|
62
|
+
break done.value if done.is_a?(Concurrent::Maybe)
|
63
|
+
Thread.pass
|
64
|
+
end
|
65
|
+
rescue => ex
|
66
|
+
@error_handler.call(ex) if @error_handler
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'concurrent/maybe'
|
2
|
+
require 'concurrent/utility/monotonic_time'
|
3
|
+
|
4
|
+
module Concurrent
|
5
|
+
class Channel
|
6
|
+
class Selector
|
7
|
+
|
8
|
+
class AfterClause
|
9
|
+
|
10
|
+
def initialize(seconds, block)
|
11
|
+
raise ArgumentError.new('timeout must 0.0 or more') if seconds.to_f < 0.0
|
12
|
+
@end = Concurrent.monotonic_time + seconds.to_f
|
13
|
+
@block = block
|
14
|
+
end
|
15
|
+
|
16
|
+
def execute
|
17
|
+
if Concurrent.monotonic_time > @end
|
18
|
+
result = @block ? @block.call : nil
|
19
|
+
Concurrent::Maybe.just(result)
|
20
|
+
else
|
21
|
+
Concurrent::Maybe.nothing
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'concurrent/maybe'
|
2
|
+
|
3
|
+
module Concurrent
|
4
|
+
class Channel
|
5
|
+
class Selector
|
6
|
+
|
7
|
+
class DefaultClause
|
8
|
+
|
9
|
+
def initialize(block)
|
10
|
+
@block = block
|
11
|
+
end
|
12
|
+
|
13
|
+
def execute
|
14
|
+
Concurrent::Maybe.just(@block.call)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Concurrent
|
2
|
+
class Channel
|
3
|
+
class Selector
|
4
|
+
|
5
|
+
class ErrorClause
|
6
|
+
|
7
|
+
def initialize(block)
|
8
|
+
@block = block
|
9
|
+
end
|
10
|
+
|
11
|
+
def execute(error)
|
12
|
+
@block.call(error)
|
13
|
+
rescue
|
14
|
+
# suppress and move on
|
15
|
+
ensure
|
16
|
+
return nil
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'concurrent/maybe'
|
2
|
+
|
3
|
+
module Concurrent
|
4
|
+
class Channel
|
5
|
+
class Selector
|
6
|
+
|
7
|
+
class PutClause
|
8
|
+
|
9
|
+
def initialize(channel, message, block)
|
10
|
+
@channel = channel
|
11
|
+
@message = message
|
12
|
+
@block = block
|
13
|
+
end
|
14
|
+
|
15
|
+
def execute
|
16
|
+
if @channel.offer?(@message).just?
|
17
|
+
result = @block ? @block.call : nil
|
18
|
+
Concurrent::Maybe.just(result)
|
19
|
+
else
|
20
|
+
Concurrent::Maybe.nothing
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'concurrent/maybe'
|
2
|
+
|
3
|
+
module Concurrent
|
4
|
+
class Channel
|
5
|
+
class Selector
|
6
|
+
|
7
|
+
class TakeClause
|
8
|
+
|
9
|
+
def initialize(channel, block)
|
10
|
+
@channel = channel
|
11
|
+
@block = block
|
12
|
+
end
|
13
|
+
|
14
|
+
def execute
|
15
|
+
if (result = @channel.poll?).just?
|
16
|
+
Concurrent::Maybe.just(@block.call(result.value))
|
17
|
+
else
|
18
|
+
Concurrent::Maybe.nothing
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'concurrent/utility/monotonic_time'
|
2
|
+
|
3
|
+
module Concurrent
|
4
|
+
class Channel
|
5
|
+
|
6
|
+
# A convenience class representing a single moment in monotonic time.
|
7
|
+
# Returned by {Concurrent::Channel} tickers and timers when they
|
8
|
+
# resolve.
|
9
|
+
#
|
10
|
+
# Includes `Comparable` and can be compared to monotonic_time, UTC
|
11
|
+
# time, or epoch time.
|
12
|
+
#
|
13
|
+
# @see Concurrent.monotonic_time
|
14
|
+
# @see Concurrent::Channel.ticker
|
15
|
+
# @see Concurrent::Channel.timer
|
16
|
+
class Tick
|
17
|
+
include Comparable
|
18
|
+
|
19
|
+
STRING_FORMAT = '%F %T.%6N %z %Z'.freeze
|
20
|
+
|
21
|
+
attr_reader :monotonic, :utc
|
22
|
+
|
23
|
+
def initialize(tick = Concurrent.monotonic_time)
|
24
|
+
@monotonic = tick
|
25
|
+
@utc = monotonic_to_utc(tick).freeze
|
26
|
+
end
|
27
|
+
|
28
|
+
def epoch
|
29
|
+
@utc.to_f
|
30
|
+
end
|
31
|
+
|
32
|
+
def to_s
|
33
|
+
@utc.strftime(STRING_FORMAT)
|
34
|
+
end
|
35
|
+
|
36
|
+
def <=>(other)
|
37
|
+
if other.is_a? Numeric
|
38
|
+
@monotonic <=> other
|
39
|
+
elsif other.is_a? Time
|
40
|
+
@utc <=> other.utc
|
41
|
+
elsif other.is_a? Tick
|
42
|
+
@monotonic <=> other.monotonic
|
43
|
+
else
|
44
|
+
nil
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def monotonic_to_utc(tick)
|
51
|
+
Time.now.utc + Concurrent.monotonic_time - tick
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -94,9 +94,13 @@ module Concurrent
|
|
94
94
|
# only proof of concept
|
95
95
|
# @return [Future]
|
96
96
|
def select(*channels)
|
97
|
-
|
98
|
-
|
99
|
-
|
97
|
+
future do
|
98
|
+
Channel.select do |s|
|
99
|
+
channels.each do |ch|
|
100
|
+
s.take(ch) { |value| [value, ch] }
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
100
104
|
end
|
101
105
|
|
102
106
|
# post job on :fast executor
|
@@ -693,8 +697,8 @@ module Concurrent
|
|
693
697
|
|
694
698
|
# @note may block
|
695
699
|
# @note only proof of concept
|
696
|
-
def
|
697
|
-
on_success(:io) { |value| channel.
|
700
|
+
def then_put(channel)
|
701
|
+
on_success(:io) { |value| channel.put value }
|
698
702
|
end
|
699
703
|
|
700
704
|
# @yield [value] executed async on `executor` when success
|
@@ -1137,17 +1141,24 @@ module Concurrent
|
|
1137
1141
|
|
1138
1142
|
def process_on_done(future)
|
1139
1143
|
countdown = super(future)
|
1140
|
-
value = future.value!
|
1141
1144
|
if countdown.nonzero?
|
1145
|
+
internal_state = future.internal_state
|
1146
|
+
|
1147
|
+
unless internal_state.success?
|
1148
|
+
complete_with internal_state
|
1149
|
+
return countdown
|
1150
|
+
end
|
1151
|
+
|
1152
|
+
value = internal_state.value
|
1142
1153
|
case value
|
1143
1154
|
when Future
|
1144
1155
|
@BlockedBy.push value
|
1145
1156
|
value.add_callback :pr_callback_notify_blocked, self
|
1146
1157
|
@Countdown.value
|
1147
1158
|
when Event
|
1148
|
-
raise TypeError, 'cannot flatten to Event'
|
1159
|
+
evaluate_to(lambda { raise TypeError, 'cannot flatten to Event' })
|
1149
1160
|
else
|
1150
|
-
raise TypeError, "returned value #{value.inspect} is not a Future"
|
1161
|
+
evaluate_to(lambda { raise TypeError, "returned value #{value.inspect} is not a Future" })
|
1151
1162
|
end
|
1152
1163
|
end
|
1153
1164
|
countdown
|
@@ -1170,6 +1181,10 @@ module Concurrent
|
|
1170
1181
|
@BlockedBy.clear
|
1171
1182
|
nil
|
1172
1183
|
end
|
1184
|
+
|
1185
|
+
def completable?(countdown)
|
1186
|
+
!@Future.internal_state.completed? && super(countdown)
|
1187
|
+
end
|
1173
1188
|
end
|
1174
1189
|
|
1175
1190
|
# @!visibility private
|
@@ -1375,39 +1390,6 @@ module Concurrent
|
|
1375
1390
|
end
|
1376
1391
|
end
|
1377
1392
|
end
|
1378
|
-
|
1379
|
-
# @note proof of concept
|
1380
|
-
class Channel < Synchronization::Object
|
1381
|
-
safe_initialization!
|
1382
|
-
|
1383
|
-
# TODO make lock free
|
1384
|
-
def initialize
|
1385
|
-
super()
|
1386
|
-
@ProbeSet = Concurrent::Channel::WaitableList.new
|
1387
|
-
end
|
1388
|
-
|
1389
|
-
def probe_set_size
|
1390
|
-
@ProbeSet.size
|
1391
|
-
end
|
1392
|
-
|
1393
|
-
def push(value)
|
1394
|
-
until @ProbeSet.take.try_success([value, self])
|
1395
|
-
end
|
1396
|
-
end
|
1397
|
-
|
1398
|
-
def pop
|
1399
|
-
select(Concurrent.future)
|
1400
|
-
end
|
1401
|
-
|
1402
|
-
def select(probe)
|
1403
|
-
@ProbeSet.put(probe)
|
1404
|
-
probe
|
1405
|
-
end
|
1406
|
-
|
1407
|
-
def inspect
|
1408
|
-
to_s
|
1409
|
-
end
|
1410
|
-
end
|
1411
1393
|
end
|
1412
1394
|
|
1413
1395
|
extend Edge::FutureShortcuts
|
@@ -111,7 +111,8 @@ module Concurrent
|
|
111
111
|
succ = curr.next_node
|
112
112
|
removed = curr.successor_reference.compare_and_set succ, succ, false, true
|
113
113
|
|
114
|
-
next_node unless removed
|
114
|
+
#next_node unless removed
|
115
|
+
continue unless removed
|
115
116
|
|
116
117
|
pred.successor_reference.compare_and_set curr, succ, false, false
|
117
118
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: concurrent-ruby-edge
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.0.
|
4
|
+
version: 0.2.0.pre3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jerry D'Antonio
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2015-09-
|
13
|
+
date: 2015-09-29 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: concurrent-ruby
|
@@ -18,14 +18,14 @@ dependencies:
|
|
18
18
|
requirements:
|
19
19
|
- - "~>"
|
20
20
|
- !ruby/object:Gem::Version
|
21
|
-
version: 1.0.0.
|
21
|
+
version: 1.0.0.pre3
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
24
|
version_requirements: !ruby/object:Gem::Requirement
|
25
25
|
requirements:
|
26
26
|
- - "~>"
|
27
27
|
- !ruby/object:Gem::Version
|
28
|
-
version: 1.0.0.
|
28
|
+
version: 1.0.0.pre3
|
29
29
|
description: |
|
30
30
|
These features are under active development and may change frequently. They are expected not to
|
31
31
|
keep backward compatibility (there may also lack tests and documentation). Semantic versions will
|
@@ -72,12 +72,21 @@ files:
|
|
72
72
|
- lib/concurrent/actor/utils/broadcast.rb
|
73
73
|
- lib/concurrent/actor/utils/pool.rb
|
74
74
|
- lib/concurrent/channel.rb
|
75
|
-
- lib/concurrent/channel/
|
76
|
-
- lib/concurrent/channel/
|
77
|
-
- lib/concurrent/channel/
|
78
|
-
- lib/concurrent/channel/
|
79
|
-
- lib/concurrent/channel/
|
80
|
-
- lib/concurrent/channel/
|
75
|
+
- lib/concurrent/channel/buffer.rb
|
76
|
+
- lib/concurrent/channel/buffer/base.rb
|
77
|
+
- lib/concurrent/channel/buffer/buffered.rb
|
78
|
+
- lib/concurrent/channel/buffer/dropping.rb
|
79
|
+
- lib/concurrent/channel/buffer/sliding.rb
|
80
|
+
- lib/concurrent/channel/buffer/ticker.rb
|
81
|
+
- lib/concurrent/channel/buffer/timer.rb
|
82
|
+
- lib/concurrent/channel/buffer/unbuffered.rb
|
83
|
+
- lib/concurrent/channel/selector.rb
|
84
|
+
- lib/concurrent/channel/selector/after_clause.rb
|
85
|
+
- lib/concurrent/channel/selector/default_clause.rb
|
86
|
+
- lib/concurrent/channel/selector/error_clause.rb
|
87
|
+
- lib/concurrent/channel/selector/put_clause.rb
|
88
|
+
- lib/concurrent/channel/selector/take_clause.rb
|
89
|
+
- lib/concurrent/channel/tick.rb
|
81
90
|
- lib/concurrent/edge/atomic_markable_reference.rb
|
82
91
|
- lib/concurrent/edge/future.rb
|
83
92
|
- lib/concurrent/edge/lock_free_linked_set.rb
|
@@ -104,7 +113,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
104
113
|
version: 1.3.1
|
105
114
|
requirements: []
|
106
115
|
rubyforge_project:
|
107
|
-
rubygems_version: 2.4.
|
116
|
+
rubygems_version: 2.4.8
|
108
117
|
signing_key:
|
109
118
|
specification_version: 4
|
110
119
|
summary: Edge features and additions to the concurrent-ruby gem.
|
@@ -1,82 +0,0 @@
|
|
1
|
-
require 'concurrent/synchronization'
|
2
|
-
|
3
|
-
module Concurrent
|
4
|
-
module Channel
|
5
|
-
|
6
|
-
# @api Channel
|
7
|
-
# @!macro edge_warning
|
8
|
-
class BlockingRingBuffer < Synchronization::LockableObject
|
9
|
-
|
10
|
-
def initialize(capacity)
|
11
|
-
super()
|
12
|
-
synchronize { ns_initialize capacity}
|
13
|
-
end
|
14
|
-
|
15
|
-
# @return [Integer] the capacity of the buffer
|
16
|
-
def capacity
|
17
|
-
synchronize { @buffer.capacity }
|
18
|
-
end
|
19
|
-
|
20
|
-
# @return [Integer] the number of elements currently in the buffer
|
21
|
-
def count
|
22
|
-
synchronize { @buffer.count }
|
23
|
-
end
|
24
|
-
|
25
|
-
# @return [Boolean] true if buffer is empty, false otherwise
|
26
|
-
def empty?
|
27
|
-
synchronize { @buffer.empty? }
|
28
|
-
end
|
29
|
-
|
30
|
-
# @return [Boolean] true if buffer is full, false otherwise
|
31
|
-
def full?
|
32
|
-
synchronize { @buffer.full? }
|
33
|
-
end
|
34
|
-
|
35
|
-
# @param [Object] value the value to be inserted
|
36
|
-
# @return [Boolean] true if value has been inserted, false otherwise
|
37
|
-
def put(value)
|
38
|
-
synchronize do
|
39
|
-
wait_while_full
|
40
|
-
@buffer.offer(value)
|
41
|
-
ns_signal
|
42
|
-
true
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
# @return [Object] the first available value and removes it from the buffer.
|
47
|
-
# If buffer is empty it blocks until an element is available
|
48
|
-
def take
|
49
|
-
synchronize do
|
50
|
-
wait_while_empty
|
51
|
-
result = @buffer.poll
|
52
|
-
ns_signal
|
53
|
-
result
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
# @return [Object] the first available value and without removing it from
|
58
|
-
# the buffer. If buffer is empty returns nil
|
59
|
-
def peek
|
60
|
-
synchronize { @buffer.peek }
|
61
|
-
end
|
62
|
-
|
63
|
-
protected
|
64
|
-
|
65
|
-
def ns_initialize(capacity)
|
66
|
-
@buffer = RingBuffer.new(capacity)
|
67
|
-
@first = @last = 0
|
68
|
-
@count = 0
|
69
|
-
end
|
70
|
-
|
71
|
-
private
|
72
|
-
|
73
|
-
def wait_while_full
|
74
|
-
ns_wait_until { !@buffer.full? }
|
75
|
-
end
|
76
|
-
|
77
|
-
def wait_while_empty
|
78
|
-
ns_wait_until { !@buffer.empty? }
|
79
|
-
end
|
80
|
-
end
|
81
|
-
end
|
82
|
-
end
|