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.
@@ -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
- probe = future
98
- channels.each { |ch| ch.select probe }
99
- probe
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 then_push(channel)
697
- on_success(:io) { |value| channel.push value }
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.pre2
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-19 00:00:00.000000000 Z
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.pre2
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.pre2
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/blocking_ring_buffer.rb
76
- - lib/concurrent/channel/buffered_channel.rb
77
- - lib/concurrent/channel/channel.rb
78
- - lib/concurrent/channel/ring_buffer.rb
79
- - lib/concurrent/channel/unbuffered_channel.rb
80
- - lib/concurrent/channel/waitable_list.rb
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.5.1
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