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.
@@ -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