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

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