concurrent-ruby 1.0.0.pre1-java → 1.0.0.pre2-java

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.
Files changed (77) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +14 -1
  3. data/README.md +16 -18
  4. data/lib/concurrent.rb +3 -3
  5. data/lib/concurrent/agent.rb +583 -0
  6. data/lib/concurrent/array.rb +1 -0
  7. data/lib/concurrent/async.rb +236 -111
  8. data/lib/concurrent/atom.rb +101 -46
  9. data/lib/concurrent/atomic/atomic_boolean.rb +2 -0
  10. data/lib/concurrent/atomic/atomic_fixnum.rb +2 -0
  11. data/lib/concurrent/atomic/cyclic_barrier.rb +1 -1
  12. data/lib/concurrent/atomic/event.rb +1 -1
  13. data/lib/concurrent/atomic/mutex_atomic_boolean.rb +1 -1
  14. data/lib/concurrent/atomic/mutex_atomic_fixnum.rb +1 -1
  15. data/lib/concurrent/atomic/mutex_count_down_latch.rb +1 -1
  16. data/lib/concurrent/atomic/mutex_semaphore.rb +2 -2
  17. data/lib/concurrent/atomic/read_write_lock.rb +5 -4
  18. data/lib/concurrent/atomic/reentrant_read_write_lock.rb +3 -1
  19. data/lib/concurrent/atomic/thread_local_var.rb +2 -0
  20. data/lib/concurrent/atomic_reference/mutex_atomic.rb +1 -1
  21. data/lib/concurrent/atomics.rb +6 -4
  22. data/lib/concurrent/collection/copy_on_notify_observer_set.rb +7 -15
  23. data/lib/concurrent/collection/copy_on_write_observer_set.rb +7 -15
  24. data/lib/concurrent/collection/map/atomic_reference_map_backend.rb +5 -0
  25. data/lib/concurrent/concern/observable.rb +38 -13
  26. data/lib/concurrent/configuration.rb +5 -4
  27. data/lib/concurrent/delay.rb +9 -8
  28. data/lib/concurrent/exchanger.rb +2 -0
  29. data/lib/concurrent/executor/abstract_executor_service.rb +2 -2
  30. data/lib/concurrent/executor/java_single_thread_executor.rb +0 -1
  31. data/lib/concurrent/executor/ruby_executor_service.rb +10 -4
  32. data/lib/concurrent/executor/ruby_single_thread_executor.rb +10 -68
  33. data/lib/concurrent/executor/safe_task_executor.rb +7 -8
  34. data/lib/concurrent/executor/serialized_execution.rb +4 -4
  35. data/lib/concurrent/executor/single_thread_executor.rb +20 -10
  36. data/lib/concurrent/executor/timer_set.rb +4 -2
  37. data/lib/concurrent/executors.rb +0 -1
  38. data/lib/concurrent/future.rb +3 -2
  39. data/lib/concurrent/hash.rb +1 -1
  40. data/lib/concurrent/immutable_struct.rb +5 -1
  41. data/lib/concurrent/ivar.rb +1 -1
  42. data/lib/concurrent/mutable_struct.rb +7 -6
  43. data/lib/concurrent/{executor/executor.rb → options.rb} +4 -3
  44. data/lib/concurrent/promise.rb +3 -2
  45. data/lib/concurrent/scheduled_task.rb +3 -2
  46. data/lib/concurrent/settable_struct.rb +5 -4
  47. data/lib/concurrent/synchronization.rb +11 -3
  48. data/lib/concurrent/synchronization/abstract_lockable_object.rb +117 -0
  49. data/lib/concurrent/synchronization/abstract_object.rb +16 -129
  50. data/lib/concurrent/synchronization/abstract_struct.rb +2 -3
  51. data/lib/concurrent/synchronization/condition.rb +6 -4
  52. data/lib/concurrent/synchronization/jruby_lockable_object.rb +13 -0
  53. data/lib/concurrent/synchronization/{java_object.rb → jruby_object.rb} +5 -3
  54. data/lib/concurrent/synchronization/lock.rb +3 -2
  55. data/lib/concurrent/synchronization/lockable_object.rb +59 -0
  56. data/lib/concurrent/synchronization/mri_lockable_object.rb +71 -0
  57. data/lib/concurrent/synchronization/mri_object.rb +35 -0
  58. data/lib/concurrent/synchronization/object.rb +111 -39
  59. data/lib/concurrent/synchronization/rbx_lockable_object.rb +64 -0
  60. data/lib/concurrent/synchronization/rbx_object.rb +17 -68
  61. data/lib/concurrent/thread_safe/util.rb +0 -9
  62. data/lib/concurrent/thread_safe/util/adder.rb +3 -0
  63. data/lib/concurrent/thread_safe/util/array_hash_rbx.rb +3 -1
  64. data/lib/concurrent/thread_safe/util/cheap_lockable.rb +3 -0
  65. data/lib/concurrent/thread_safe/util/power_of_two_tuple.rb +1 -0
  66. data/lib/concurrent/thread_safe/util/striped64.rb +6 -1
  67. data/lib/concurrent/thread_safe/util/volatile.rb +2 -0
  68. data/lib/concurrent/thread_safe/util/xor_shift_random.rb +2 -0
  69. data/lib/concurrent/tvar.rb +36 -0
  70. data/lib/concurrent/utility/at_exit.rb +1 -1
  71. data/lib/concurrent/utility/monotonic_time.rb +3 -4
  72. data/lib/concurrent/utility/native_extension_loader.rb +1 -1
  73. data/lib/concurrent/version.rb +2 -2
  74. data/lib/concurrent_ruby_ext.jar +0 -0
  75. metadata +11 -6
  76. data/lib/concurrent/synchronization/monitor_object.rb +0 -27
  77. data/lib/concurrent/synchronization/mutex_object.rb +0 -43
@@ -1,6 +1,7 @@
1
- require 'concurrent/concern/dereferenceable'
2
1
  require 'concurrent/atomic/atomic_reference'
3
- require 'concurrent/synchronization/object'
2
+ require 'concurrent/collection/copy_on_notify_observer_set'
3
+ require 'concurrent/concern/observable'
4
+ require 'concurrent/synchronization'
4
5
 
5
6
  module Concurrent
6
7
 
@@ -18,11 +19,50 @@ module Concurrent
18
19
  # new value to the result of running the given block if and only if that
19
20
  # value validates.
20
21
  #
21
- # @!macro copy_options
22
+ # ## Example
23
+ #
24
+ # ```
25
+ # def next_fibonacci(set = nil)
26
+ # return [0, 1] if set.nil?
27
+ # set + [set[-2..-1].reduce{|sum,x| sum + x }]
28
+ # end
29
+ #
30
+ # # create an atom with aninitial value
31
+ # atom = Concurrent::Atom.new(next_fibonacci)
32
+ #
33
+ # # send a few update requests
34
+ # 5.times do
35
+ # atom.swap{|set| next_fibonacci(set) }
36
+ # end
37
+ #
38
+ # # get the current value
39
+ # atom.value #=> [0, 1, 1, 2, 3, 5, 8]
40
+ # ```
41
+ #
42
+ # ## Observation
43
+ #
44
+ # Atoms support observers through the {Concurrent::Observable} mixin module.
45
+ # Notification of observers occurs every time the value of the Atom changes.
46
+ # When notified the observer will receive three arguments: `time`, `old_value`,
47
+ # and `new_value`. The `time` argument is the time at which the value change
48
+ # occurred. The `old_value` is the value of the Atom when the change began
49
+ # The `new_value` is the value to which the Atom was set when the change
50
+ # completed. Note that `old_value` and `new_value` may be the same. This is
51
+ # not an error. It simply means that the change operation returned the same
52
+ # value.
53
+ #
54
+ # Unlike in Clojure, `Atom` cannot participate in {Concurrent::TVar} transactions.
55
+ #
56
+ # @!macro thread_safe_variable_comparison
22
57
  #
23
58
  # @see http://clojure.org/atoms Clojure Atoms
59
+ # @see http://clojure.org/state Values and Change - Clojure's approach to Identity and State
24
60
  class Atom < Synchronization::Object
25
- include Concern::Dereferenceable
61
+ include Concern::Observable
62
+
63
+ safe_initialization!
64
+ private *attr_volatile_with_cas(:value)
65
+ public :value
26
66
 
27
67
  # Create a new atom with the given initial value.
28
68
  #
@@ -34,25 +74,19 @@ module Concurrent
34
74
  # is acceptable else return false (preferrably) or raise an exception.
35
75
  #
36
76
  # @!macro deref_options
37
- #
77
+ #
38
78
  # @raise [ArgumentError] if the validator is not a `Proc` (when given)
39
79
  def initialize(value, opts = {})
40
- super()
41
-
42
- @validator = opts.fetch(:validator, ->(v){ true })
43
- raise ArgumentError.new('validator must be a proc') unless @validator.is_a? Proc
44
-
45
- @value = Concurrent::AtomicReference.new(value)
46
- ns_set_deref_options(opts)
47
- ensure_ivar_visibility!
80
+ @Validator = opts.fetch(:validator, -> v { true })
81
+ self.observers = Collection::CopyOnNotifyObserverSet.new
82
+ super(value)
48
83
  end
49
84
 
50
- # The current value of the atom.
85
+ # @!method value
86
+ # The current value of the atom.
51
87
  #
52
- # @return [Object] The current value.
53
- def value
54
- apply_deref_options(@value.value)
55
- end
88
+ # @return [Object] The current value.
89
+
56
90
  alias_method :deref, :value
57
91
 
58
92
  # Atomically swaps the value of atom using the given block. The current
@@ -68,7 +102,7 @@ module Concurrent
68
102
  # the application of the supplied block to a current value, atomically.
69
103
  # However, because the block might be called multiple times, it must be free
70
104
  # of side effects.
71
- #
105
+ #
72
106
  # @note The given block may be called multiple times, and thus should be free
73
107
  # of side effects.
74
108
  #
@@ -87,45 +121,66 @@ module Concurrent
87
121
  def swap(*args)
88
122
  raise ArgumentError.new('no block given') unless block_given?
89
123
 
90
- begin
91
- loop do
92
- old_value = @value.value
124
+ loop do
125
+ old_value = value
126
+ begin
93
127
  new_value = yield(old_value, *args)
94
- return old_value unless @validator.call(new_value)
95
- return new_value if compare_and_set!(old_value, new_value)
128
+ break old_value unless valid?(new_value)
129
+ break new_value if compare_and_set(old_value, new_value)
130
+ rescue
131
+ break old_value
96
132
  end
97
- rescue
98
- return @value.value
99
133
  end
100
134
  end
101
135
 
102
- # @!macro [attach] atom_compare_and_set
103
- # Atomically sets the value of atom to the new value if and only if the
104
- # current value of the atom is identical to the old value and the new
105
- # value successfully validates against the (optional) validator given
106
- # at construction.
136
+ # Atomically sets the value of atom to the new value if and only if the
137
+ # current value of the atom is identical to the old value and the new
138
+ # value successfully validates against the (optional) validator given
139
+ # at construction.
107
140
  #
108
- # @param [Object] old_value The expected current value.
109
- # @param [Object] new_value The intended new value.
141
+ # @param [Object] old_value The expected current value.
142
+ # @param [Object] new_value The intended new value.
110
143
  #
111
- # @return [Boolean] True if the value is changed else false.
144
+ # @return [Boolean] True if the value is changed else false.
112
145
  def compare_and_set(old_value, new_value)
113
- compare_and_set!(old_value, new_value)
114
- rescue
115
- false
146
+ if valid?(new_value) && compare_and_set_value(old_value, new_value)
147
+ observers.notify_observers(Time.now, old_value, new_value)
148
+ true
149
+ else
150
+ false
151
+ end
116
152
  end
117
153
 
118
- private
119
-
120
- # @!macro atom_compare_and_set
121
- # @raise [Exception] if the validator proc raises an exception
122
- # @!visibility private
123
- def compare_and_set!(old_value, new_value)
124
- if @validator.call(new_value) # may raise exception
125
- @value.compare_and_set(old_value, new_value)
154
+ # Atomically sets the value of atom to the new value without regard for the
155
+ # current value so long as the new value successfully validates against the
156
+ # (optional) validator given at construction.
157
+ #
158
+ # @param [Object] new_value The intended new value.
159
+ #
160
+ # @return [Object] The final value of the atom after all operations and
161
+ # validations are complete.
162
+ def reset(new_value)
163
+ old_value = value
164
+ if valid?(new_value)
165
+ self.value = new_value
166
+ observers.notify_observers(Time.now, old_value, new_value)
167
+ new_value
126
168
  else
127
- false
169
+ old_value
128
170
  end
129
171
  end
172
+
173
+ private
174
+
175
+ # Is the new value valid?
176
+ #
177
+ # @param [Object] new_value The intended new value.
178
+ # @return [Boolean] false if the validator function returns false or raises
179
+ # an exception else true
180
+ def valid?(new_value)
181
+ @Validator.call(new_value)
182
+ rescue
183
+ false
184
+ end
130
185
  end
131
186
  end
@@ -94,6 +94,8 @@ module Concurrent
94
94
  # boolean and thread-safe and guaranteed to succeed. Reads and writes may block
95
95
  # briefly but no explicit locking is required.
96
96
  #
97
+ # @!macro thread_safe_variable_comparison
98
+ #
97
99
  # Testing with ruby 2.1.2
98
100
  # Testing with Concurrent::MutexAtomicBoolean...
99
101
  # 2.790000 0.000000 2.790000 ( 2.791454)
@@ -111,6 +111,8 @@ module Concurrent
111
111
  # fixnum and thread-safe and guaranteed to succeed. Reads and writes may block
112
112
  # briefly but no explicit locking is required.
113
113
  #
114
+ # @!macro thread_safe_variable_comparison
115
+ #
114
116
  # Testing with ruby 2.1.2
115
117
  # Testing with Concurrent::MutexAtomicFixnum...
116
118
  # 3.130000 0.000000 3.130000 ( 3.136505)
@@ -4,7 +4,7 @@ module Concurrent
4
4
 
5
5
  # A synchronization aid that allows a set of threads to all wait for each
6
6
  # other to reach a common barrier point.
7
- class CyclicBarrier < Synchronization::Object
7
+ class CyclicBarrier < Synchronization::LockableObject
8
8
 
9
9
  # @!visibility private
10
10
  Generation = Struct.new(:status)
@@ -13,7 +13,7 @@ module Concurrent
13
13
  # `#reset` at any time once it has been set.
14
14
  #
15
15
  # @see http://msdn.microsoft.com/en-us/library/windows/desktop/ms682655.aspx
16
- class Event < Synchronization::Object
16
+ class Event < Synchronization::LockableObject
17
17
 
18
18
  # Creates a new `Event` in the unset state. Threads calling `#wait` on the
19
19
  # `Event` will block.
@@ -5,7 +5,7 @@ module Concurrent
5
5
  # @!macro atomic_boolean
6
6
  # @!visibility private
7
7
  # @!macro internal_implementation_note
8
- class MutexAtomicBoolean < Synchronization::Object
8
+ class MutexAtomicBoolean < Synchronization::LockableObject
9
9
 
10
10
  # @!macro atomic_boolean_method_initialize
11
11
  def initialize(initial = false)
@@ -5,7 +5,7 @@ module Concurrent
5
5
  # @!macro atomic_fixnum
6
6
  # @!visibility private
7
7
  # @!macro internal_implementation_note
8
- class MutexAtomicFixnum < Synchronization::Object
8
+ class MutexAtomicFixnum < Synchronization::LockableObject
9
9
 
10
10
  # http://stackoverflow.com/questions/535721/ruby-max-integer
11
11
  MIN_VALUE = -(2**(0.size * 8 - 2))
@@ -5,7 +5,7 @@ module Concurrent
5
5
  # @!macro count_down_latch
6
6
  # @!visibility private
7
7
  # @!macro internal_implementation_note
8
- class MutexCountDownLatch < Synchronization::Object
8
+ class MutexCountDownLatch < Synchronization::LockableObject
9
9
 
10
10
  # @!macro count_down_latch_method_initialize
11
11
  def initialize(count = 1)
@@ -5,7 +5,7 @@ module Concurrent
5
5
  # @!macro semaphore
6
6
  # @!visibility private
7
7
  # @!macro internal_implementation_note
8
- class MutexSemaphore < Synchronization::Object
8
+ class MutexSemaphore < Synchronization::LockableObject
9
9
 
10
10
  # @!macro semaphore_method_initialize
11
11
  def initialize(count)
@@ -78,7 +78,7 @@ module Concurrent
78
78
  # @raise [ArgumentError] if `@free` - `@reduction` is less than zero
79
79
  #
80
80
  # @return [nil]
81
- #
81
+ #
82
82
  # @!visibility private
83
83
  def reduce_permits(reduction)
84
84
  unless reduction.is_a?(Fixnum) && reduction >= 0
@@ -41,10 +41,12 @@ module Concurrent
41
41
  # @!visibility private
42
42
  MAX_WRITERS = RUNNING_WRITER - MAX_READERS - 1
43
43
 
44
- # Implementation notes:
44
+ safe_initialization!
45
+
46
+ # Implementation notes:
45
47
  # A goal is to make the uncontended path for both readers/writers lock-free
46
48
  # Only if there is reader-writer or writer-writer contention, should locks be used
47
- # Internal state is represented by a single integer ("counter"), and updated
49
+ # Internal state is represented by a single integer ("counter"), and updated
48
50
  # using atomic compare-and-swap operations
49
51
  # When the counter is 0, the lock is free
50
52
  # Each reader increments the counter by 1 when acquiring a read lock
@@ -54,11 +56,10 @@ module Concurrent
54
56
 
55
57
  # Create a new `ReadWriteLock` in the unlocked state.
56
58
  def initialize
59
+ super()
57
60
  @Counter = AtomicFixnum.new(0) # single integer which represents lock state
58
61
  @ReadLock = Synchronization::Lock.new
59
62
  @WriteLock = Synchronization::Lock.new
60
- ensure_ivar_visibility!
61
- super()
62
63
  end
63
64
 
64
65
  # Execute a block operation within a read lock.
@@ -99,13 +99,15 @@ module Concurrent
99
99
  # @!visibility private
100
100
  WRITE_LOCK_MASK = MAX_WRITERS
101
101
 
102
+ safe_initialization!
103
+
102
104
  # Create a new `ReentrantReadWriteLock` in the unlocked state.
103
105
  def initialize
106
+ super()
104
107
  @Counter = AtomicFixnum.new(0) # single integer which represents lock state
105
108
  @ReadQueue = Synchronization::Lock.new # used to queue waiting readers
106
109
  @WriteQueue = Synchronization::Lock.new # used to queue waiting writers
107
110
  @HeldCount = ThreadLocalVar.new(0) # indicates # of R & W locks held by this thread
108
- ensure_ivar_visibility!
109
111
  end
110
112
 
111
113
  # Execute a block operation within a read lock.
@@ -68,6 +68,8 @@ module Concurrent
68
68
  # A `ThreadLocalVar` is a variable where the value is different for each thread.
69
69
  # Each variable may have a default value, but when you modify the variable only
70
70
  # the current thread will ever see that change.
71
+ #
72
+ # @!macro thread_safe_variable_comparison
71
73
  #
72
74
  # @example
73
75
  # v = ThreadLocalVar.new(14)
@@ -8,7 +8,7 @@ module Concurrent
8
8
  #
9
9
  # @!visibility private
10
10
  # @!macro internal_implementation_note
11
- class MutexAtomicReference < Synchronization::Object
11
+ class MutexAtomicReference < Synchronization::LockableObject
12
12
  include Concurrent::AtomicDirectUpdate
13
13
  include Concurrent::AtomicNumericCompareAndSetWrapper
14
14
 
@@ -2,30 +2,32 @@
2
2
  #
3
3
  # An object reference that may be updated atomically.
4
4
  #
5
+ # @!macro thread_safe_variable_comparison
6
+ #
5
7
  # @see http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/atomic/AtomicReference.html
6
8
  # @see http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/atomic/package-summary.html
7
9
  #
8
10
  # @!method initialize
9
11
  # @!macro [new] atomic_reference_method_initialize
10
12
  # @param [Object] value The initial value.
11
- #
13
+ #
12
14
  # @!method get
13
15
  # @!macro [new] atomic_reference_method_get
14
16
  # Gets the current value.
15
17
  # @return [Object] the current value
16
- #
18
+ #
17
19
  # @!method set
18
20
  # @!macro [new] atomic_reference_method_set
19
21
  # Sets to the given value.
20
22
  # @param [Object] new_value the new value
21
23
  # @return [Object] the new value
22
- #
24
+ #
23
25
  # @!method get_and_set
24
26
  # @!macro [new] atomic_reference_method_get_and_set
25
27
  # Atomically sets to the given value and returns the old value.
26
28
  # @param [Object] new_value the new value
27
29
  # @return [Object] the old value
28
- #
30
+ #
29
31
  # @!method compare_and_set
30
32
  # @!macro [new] atomic_reference_method_compare_and_set
31
33
  #
@@ -7,23 +7,17 @@ module Concurrent
7
7
  # observers are added and removed from a thread safe collection; every time
8
8
  # a notification is required the internal data structure is copied to
9
9
  # prevent concurrency issues
10
- #
10
+ #
11
11
  # @api private
12
- class CopyOnNotifyObserverSet < Synchronization::Object
12
+ class CopyOnNotifyObserverSet < Synchronization::LockableObject
13
13
 
14
14
  def initialize
15
15
  super()
16
16
  synchronize { ns_initialize }
17
17
  end
18
18
 
19
- # Adds an observer to this set. If a block is passed, the observer will be
20
- # created by this method and no other params should be passed
21
- #
22
- # @param [Object] observer the observer to add
23
- # @param [Symbol] func the function to call on the observer during notification.
24
- # Default is :update
25
- # @return [Object] the added observer
26
- def add_observer(observer=nil, func=:update, &block)
19
+ # @!macro observable_add_observer
20
+ def add_observer(observer = nil, func = :update, &block)
27
21
  if observer.nil? && block.nil?
28
22
  raise ArgumentError, 'should pass observer as a first argument or block'
29
23
  elsif observer && block
@@ -41,8 +35,7 @@ module Concurrent
41
35
  end
42
36
  end
43
37
 
44
- # @param [Object] observer the observer to remove
45
- # @return [Object] the deleted observer
38
+ # @!macro observable_delete_observer
46
39
  def delete_observer(observer)
47
40
  synchronize do
48
41
  @observers.delete(observer)
@@ -50,8 +43,7 @@ module Concurrent
50
43
  end
51
44
  end
52
45
 
53
- # Deletes all observers
54
- # @return [CopyOnWriteObserverSet] self
46
+ # @!macro observable_delete_observers
55
47
  def delete_observers
56
48
  synchronize do
57
49
  @observers.clear
@@ -59,7 +51,7 @@ module Concurrent
59
51
  end
60
52
  end
61
53
 
62
- # @return [Integer] the observers count
54
+ # @!macro observable_count_observers
63
55
  def count_observers
64
56
  synchronize { @observers.count }
65
57
  end