concurrent-ruby 1.0.0.pre1 → 1.0.0.pre2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +14 -1
- data/README.md +16 -18
- data/lib/concurrent.rb +3 -3
- data/lib/concurrent/agent.rb +583 -0
- data/lib/concurrent/array.rb +1 -0
- data/lib/concurrent/async.rb +236 -111
- data/lib/concurrent/atom.rb +101 -46
- data/lib/concurrent/atomic/atomic_boolean.rb +2 -0
- data/lib/concurrent/atomic/atomic_fixnum.rb +2 -0
- data/lib/concurrent/atomic/cyclic_barrier.rb +1 -1
- data/lib/concurrent/atomic/event.rb +1 -1
- data/lib/concurrent/atomic/mutex_atomic_boolean.rb +1 -1
- data/lib/concurrent/atomic/mutex_atomic_fixnum.rb +1 -1
- data/lib/concurrent/atomic/mutex_count_down_latch.rb +1 -1
- data/lib/concurrent/atomic/mutex_semaphore.rb +2 -2
- data/lib/concurrent/atomic/read_write_lock.rb +5 -4
- data/lib/concurrent/atomic/reentrant_read_write_lock.rb +3 -1
- data/lib/concurrent/atomic/thread_local_var.rb +2 -0
- data/lib/concurrent/atomic_reference/mutex_atomic.rb +1 -1
- data/lib/concurrent/atomics.rb +6 -4
- data/lib/concurrent/collection/copy_on_notify_observer_set.rb +7 -15
- data/lib/concurrent/collection/copy_on_write_observer_set.rb +7 -15
- data/lib/concurrent/collection/map/atomic_reference_map_backend.rb +5 -0
- data/lib/concurrent/concern/observable.rb +38 -13
- data/lib/concurrent/configuration.rb +5 -4
- data/lib/concurrent/delay.rb +9 -8
- data/lib/concurrent/exchanger.rb +2 -0
- data/lib/concurrent/executor/abstract_executor_service.rb +2 -2
- data/lib/concurrent/executor/java_single_thread_executor.rb +0 -1
- data/lib/concurrent/executor/ruby_executor_service.rb +10 -4
- data/lib/concurrent/executor/ruby_single_thread_executor.rb +10 -68
- data/lib/concurrent/executor/safe_task_executor.rb +7 -8
- data/lib/concurrent/executor/serialized_execution.rb +4 -4
- data/lib/concurrent/executor/single_thread_executor.rb +20 -10
- data/lib/concurrent/executor/timer_set.rb +4 -2
- data/lib/concurrent/executors.rb +0 -1
- data/lib/concurrent/future.rb +3 -2
- data/lib/concurrent/hash.rb +1 -1
- data/lib/concurrent/immutable_struct.rb +5 -1
- data/lib/concurrent/ivar.rb +1 -1
- data/lib/concurrent/mutable_struct.rb +7 -6
- data/lib/concurrent/{executor/executor.rb → options.rb} +4 -3
- data/lib/concurrent/promise.rb +3 -2
- data/lib/concurrent/scheduled_task.rb +3 -2
- data/lib/concurrent/settable_struct.rb +5 -4
- data/lib/concurrent/synchronization.rb +11 -3
- data/lib/concurrent/synchronization/abstract_lockable_object.rb +117 -0
- data/lib/concurrent/synchronization/abstract_object.rb +16 -129
- data/lib/concurrent/synchronization/abstract_struct.rb +2 -3
- data/lib/concurrent/synchronization/condition.rb +6 -4
- data/lib/concurrent/synchronization/jruby_lockable_object.rb +13 -0
- data/lib/concurrent/synchronization/{java_object.rb → jruby_object.rb} +5 -3
- data/lib/concurrent/synchronization/lock.rb +3 -2
- data/lib/concurrent/synchronization/lockable_object.rb +59 -0
- data/lib/concurrent/synchronization/mri_lockable_object.rb +71 -0
- data/lib/concurrent/synchronization/mri_object.rb +35 -0
- data/lib/concurrent/synchronization/object.rb +111 -39
- data/lib/concurrent/synchronization/rbx_lockable_object.rb +64 -0
- data/lib/concurrent/synchronization/rbx_object.rb +17 -68
- data/lib/concurrent/thread_safe/util.rb +0 -9
- data/lib/concurrent/thread_safe/util/adder.rb +3 -0
- data/lib/concurrent/thread_safe/util/array_hash_rbx.rb +3 -1
- data/lib/concurrent/thread_safe/util/cheap_lockable.rb +3 -0
- data/lib/concurrent/thread_safe/util/power_of_two_tuple.rb +1 -0
- data/lib/concurrent/thread_safe/util/striped64.rb +6 -1
- data/lib/concurrent/thread_safe/util/volatile.rb +2 -0
- data/lib/concurrent/thread_safe/util/xor_shift_random.rb +2 -0
- data/lib/concurrent/tvar.rb +36 -0
- data/lib/concurrent/utility/at_exit.rb +1 -1
- data/lib/concurrent/utility/monotonic_time.rb +3 -4
- data/lib/concurrent/utility/native_extension_loader.rb +1 -1
- data/lib/concurrent/version.rb +2 -2
- metadata +12 -7
- data/lib/concurrent/synchronization/monitor_object.rb +0 -27
- data/lib/concurrent/synchronization/mutex_object.rb +0 -43
data/lib/concurrent/atom.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
|
-
require 'concurrent/concern/dereferenceable'
|
2
1
|
require 'concurrent/atomic/atomic_reference'
|
3
|
-
require 'concurrent/
|
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
|
-
#
|
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::
|
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
|
-
|
41
|
-
|
42
|
-
|
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
|
-
#
|
85
|
+
# @!method value
|
86
|
+
# The current value of the atom.
|
51
87
|
#
|
52
|
-
#
|
53
|
-
|
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
|
-
|
91
|
-
|
92
|
-
|
124
|
+
loop do
|
125
|
+
old_value = value
|
126
|
+
begin
|
93
127
|
new_value = yield(old_value, *args)
|
94
|
-
|
95
|
-
|
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
|
-
#
|
103
|
-
#
|
104
|
-
#
|
105
|
-
#
|
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
|
-
#
|
109
|
-
#
|
141
|
+
# @param [Object] old_value The expected current value.
|
142
|
+
# @param [Object] new_value The intended new value.
|
110
143
|
#
|
111
|
-
#
|
144
|
+
# @return [Boolean] True if the value is changed else false.
|
112
145
|
def compare_and_set(old_value, new_value)
|
113
|
-
|
114
|
-
|
115
|
-
|
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
|
-
|
119
|
-
|
120
|
-
#
|
121
|
-
#
|
122
|
-
#
|
123
|
-
|
124
|
-
|
125
|
-
|
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
|
-
|
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::
|
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::
|
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::
|
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::
|
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::
|
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::
|
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
|
-
|
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::
|
11
|
+
class MutexAtomicReference < Synchronization::LockableObject
|
12
12
|
include Concurrent::AtomicDirectUpdate
|
13
13
|
include Concurrent::AtomicNumericCompareAndSetWrapper
|
14
14
|
|
data/lib/concurrent/atomics.rb
CHANGED
@@ -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::
|
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
|
-
#
|
20
|
-
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
54
|
+
# @!macro observable_count_observers
|
63
55
|
def count_observers
|
64
56
|
synchronize { @observers.count }
|
65
57
|
end
|
@@ -6,23 +6,17 @@ module Concurrent
|
|
6
6
|
# A thread safe observer set implemented using copy-on-write approach:
|
7
7
|
# every time an observer is added or removed the whole internal data structure is
|
8
8
|
# duplicated and replaced with a new one.
|
9
|
-
#
|
9
|
+
#
|
10
10
|
# @api private
|
11
|
-
class CopyOnWriteObserverSet < Synchronization::
|
11
|
+
class CopyOnWriteObserverSet < Synchronization::LockableObject
|
12
12
|
|
13
13
|
def initialize
|
14
14
|
super()
|
15
15
|
synchronize { ns_initialize }
|
16
16
|
end
|
17
17
|
|
18
|
-
#
|
19
|
-
|
20
|
-
# other params should be passed
|
21
|
-
# @param [Object] observer the observer to add
|
22
|
-
# @param [Symbol] func the function to call on the observer during notification.
|
23
|
-
# Default is :update
|
24
|
-
# @return [Object] the added observer
|
25
|
-
def add_observer(observer=nil, func=:update, &block)
|
18
|
+
# @!macro observable_add_observer
|
19
|
+
def add_observer(observer = nil, func = :update, &block)
|
26
20
|
if observer.nil? && block.nil?
|
27
21
|
raise ArgumentError, 'should pass observer as a first argument or block'
|
28
22
|
elsif observer && block
|
@@ -42,8 +36,7 @@ module Concurrent
|
|
42
36
|
end
|
43
37
|
end
|
44
38
|
|
45
|
-
#
|
46
|
-
# @return [Object] the deleted observer
|
39
|
+
# @!macro observable_delete_observer
|
47
40
|
def delete_observer(observer)
|
48
41
|
synchronize do
|
49
42
|
new_observers = @observers.dup
|
@@ -53,14 +46,13 @@ module Concurrent
|
|
53
46
|
end
|
54
47
|
end
|
55
48
|
|
56
|
-
#
|
57
|
-
# @return [CopyOnWriteObserverSet] self
|
49
|
+
# @!macro observable_delete_observers
|
58
50
|
def delete_observers
|
59
51
|
self.observers = {}
|
60
52
|
self
|
61
53
|
end
|
62
54
|
|
63
|
-
#
|
55
|
+
# @!macro observable_count_observers
|
64
56
|
def count_observers
|
65
57
|
observers.count
|
66
58
|
end
|