concurrent-ruby-edge 0.2.0.pre2 → 0.2.0.pre3
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 +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
|