concurrent-ruby-edge 0.2.0.pre1 → 0.2.0.pre2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 09cfa6d29b8c6ad451d4d5805fe16ff7fe490703
4
- data.tar.gz: 721b15c9fa7a8c38e88c9d5f1e8597e0528386c6
3
+ metadata.gz: c02c328ebe5aaf45aaf86106223e979107a4ea99
4
+ data.tar.gz: 425af0cce7f688a07e0546922c916877c2b4277a
5
5
  SHA512:
6
- metadata.gz: ce588f1344ce16ab7c0d166bdc1f5c86c02c908083d7fcc10c7ead35a665af4325c1e9ccabfc925f289fdb15db1d68a4fff621d67b921b6b75f6115fc4ffc22b
7
- data.tar.gz: 479a2bb45adde98a74aa5ced4fea11d1112ccedb64d07e5dc5783d07cf750d905888effd97d8108b3dc2b928f394055423aa5bc6b8343bc26647b62bf4422300
6
+ metadata.gz: 877ef401c5cb31943832dd1a27f592e65b261608744b1d1b00e0210f82d9ae7d246fd3d224a78cbe64aa1533cf88b5c77338985bcaf7a29a8ceb7f29174bbb32
7
+ data.tar.gz: d3aa66b036246d9af5f5ba0fff8ec7b46943a7c5d8fcbfcf52e4166c40a9b3fd1a80e335aaea539a756f21210a12ccd981243409d94a99fb9c4c874fe31d74bb
data/README.md CHANGED
@@ -61,7 +61,7 @@ This library contains a variety of concurrency abstractions at high and low leve
61
61
 
62
62
  #### General-purpose Concurrency Abstractions
63
63
 
64
- * [Async](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/Async.html): A mixin module that provides simple asynchronous behavior to any standard class/object or object.
64
+ * [Async](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/Async.html): A mixin module that provides simple asynchronous behavior to a class. Loosely based on Erlang's [gen_server](http://www.erlang.org/doc/man/gen_server.html).
65
65
  * [Future](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/Future.html): An asynchronous operation that produces a value.
66
66
  * [Dataflow](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent.html#dataflow-class_method): Built on Futures, Dataflow allows you to create a task that will be scheduled when all of its data dependencies are available.
67
67
  * [Promise](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/Promise.html): Similar to Futures, with more features.
@@ -79,11 +79,9 @@ Collection classes that were originally part of the (deprecated) `thread_safe` g
79
79
 
80
80
  Value objects inspired by other languages:
81
81
 
82
- * [Atom](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/Atom.html): A way to manage shared, synchronous, independent state.
83
82
  * [Maybe](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/Maybe.html) A thread-safe, immutable object representing an optional value, based on
84
83
  [Haskell Data.Maybe](https://hackage.haskell.org/package/base-4.2.0.1/docs/Data-Maybe.html).
85
- * [Delay](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/Delay.html) Lazy evaluation of a block yielding an immutable result. Based on Clojure's
86
- [delay](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/Delay.html).
84
+ * [Delay](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/Delay.html) Lazy evaluation of a block yielding an immutable result. Based on Clojure's [delay](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/Delay.html).
87
85
 
88
86
  Structure classes derived from Ruby's [Struct](http://ruby-doc.org/core-2.2.0/Struct.html):
89
87
 
@@ -93,10 +91,15 @@ Structure classes derived from Ruby's [Struct](http://ruby-doc.org/core-2.2.0/St
93
91
 
94
92
  Thread-safe variables:
95
93
 
94
+ * [Agent](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/Agent.html): A way to manage shared, mutable, *asynchronous*, independent, state. Based on Clojure's [Agent](http://clojure.org/agents).
95
+ * [Atom](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/Atom.html): A way to manage shared, mutable, *synchronous*, independent state. Based on Clojure's [Atom](http://clojure.org/atoms).
96
96
  * [AtomicBoolean](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/AtomicBoolean.html) A boolean value that can be updated atomically.
97
97
  * [AtomicFixnum](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/AtomicFixnum.html) A numeric value that can be updated atomically.
98
98
  * [AtomicReference](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/MutexAtomic.html) An object reference that may be updated atomically.
99
+ * [Exchanger](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/Exchanger.html) A synchronization point at which threads can pair and swap elements within pairs. Based on Java's [Exchanger](http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Exchanger.html).
100
+ * [MVar](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/MVar.html) A synchronized single element container. Based on Haskell's [MVar](https://hackage.haskell.org/package/base-4.8.1.0/docs/Control-Concurrent-MVar.html) and Scala's [MVar](http://docs.typelevel.org/api/scalaz/nightly/index.html#scalaz.concurrent.MVar$).
99
101
  * [ThreadLocalVar](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/ThreadLocalVar.html) A variable where the value is different for each thread.
102
+ * [TVar](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/TVar.html) A transactional variable implementing software transactional memory (STM). Based on Clojure's [Ref](http://clojure.org/refs).
100
103
 
101
104
  #### Java-inspired ThreadPools and Other Executors
102
105
 
@@ -104,16 +107,13 @@ Thread-safe variables:
104
107
 
105
108
  #### Thread Synchronization Classes and Algorithms
106
109
 
107
- * [CountdownLatch](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/CountDownLatch.html) A synchronization object that allows one thread to wait on multiple other threads.
110
+ * [CountDownLatch](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/CountDownLatch.html) A synchronization object that allows one thread to wait on multiple other threads.
108
111
  * [CyclicBarrier](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/CyclicBarrier.html) A synchronization aid that allows a set of threads to all wait for each other to reach a common barrier point.
109
112
  * [Event](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/Event.html) Old school kernel-style event.
110
- * [Exchanger](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/Exchanger.html)
111
- * [I-Structure](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/IVar.html) (IVar) Similar to a "future" but can be manually assigned once, after which it becomes immutable.
112
- * [M-Structure](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/MVar.html) (MVar) A synchronized single element container.
113
+ * [IVar](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/IVar.html) Similar to a "future" but can be manually assigned once, after which it becomes immutable.
113
114
  * [ReadWriteLock](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/ReadWriteLock.html) A lock that supports multiple readers but only one writer.
114
115
  * [ReentrantReadWriteLock](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/ReentrantReadWriteLock.html) A read/write lock with reentrant and upgrade features.
115
116
  * [Semaphore](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/Semaphore.html) A counting-based locking mechanism that uses permits.
116
- * [Software transactional memory](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/TVar.html) (TVar) A transactional variable - a single-element container that is used as part of a transaction.
117
117
 
118
118
  ### Edge Features
119
119
 
@@ -125,12 +125,11 @@ be obeyed though. Features developed in `concurrent-ruby-edge` are expected to m
125
125
 
126
126
  * [Actor](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/Actor.html):
127
127
  Implements the Actor Model, where concurrent actors exchange messages.
128
- * [new Future Framework](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/Edge/FutureShortcuts.html) - new
129
- unified implementation of Futures and Promises which combines Features of previous `Future`,
130
- `Promise`, `IVar`, `Event`, `Probe`, `dataflow`, `Delay`, `TimerTask` into single framework. It uses extensively
131
- new synchronization layer to make all the features **non-blocking** and **lock-free** with exception of obviously blocking
128
+ * [New Future Framework](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/Edge/FutureShortcuts.html):
129
+ Unified implementation of futures and promises which combines features of previous `Future`,
130
+ `Promise`, `IVar`, `Event`, `dataflow`, `Delay`, and `TimerTask` into a single framework. It extensively uses the
131
+ new synchronization layer to make all the features **non-blocking** and **lock-free**, with the exception of obviously blocking
132
132
  operations like `#wait`, `#value`. It also offers better performance.
133
- * [Agent](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/Agent.html): A single atomic value that represents an identity.
134
133
  * [Channel](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/Channel.html):
135
134
  Communicating Sequential Processes (CSP).
136
135
  * [LazyRegister](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/LazyRegister.html)
@@ -142,12 +141,11 @@ be obeyed though. Features developed in `concurrent-ruby-edge` are expected to m
142
141
 
143
142
  *Why are these not in core?*
144
143
 
145
- - **Actor** - Partial documentation and tests; stability is good.
144
+ - **Actor** - Partial documentation and tests; depends on new future/promise framework; stability is good.
146
145
  - **Future/Promise Framework** - API changes; partial documentation and tests; stability good.
147
- - **Agent** - Incomplete behaviour compared to Clojure's models; stability good.
148
- - **Channel** - Missing documentation; limted features; stability good.
146
+ - **Channel** - Missing documentation; limited features; stability good.
149
147
  - **LazyRegister** - Missing documentation and tests.
150
- - **AtomicMarkableReference, LockFreeLinkedSet, LockFreeStack** - Needs real world battle testing
148
+ - **AtomicMarkableReference, LockFreeLinkedSet, LockFreeStack** - Need real world battle testing
151
149
 
152
150
  ## Usage
153
151
 
@@ -15,7 +15,7 @@ module Concurrent
15
15
  # > {include:Actor::RestartingContext}
16
16
  #
17
17
  # Example of ac actor definition:
18
- #
18
+ #
19
19
  # {include:file:doc/actor/define.out.rb}
20
20
  #
21
21
  # See methods of {AbstractContext} what else can be tweaked, e.g {AbstractContext#default_reference_class}
@@ -10,7 +10,7 @@ module Concurrent
10
10
  # @note Whole class should be considered private. An user should use {Context}s and {Reference}s only.
11
11
  # @note devel: core should not block on anything, e.g. it cannot wait on children to terminate
12
12
  # that would eat up all threads in task pool and deadlock
13
- class Core < Synchronization::Object
13
+ class Core < Synchronization::LockableObject
14
14
  include TypeCheck
15
15
  include Concern::Logging
16
16
 
@@ -5,7 +5,7 @@ module Concurrent
5
5
 
6
6
  # @api Channel
7
7
  # @!macro edge_warning
8
- class BlockingRingBuffer < Synchronization::Object
8
+ class BlockingRingBuffer < Synchronization::LockableObject
9
9
 
10
10
  def initialize(capacity)
11
11
  super()
@@ -6,7 +6,7 @@ module Concurrent
6
6
 
7
7
  # @api Channel
8
8
  # @!macro edge_warning
9
- class BufferedChannel < Synchronization::Object
9
+ class BufferedChannel < Synchronization::LockableObject
10
10
 
11
11
  def initialize(size)
12
12
  super()
@@ -14,7 +14,7 @@ module Concurrent
14
14
  end
15
15
 
16
16
  def probe_set_size
17
- @probe_set.size
17
+ @probe_set.size # TODO (pitr 12-Sep-2015): unsafe?
18
18
  end
19
19
 
20
20
  def buffer_queue_size
@@ -5,7 +5,7 @@ module Concurrent
5
5
 
6
6
  # @api Channel
7
7
  # @!macro edge_warning
8
- class WaitableList < Synchronization::Object
8
+ class WaitableList < Synchronization::LockableObject
9
9
 
10
10
  def initialize
11
11
  super()
@@ -11,11 +11,11 @@ module Concurrent
11
11
  # @api Edge
12
12
  class AtomicMarkableReference < ::Concurrent::Synchronization::Object
13
13
 
14
+ private *attr_volatile_with_cas(:reference)
15
+
14
16
  # @!macro [attach] atomic_markable_reference_method_initialize
15
17
  def initialize(value = nil, mark = false)
16
- super()
17
- @Reference = AtomicReference.new ImmutableArray[value, mark]
18
- ensure_ivar_visibility!
18
+ super(ImmutableArray[value, mark]) # ensures visibility
19
19
  end
20
20
 
21
21
  # @!macro [attach] atomic_markable_reference_method_compare_and_set
@@ -36,7 +36,7 @@ module Concurrent
36
36
  def compare_and_set(expected_val, new_val, expected_mark, new_mark)
37
37
  # Memoize a valid reference to the current AtomicReference for
38
38
  # later comparison.
39
- current = @Reference.get
39
+ current = reference
40
40
  curr_val, curr_mark = current
41
41
 
42
42
  # Ensure that that the expected marks match.
@@ -56,7 +56,7 @@ module Concurrent
56
56
 
57
57
  prospect = ImmutableArray[new_val, new_mark]
58
58
 
59
- @Reference.compare_and_set current, prospect
59
+ compare_and_set_reference current, prospect
60
60
  end
61
61
  alias_method :compare_and_swap, :compare_and_set
62
62
 
@@ -66,7 +66,7 @@ module Concurrent
66
66
  #
67
67
  # @return [ImmutableArray] the current reference and marked values
68
68
  def get
69
- @Reference.get
69
+ reference
70
70
  end
71
71
 
72
72
  # @!macro [attach] atomic_markable_reference_method_value
@@ -75,7 +75,7 @@ module Concurrent
75
75
  #
76
76
  # @return [Object] the current value of the reference
77
77
  def value
78
- @Reference.get[0]
78
+ reference[0]
79
79
  end
80
80
 
81
81
  # @!macro [attach] atomic_markable_reference_method_mark
@@ -84,7 +84,7 @@ module Concurrent
84
84
  #
85
85
  # @return [Boolean] the current marked value
86
86
  def mark
87
- @Reference.get[1]
87
+ reference[1]
88
88
  end
89
89
  alias_method :marked?, :mark
90
90
 
@@ -98,7 +98,7 @@ module Concurrent
98
98
  #
99
99
  # @return [ImmutableArray] both the new value and the new mark
100
100
  def set(new_val, new_mark)
101
- @Reference.set ImmutableArray[new_val, new_mark]
101
+ self.reference = ImmutableArray[new_val, new_mark]
102
102
  end
103
103
 
104
104
  # @!macro [attach] atomic_markable_reference_method_update
@@ -115,7 +115,7 @@ module Concurrent
115
115
  # @return [ImmutableArray] the new value and new mark
116
116
  def update
117
117
  loop do
118
- old_val, old_mark = @Reference.get
118
+ old_val, old_mark = reference
119
119
  new_val, new_mark = yield old_val, old_mark
120
120
 
121
121
  if compare_and_set old_val, new_val, old_mark, new_mark
@@ -139,7 +139,7 @@ module Concurrent
139
139
  #
140
140
  # @raise [Concurrent::ConcurrentUpdateError] if the update fails
141
141
  def try_update!
142
- old_val, old_mark = @Reference.get
142
+ old_val, old_mark = reference
143
143
  new_val, new_mark = yield old_val, old_mark
144
144
 
145
145
  unless compare_and_set old_val, new_val, old_mark, new_mark
@@ -165,7 +165,7 @@ module Concurrent
165
165
  # @return [ImmutableArray] the new value and marked state, or nil if
166
166
  # the update failed
167
167
  def try_update
168
- old_val, old_mark = @Reference.get
168
+ old_val, old_mark = reference
169
169
  new_val, new_mark = yield old_val, old_mark
170
170
 
171
171
  return unless compare_and_set old_val, new_val, old_mark, new_mark
@@ -125,7 +125,8 @@ module Concurrent
125
125
  include FutureShortcuts
126
126
 
127
127
  # Represents an event which will happen in future (will be completed). It has to always happen.
128
- class Event < Synchronization::Object
128
+ class Event < Synchronization::LockableObject
129
+ safe_initialization!
129
130
  include Concern::Deprecation
130
131
 
131
132
  # @!visibility private
@@ -167,14 +168,15 @@ module Concurrent
167
168
  COMPLETED = Completed.new
168
169
 
169
170
  def initialize(promise, default_executor)
171
+ super()
170
172
  @Promise = promise
171
173
  @DefaultExecutor = default_executor
172
174
  @Touched = AtomicBoolean.new(false)
173
175
  @Callbacks = LockFreeStack.new
174
- @Waiters = LockFreeStack.new # TODO replace with AtomicFixnum, avoid aba problem
176
+ # TODO (pitr 12-Sep-2015): replace with AtomicFixnum, avoid aba problem
177
+ # TODO (pitr 12-Sep-2015): look at java.util.concurrent solution
178
+ @Waiters = LockFreeStack.new
175
179
  @State = AtomicReference.new PENDING
176
- super()
177
- ensure_ivar_visibility!
178
180
  end
179
181
 
180
182
  # @return [:pending, :completed]
@@ -878,9 +880,11 @@ module Concurrent
878
880
  # @abstract
879
881
  # @!visibility private
880
882
  class AbstractPromise < Synchronization::Object
883
+ safe_initialization!
884
+
881
885
  def initialize(future)
886
+ super()
882
887
  @Future = future
883
- ensure_ivar_visibility!
884
888
  end
885
889
 
886
890
  def future
@@ -1374,11 +1378,12 @@ module Concurrent
1374
1378
 
1375
1379
  # @note proof of concept
1376
1380
  class Channel < Synchronization::Object
1381
+ safe_initialization!
1382
+
1377
1383
  # TODO make lock free
1378
1384
  def initialize
1379
- super
1385
+ super()
1380
1386
  @ProbeSet = Concurrent::Channel::WaitableList.new
1381
- ensure_ivar_visibility!
1382
1387
  end
1383
1388
 
1384
1389
  def probe_set_size
@@ -55,7 +55,7 @@ module Concurrent
55
55
 
56
56
  node = Node.new item, curr
57
57
 
58
- if pred.Successor_reference.compare_and_set curr, node, false, false
58
+ if pred.successor_reference.compare_and_set curr, node, false, false
59
59
  return true
60
60
  end
61
61
  end
@@ -88,7 +88,7 @@ module Concurrent
88
88
 
89
89
  while curr < item
90
90
  curr = curr.next_node
91
- marked = curr.Successor_reference.marked?
91
+ marked = curr.successor_reference.marked?
92
92
  end
93
93
 
94
94
  curr == item && !marked
@@ -109,11 +109,11 @@ module Concurrent
109
109
  return false if curr != item
110
110
 
111
111
  succ = curr.next_node
112
- removed = curr.Successor_reference.compare_and_set succ, succ, false, true
112
+ removed = curr.successor_reference.compare_and_set succ, succ, false, true
113
113
 
114
114
  next_node unless removed
115
115
 
116
- pred.Successor_reference.compare_and_set curr, succ, false, false
116
+ pred.successor_reference.compare_and_set curr, succ, false, false
117
117
 
118
118
  return true
119
119
  end
@@ -134,9 +134,9 @@ module Concurrent
134
134
 
135
135
  until curr.last?
136
136
  curr = curr.next_node
137
- marked = curr.Successor_reference.marked?
137
+ marked = curr.successor_reference.marked?
138
138
 
139
- yield curr.Data unless marked
139
+ yield curr.data unless marked
140
140
  end
141
141
 
142
142
  self
@@ -6,27 +6,36 @@ module Concurrent
6
6
  class Node < Synchronization::Object
7
7
  include Comparable
8
8
 
9
- attr_reader :Data, :Successor_reference, :Key
9
+ safe_initialization!
10
10
 
11
11
  def initialize(data = nil, successor = nil)
12
12
  super()
13
+ @SuccessorReference = AtomicMarkableReference.new(successor || Tail.new)
14
+ @Data = data
15
+ @Key = key_for data
16
+ end
17
+
18
+ def data
19
+ @Data
20
+ end
13
21
 
14
- @Successor_reference = AtomicMarkableReference.new(successor || Tail.new)
15
- @Data = data
16
- @Key = key_for data
22
+ def successor_reference
23
+ @SuccessorReference
24
+ end
17
25
 
18
- ensure_ivar_visibility!
26
+ def key
27
+ @Key
19
28
  end
20
29
 
21
30
  # Check to see if the node is the last in the list.
22
31
  def last?
23
- @Successor_reference.value.is_a? Tail
32
+ @SuccessorReference.value.is_a? Tail
24
33
  end
25
34
 
26
35
  # Next node in the list. Note: this is not the AtomicMarkableReference
27
36
  # of the next node, this is the actual Node itself.
28
37
  def next_node
29
- @Successor_reference.value
38
+ @SuccessorReference.value
30
39
  end
31
40
 
32
41
  # This method provides a unqiue key for the data which will be used for
@@ -49,7 +58,7 @@ module Concurrent
49
58
  # a self-loop.
50
59
  class Tail < Node
51
60
  def initialize(_data = nil, _succ = nil)
52
- @Successor_reference = AtomicMarkableReference.new self
61
+ @SuccessorReference = AtomicMarkableReference.new self
53
62
  end
54
63
 
55
64
  # Always greater than other nodes. This means that traversal will end
@@ -20,17 +20,17 @@ module Concurrent
20
20
  curr = pred.next_node
21
21
 
22
22
  loop do
23
- succ, marked = curr.Successor_reference.get
23
+ succ, marked = curr.successor_reference.get
24
24
 
25
25
  # Remove sequence of marked nodes
26
26
  while marked
27
- removed = pred.Successor_reference.compare_and_set curr, succ, false, false
27
+ removed = pred.successor_reference.compare_and_set curr, succ, false, false
28
28
 
29
29
  # If could not remove node, try again
30
30
  break_inner_loops = true && break unless removed
31
31
 
32
32
  curr = succ
33
- succ, marked = curr.Successor_reference.get
33
+ succ, marked = curr.successor_reference.get
34
34
  end
35
35
 
36
36
  break if break_inner_loops
@@ -2,6 +2,8 @@ module Concurrent
2
2
  module Edge
3
3
  class LockFreeStack < Synchronization::Object
4
4
 
5
+ safe_initialization!
6
+
5
7
  class Node
6
8
  attr_reader :value, :next_node
7
9
 
@@ -21,50 +23,51 @@ module Concurrent
21
23
 
22
24
  EMPTY = Empty[nil, nil]
23
25
 
26
+ private *attr_volatile_with_cas(:head)
27
+
24
28
  def initialize
25
- @Head = AtomicReference.new EMPTY
26
- ensure_ivar_visibility!
29
+ super(EMPTY)
27
30
  end
28
31
 
29
32
  def empty?
30
- @Head.get.equal? EMPTY
33
+ head.equal? EMPTY
31
34
  end
32
35
 
33
36
  def compare_and_push(head, value)
34
- @Head.compare_and_set head, Node[value, head]
37
+ compare_and_set_head head, Node[value, head]
35
38
  end
36
39
 
37
40
  def push(value)
38
41
  while true
39
- head = @Head.get
40
- return self if @Head.compare_and_set head, Node[value, head]
42
+ current_head = head
43
+ return self if compare_and_set_head current_head, Node[value, current_head]
41
44
  end
42
45
  end
43
46
 
44
47
  def peek
45
- @Head.get
48
+ head
46
49
  end
47
50
 
48
51
  def compare_and_pop(head)
49
- @Head.compare_and_set head, head.next_node
52
+ compare_and_set_head head, head.next_node
50
53
  end
51
54
 
52
55
  def pop
53
56
  while true
54
- head = @Head.get
55
- return head.value if @Head.compare_and_set head, head.next_node
57
+ current_head = head
58
+ return current_head.value if compare_and_set_head current_head, current_head.next_node
56
59
  end
57
60
  end
58
61
 
59
62
  def compare_and_clear(head)
60
- @Head.compare_and_set head, EMPTY
63
+ compare_and_set_head head, EMPTY
61
64
  end
62
65
 
63
66
  def clear
64
67
  while true
65
- head = @Head.get
66
- return false if head == EMPTY
67
- return true if @Head.compare_and_set head, EMPTY
68
+ current_head = head
69
+ return false if current_head == EMPTY
70
+ return true if compare_and_set_head current_head, EMPTY
68
71
  end
69
72
  end
70
73
 
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.pre1
4
+ version: 0.2.0.pre2
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-08-19 00:00:00.000000000 Z
13
+ date: 2015-09-19 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.pre1
21
+ version: 1.0.0.pre2
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.pre1
28
+ version: 1.0.0.pre2
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
@@ -71,7 +71,6 @@ files:
71
71
  - lib/concurrent/actor/utils/balancer.rb
72
72
  - lib/concurrent/actor/utils/broadcast.rb
73
73
  - lib/concurrent/actor/utils/pool.rb
74
- - lib/concurrent/agent.rb
75
74
  - lib/concurrent/channel.rb
76
75
  - lib/concurrent/channel/blocking_ring_buffer.rb
77
76
  - lib/concurrent/channel/buffered_channel.rb
@@ -105,7 +104,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
105
104
  version: 1.3.1
106
105
  requirements: []
107
106
  rubyforge_project:
108
- rubygems_version: 2.4.8
107
+ rubygems_version: 2.4.5.1
109
108
  signing_key:
110
109
  specification_version: 4
111
110
  summary: Edge features and additions to the concurrent-ruby gem.
@@ -1,259 +0,0 @@
1
- require 'concurrent/collection/copy_on_write_observer_set'
2
- require 'concurrent/concern/dereferenceable'
3
- require 'concurrent/concern/observable'
4
- require 'concurrent/concern/logging'
5
- require 'concurrent/executor/executor'
6
- require 'concurrent/synchronization'
7
-
8
- module Concurrent
9
-
10
- # `Agent`s are inspired by [Clojure's](http://clojure.org/) [agent](http://clojure.org/agents) function. An `Agent` is a single atomic value that represents an identity. The current value of the `Agent` can be requested at any time (`deref`). Each `Agent` has a work queue and operates on the global thread pool (see below). Consumers can `post` code blocks to the `Agent`. The code block (function) will receive the current value of the `Agent` as its sole parameter. The return value of the block will become the new value of the `Agent`. `Agent`s support two error handling modes: fail and continue. A good example of an `Agent` is a shared incrementing counter, such as the score in a video game.
11
- #
12
- # An `Agent` must be initialize with an initial value. This value is always accessible via the `value` (or `deref`) methods. Code blocks sent to the `Agent` will be processed in the order received. As each block is processed the current value is updated with the result from the block. This update is an atomic operation so a `deref` will never block and will always return the current value.
13
- #
14
- # When an `Agent` is created it may be given an optional `validate` block and zero or more `rescue` blocks. When a new value is calculated the value will be checked against the validator, if present. If the validator returns `true` the new value will be accepted. If it returns `false` it will be rejected. If a block raises an exception during execution the list of `rescue` blocks will be seacrhed in order until one matching the current exception is found. That `rescue` block will then be called an passed the exception object. If no matching `rescue` block is found, or none were configured, then the exception will be suppressed.
15
- #
16
- # `Agent`s also implement Ruby's [Observable](http://ruby-doc.org/stdlib-2.0/libdoc/observer/rdoc/Observable.html). Code that observes an `Agent` will receive a callback with the new value any time the value is changed.
17
- #
18
- # @!macro copy_options
19
- #
20
- # @example Simple Example
21
- #
22
- # require 'concurrent'
23
- #
24
- # score = Concurrent::Agent.new(10)
25
- # score.value #=> 10
26
- #
27
- # score << proc{|current| current + 100 }
28
- # sleep(0.1)
29
- # score.value #=> 110
30
- #
31
- # score << proc{|current| current * 2 }
32
- # sleep(0.1)
33
- # score.value #=> 220
34
- #
35
- # score << proc{|current| current - 50 }
36
- # sleep(0.1)
37
- # score.value #=> 170
38
- #
39
- # @example With Validation and Error Handling
40
- #
41
- # score = Concurrent::Agent.new(0).validate{|value| value <= 1024 }.
42
- # rescue(NoMethodError){|ex| puts "Bam!" }.
43
- # rescue(ArgumentError){|ex| puts "Pow!" }.
44
- # rescue{|ex| puts "Boom!" }
45
- # score.value #=> 0
46
- #
47
- # score << proc{|current| current + 2048 }
48
- # sleep(0.1)
49
- # score.value #=> 0
50
- #
51
- # score << proc{|current| raise ArgumentError }
52
- # sleep(0.1)
53
- # #=> puts "Pow!"
54
- # score.value #=> 0
55
- #
56
- # score << proc{|current| current + 100 }
57
- # sleep(0.1)
58
- # score.value #=> 100
59
- #
60
- # @example With Observation
61
- #
62
- # bingo = Class.new{
63
- # def update(time, score)
64
- # puts "Bingo! [score: #{score}, time: #{time}]" if score >= 100
65
- # end
66
- # }.new
67
- #
68
- # score = Concurrent::Agent.new(0)
69
- # score.add_observer(bingo)
70
- #
71
- # score << proc{|current| sleep(0.1); current += 30 }
72
- # score << proc{|current| sleep(0.1); current += 30 }
73
- # score << proc{|current| sleep(0.1); current += 30 }
74
- # score << proc{|current| sleep(0.1); current += 30 }
75
- #
76
- # sleep(1)
77
- # #=> Bingo! [score: 120, time: 2013-07-22 21:26:08 -0400]
78
- #
79
- # @!attribute [r] timeout
80
- # @return [Fixnum] the maximum number of seconds before an update is cancelled
81
- #
82
- # @!macro edge_warning
83
- class Agent < Synchronization::Object
84
- include Concern::Dereferenceable
85
- include Concern::Observable
86
- include Concern::Logging
87
-
88
- attr_reader :timeout, :io_executor, :fast_executor
89
-
90
- # Initialize a new Agent with the given initial value and provided options.
91
- #
92
- # @param [Object] initial the initial value
93
- #
94
- # @!macro executor_and_deref_options
95
- def initialize(initial, opts = {})
96
- super()
97
- synchronize { ns_initialize(initial, opts) }
98
- end
99
-
100
- # Specifies a block fast to be performed when an update fast raises
101
- # an exception. Rescue blocks will be checked in order they were added. The first
102
- # block for which the raised exception "is-a" subclass of the given `clazz` will
103
- # be called. If no `clazz` is given the block will match any caught exception.
104
- # This behavior is intended to be identical to Ruby's `begin/rescue/end` behavior.
105
- # Any number of rescue handlers can be added. If no rescue handlers are added then
106
- # caught exceptions will be suppressed.
107
- #
108
- # @param [Exception] clazz the class of exception to catch
109
- # @yield the block to be called when a matching exception is caught
110
- # @yieldparam [StandardError] ex the caught exception
111
- #
112
- # @example
113
- # score = Concurrent::Agent.new(0).
114
- # rescue(NoMethodError){|ex| puts "Bam!" }.
115
- # rescue(ArgumentError){|ex| puts "Pow!" }.
116
- # rescue{|ex| puts "Boom!" }
117
- #
118
- # score << proc{|current| raise ArgumentError }
119
- # sleep(0.1)
120
- # #=> puts "Pow!"
121
- def rescue(clazz = StandardError, &block)
122
- unless block.nil?
123
- synchronize { @rescuers << Rescuer.new(clazz, block) }
124
- end
125
- self
126
- end
127
-
128
- alias_method :catch, :rescue
129
- alias_method :on_error, :rescue
130
-
131
- # A block task to be performed after every update to validate if the new
132
- # value is valid. If the new value is not valid then the current value is not
133
- # updated. If no validator is provided then all updates are considered valid.
134
- #
135
- # @yield the block to be called after every update fast to determine if
136
- # the result is valid
137
- # @yieldparam [Object] value the result of the last update fast
138
- # @yieldreturn [Boolean] true if the value is valid else false
139
- def validate(&block)
140
-
141
- unless block.nil?
142
- synchronize { @validator = block }
143
- end
144
- self
145
- end
146
-
147
- alias_method :validates, :validate
148
- alias_method :validate_with, :validate
149
- alias_method :validates_with, :validate
150
-
151
- # Update the current value with the result of the given block fast,
152
- # block should not do blocking calls, use #post_off for blocking calls
153
- #
154
- # @yield the fast to be performed with the current value in order to calculate
155
- # the new value
156
- # @yieldparam [Object] value the current value
157
- # @yieldreturn [Object] the new value
158
- # @return [true, nil] nil when no block is given
159
- def post(&block)
160
- post_on(@fast_executor, &block)
161
- end
162
-
163
- # Update the current value with the result of the given block fast,
164
- # block can do blocking calls
165
- #
166
- # @yield the fast to be performed with the current value in order to calculate
167
- # the new value
168
- # @yieldparam [Object] value the current value
169
- # @yieldreturn [Object] the new value
170
- # @return [true, nil] nil when no block is given
171
- def post_off(&block)
172
- post_on(@io_executor, &block)
173
- end
174
-
175
- # Update the current value with the result of the given block fast,
176
- # block should not do blocking calls, use #post_off for blocking calls
177
- #
178
- # @yield the fast to be performed with the current value in order to calculate
179
- # the new value
180
- # @yieldparam [Object] value the current value
181
- # @yieldreturn [Object] the new value
182
- def <<(block)
183
- post(&block)
184
- self
185
- end
186
-
187
- # Waits/blocks until all the updates sent before this call are done.
188
- #
189
- # @param [Numeric] timeout the maximum time in second to wait.
190
- # @return [Boolean] false on timeout, true otherwise
191
- def await(timeout = nil)
192
- done = Event.new
193
- post { |val| done.set; val }
194
- done.wait timeout
195
- end
196
-
197
- protected
198
-
199
- def ns_initialize(initial, opts)
200
- init_mutex(self)
201
- @value = initial
202
- @rescuers = []
203
- @validator = Proc.new { |result| true }
204
- self.observers = Collection::CopyOnWriteObserverSet.new
205
- @serialized_execution = SerializedExecution.new
206
- @io_executor = Executor.executor_from_options(opts) || Concurrent.global_io_executor
207
- @fast_executor = Executor.executor_from_options(opts) || Concurrent.global_fast_executor
208
- set_deref_options(opts)
209
- end
210
-
211
- private
212
-
213
- def post_on(executor, &block)
214
- return nil if block.nil?
215
- @serialized_execution.post(executor) { work(&block) }
216
- true
217
- end
218
-
219
- # @!visibility private
220
- Rescuer = Struct.new(:clazz, :block) # :nodoc:
221
-
222
- # @!visibility private
223
- def try_rescue(ex) # :nodoc:
224
- rescuer = synchronize do
225
- @rescuers.find { |r| ex.is_a?(r.clazz) }
226
- end
227
- rescuer.block.call(ex) if rescuer
228
- rescue Exception => ex
229
- # suppress
230
- log DEBUG, ex
231
- end
232
-
233
- # @!visibility private
234
- def work(&handler) # :nodoc:
235
- validator, value = synchronize { [@validator, @value] }
236
-
237
- begin
238
- result = handler.call(value)
239
- valid = validator.call(result)
240
- rescue Exception => ex
241
- exception = ex
242
- end
243
-
244
- should_notify = synchronize do
245
- if !exception && valid
246
- @value = result
247
- true
248
- end
249
- end
250
-
251
- if should_notify
252
- time = Time.now
253
- observers.notify_observers { [time, self.value] }
254
- end
255
-
256
- try_rescue(exception)
257
- end
258
- end
259
- end