concurrent-ruby 0.8.0 → 0.9.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 +4 -4
- data/CHANGELOG.md +97 -2
- data/README.md +103 -54
- data/lib/concurrent.rb +34 -14
- data/lib/concurrent/async.rb +164 -50
- data/lib/concurrent/atom.rb +171 -0
- data/lib/concurrent/atomic/atomic_boolean.rb +57 -107
- data/lib/concurrent/atomic/atomic_fixnum.rb +73 -101
- data/lib/concurrent/atomic/atomic_reference.rb +49 -0
- data/lib/concurrent/atomic/condition.rb +23 -12
- data/lib/concurrent/atomic/count_down_latch.rb +23 -21
- data/lib/concurrent/atomic/cyclic_barrier.rb +47 -47
- data/lib/concurrent/atomic/event.rb +33 -42
- data/lib/concurrent/atomic/read_write_lock.rb +252 -0
- data/lib/concurrent/atomic/semaphore.rb +64 -89
- data/lib/concurrent/atomic/thread_local_var.rb +130 -58
- data/lib/concurrent/atomic/thread_local_var/weak_key_map.rb +236 -0
- data/lib/concurrent/atomic_reference/direct_update.rb +3 -0
- data/lib/concurrent/atomic_reference/jruby.rb +6 -3
- data/lib/concurrent/atomic_reference/mutex_atomic.rb +10 -32
- data/lib/concurrent/atomic_reference/numeric_cas_wrapper.rb +3 -0
- data/lib/concurrent/atomic_reference/rbx.rb +4 -1
- data/lib/concurrent/atomic_reference/ruby.rb +6 -3
- data/lib/concurrent/atomics.rb +74 -4
- data/lib/concurrent/collection/copy_on_notify_observer_set.rb +115 -0
- data/lib/concurrent/collection/copy_on_write_observer_set.rb +119 -0
- data/lib/concurrent/collection/priority_queue.rb +300 -245
- data/lib/concurrent/concern/deprecation.rb +27 -0
- data/lib/concurrent/concern/dereferenceable.rb +88 -0
- data/lib/concurrent/concern/logging.rb +25 -0
- data/lib/concurrent/concern/obligation.rb +228 -0
- data/lib/concurrent/concern/observable.rb +85 -0
- data/lib/concurrent/configuration.rb +226 -112
- data/lib/concurrent/dataflow.rb +2 -3
- data/lib/concurrent/delay.rb +141 -50
- data/lib/concurrent/edge.rb +30 -0
- data/lib/concurrent/errors.rb +10 -0
- data/lib/concurrent/exchanger.rb +25 -1
- data/lib/concurrent/executor/cached_thread_pool.rb +46 -33
- data/lib/concurrent/executor/executor.rb +46 -299
- data/lib/concurrent/executor/executor_service.rb +521 -0
- data/lib/concurrent/executor/fixed_thread_pool.rb +206 -26
- data/lib/concurrent/executor/immediate_executor.rb +9 -9
- data/lib/concurrent/executor/indirect_immediate_executor.rb +4 -3
- data/lib/concurrent/executor/java_cached_thread_pool.rb +18 -16
- data/lib/concurrent/executor/java_fixed_thread_pool.rb +11 -18
- data/lib/concurrent/executor/java_single_thread_executor.rb +17 -16
- data/lib/concurrent/executor/java_thread_pool_executor.rb +55 -102
- data/lib/concurrent/executor/ruby_cached_thread_pool.rb +9 -18
- data/lib/concurrent/executor/ruby_fixed_thread_pool.rb +10 -21
- data/lib/concurrent/executor/ruby_single_thread_executor.rb +14 -16
- data/lib/concurrent/executor/ruby_thread_pool_executor.rb +250 -166
- data/lib/concurrent/executor/safe_task_executor.rb +5 -4
- data/lib/concurrent/executor/serialized_execution.rb +22 -18
- data/lib/concurrent/executor/{per_thread_executor.rb → simple_executor_service.rb} +29 -20
- data/lib/concurrent/executor/single_thread_executor.rb +32 -21
- data/lib/concurrent/executor/thread_pool_executor.rb +72 -60
- data/lib/concurrent/executor/timer_set.rb +96 -84
- data/lib/concurrent/executors.rb +1 -1
- data/lib/concurrent/future.rb +70 -38
- data/lib/concurrent/immutable_struct.rb +89 -0
- data/lib/concurrent/ivar.rb +152 -60
- data/lib/concurrent/lazy_register.rb +40 -20
- data/lib/concurrent/maybe.rb +226 -0
- data/lib/concurrent/mutable_struct.rb +227 -0
- data/lib/concurrent/mvar.rb +44 -43
- data/lib/concurrent/promise.rb +208 -134
- data/lib/concurrent/scheduled_task.rb +339 -43
- data/lib/concurrent/settable_struct.rb +127 -0
- data/lib/concurrent/synchronization.rb +17 -0
- data/lib/concurrent/synchronization/abstract_object.rb +163 -0
- data/lib/concurrent/synchronization/abstract_struct.rb +158 -0
- data/lib/concurrent/synchronization/condition.rb +53 -0
- data/lib/concurrent/synchronization/java_object.rb +35 -0
- data/lib/concurrent/synchronization/lock.rb +32 -0
- data/lib/concurrent/synchronization/monitor_object.rb +24 -0
- data/lib/concurrent/synchronization/mutex_object.rb +43 -0
- data/lib/concurrent/synchronization/object.rb +78 -0
- data/lib/concurrent/synchronization/rbx_object.rb +75 -0
- data/lib/concurrent/timer_task.rb +87 -100
- data/lib/concurrent/tvar.rb +42 -38
- data/lib/concurrent/utilities.rb +3 -1
- data/lib/concurrent/utility/at_exit.rb +97 -0
- data/lib/concurrent/utility/engine.rb +40 -0
- data/lib/concurrent/utility/monotonic_time.rb +59 -0
- data/lib/concurrent/utility/native_extension_loader.rb +56 -0
- data/lib/concurrent/utility/processor_counter.rb +156 -0
- data/lib/concurrent/utility/timeout.rb +18 -14
- data/lib/concurrent/utility/timer.rb +11 -6
- data/lib/concurrent/version.rb +2 -1
- data/lib/concurrent_ruby.rb +1 -0
- metadata +47 -83
- data/lib/concurrent/actor.rb +0 -103
- data/lib/concurrent/actor/behaviour.rb +0 -70
- data/lib/concurrent/actor/behaviour/abstract.rb +0 -48
- data/lib/concurrent/actor/behaviour/awaits.rb +0 -21
- data/lib/concurrent/actor/behaviour/buffer.rb +0 -54
- data/lib/concurrent/actor/behaviour/errors_on_unknown_message.rb +0 -12
- data/lib/concurrent/actor/behaviour/executes_context.rb +0 -18
- data/lib/concurrent/actor/behaviour/linking.rb +0 -45
- data/lib/concurrent/actor/behaviour/pausing.rb +0 -77
- data/lib/concurrent/actor/behaviour/removes_child.rb +0 -16
- data/lib/concurrent/actor/behaviour/sets_results.rb +0 -36
- data/lib/concurrent/actor/behaviour/supervised.rb +0 -59
- data/lib/concurrent/actor/behaviour/supervising.rb +0 -34
- data/lib/concurrent/actor/behaviour/terminates_children.rb +0 -13
- data/lib/concurrent/actor/behaviour/termination.rb +0 -54
- data/lib/concurrent/actor/context.rb +0 -154
- data/lib/concurrent/actor/core.rb +0 -217
- data/lib/concurrent/actor/default_dead_letter_handler.rb +0 -9
- data/lib/concurrent/actor/envelope.rb +0 -41
- data/lib/concurrent/actor/errors.rb +0 -27
- data/lib/concurrent/actor/internal_delegations.rb +0 -49
- data/lib/concurrent/actor/public_delegations.rb +0 -40
- data/lib/concurrent/actor/reference.rb +0 -81
- data/lib/concurrent/actor/root.rb +0 -37
- data/lib/concurrent/actor/type_check.rb +0 -48
- data/lib/concurrent/actor/utils.rb +0 -10
- data/lib/concurrent/actor/utils/ad_hoc.rb +0 -21
- data/lib/concurrent/actor/utils/balancer.rb +0 -42
- data/lib/concurrent/actor/utils/broadcast.rb +0 -52
- data/lib/concurrent/actor/utils/pool.rb +0 -59
- data/lib/concurrent/actress.rb +0 -3
- data/lib/concurrent/agent.rb +0 -209
- data/lib/concurrent/atomic.rb +0 -92
- data/lib/concurrent/atomic/copy_on_notify_observer_set.rb +0 -118
- data/lib/concurrent/atomic/copy_on_write_observer_set.rb +0 -117
- data/lib/concurrent/atomic/synchronization.rb +0 -51
- data/lib/concurrent/channel/buffered_channel.rb +0 -85
- data/lib/concurrent/channel/channel.rb +0 -41
- data/lib/concurrent/channel/unbuffered_channel.rb +0 -35
- data/lib/concurrent/channel/waitable_list.rb +0 -40
- data/lib/concurrent/channels.rb +0 -5
- data/lib/concurrent/collection/blocking_ring_buffer.rb +0 -71
- data/lib/concurrent/collection/ring_buffer.rb +0 -59
- data/lib/concurrent/collections.rb +0 -3
- data/lib/concurrent/dereferenceable.rb +0 -108
- data/lib/concurrent/executor/ruby_thread_pool_worker.rb +0 -73
- data/lib/concurrent/logging.rb +0 -20
- data/lib/concurrent/obligation.rb +0 -171
- data/lib/concurrent/observable.rb +0 -73
- data/lib/concurrent/options_parser.rb +0 -52
- data/lib/concurrent/utility/processor_count.rb +0 -152
- data/lib/extension_helper.rb +0 -37
@@ -3,6 +3,9 @@ require 'concurrent/atomic_reference/concurrent_update_error'
|
|
3
3
|
module Concurrent
|
4
4
|
|
5
5
|
# Define update methods that use direct paths
|
6
|
+
#
|
7
|
+
# @!visibility private
|
8
|
+
# @!macro internal_implementation_note
|
6
9
|
module AtomicDirectUpdate
|
7
10
|
|
8
11
|
# @!macro [attach] atomic_reference_method_update
|
@@ -1,12 +1,15 @@
|
|
1
|
-
|
1
|
+
require 'concurrent/utility/native_extension_loader'
|
2
2
|
|
3
|
-
if defined?(Concurrent::
|
3
|
+
if defined?(Concurrent::JavaAtomicReference)
|
4
4
|
require 'concurrent/atomic_reference/direct_update'
|
5
5
|
|
6
6
|
module Concurrent
|
7
7
|
|
8
8
|
# @!macro atomic_reference
|
9
|
-
|
9
|
+
#
|
10
|
+
# @!visibility private
|
11
|
+
# @!macro internal_implementation_note
|
12
|
+
class JavaAtomicReference
|
10
13
|
include Concurrent::AtomicDirectUpdate
|
11
14
|
end
|
12
15
|
end
|
@@ -5,45 +5,32 @@ require 'concurrent/atomic_reference/numeric_cas_wrapper'
|
|
5
5
|
module Concurrent
|
6
6
|
|
7
7
|
# @!macro atomic_reference
|
8
|
-
|
8
|
+
#
|
9
|
+
# @!visibility private
|
10
|
+
# @!macro internal_implementation_note
|
11
|
+
class MutexAtomicReference
|
9
12
|
include Concurrent::AtomicDirectUpdate
|
10
13
|
include Concurrent::AtomicNumericCompareAndSetWrapper
|
11
14
|
|
12
|
-
# @!macro
|
15
|
+
# @!macro atomic_reference_method_initialize
|
13
16
|
def initialize(value = nil)
|
14
17
|
@mutex = Mutex.new
|
15
18
|
@value = value
|
16
19
|
end
|
17
20
|
|
18
|
-
# @!macro
|
19
|
-
#
|
20
|
-
# Gets the current value.
|
21
|
-
#
|
22
|
-
# @return [Object] the current value
|
21
|
+
# @!macro atomic_reference_method_get
|
23
22
|
def get
|
24
23
|
@mutex.synchronize { @value }
|
25
24
|
end
|
26
25
|
alias_method :value, :get
|
27
26
|
|
28
|
-
# @!macro
|
29
|
-
#
|
30
|
-
# Sets to the given value.
|
31
|
-
#
|
32
|
-
# @param [Object] new_value the new value
|
33
|
-
#
|
34
|
-
# @return [Object] the new value
|
27
|
+
# @!macro atomic_reference_method_set
|
35
28
|
def set(new_value)
|
36
29
|
@mutex.synchronize { @value = new_value }
|
37
30
|
end
|
38
31
|
alias_method :value=, :set
|
39
32
|
|
40
|
-
# @!macro
|
41
|
-
#
|
42
|
-
# Atomically sets to the given value and returns the old value.
|
43
|
-
#
|
44
|
-
# @param [Object] new_value the new value
|
45
|
-
#
|
46
|
-
# @return [Object] the old value
|
33
|
+
# @!macro atomic_reference_method_get_and_set
|
47
34
|
def get_and_set(new_value)
|
48
35
|
@mutex.synchronize do
|
49
36
|
old_value = @value
|
@@ -53,17 +40,8 @@ module Concurrent
|
|
53
40
|
end
|
54
41
|
alias_method :swap, :get_and_set
|
55
42
|
|
56
|
-
# @!macro
|
57
|
-
|
58
|
-
# Atomically sets the value to the given updated value if
|
59
|
-
# the current value == the expected value.
|
60
|
-
#
|
61
|
-
# @param [Object] old_value the expected value
|
62
|
-
# @param [Object] new_value the new value
|
63
|
-
#
|
64
|
-
# @return [Boolean] `true` if successful. A `false` return indicates
|
65
|
-
# that the actual value was not equal to the expected value.
|
66
|
-
def _compare_and_set(old_value, new_value) #:nodoc:
|
43
|
+
# @!macro atomic_reference_method_compare_and_set
|
44
|
+
def _compare_and_set(old_value, new_value)
|
67
45
|
return false unless @mutex.try_lock
|
68
46
|
begin
|
69
47
|
return false unless @value.equal? old_value
|
@@ -7,7 +7,10 @@ module Concurrent
|
|
7
7
|
#
|
8
8
|
# @note Extends `Rubinius::AtomicReference` version adding aliases
|
9
9
|
# and numeric logic.
|
10
|
-
|
10
|
+
#
|
11
|
+
# @!visibility private
|
12
|
+
# @!macro internal_implementation_note
|
13
|
+
class RbxAtomicReference < Rubinius::AtomicReference
|
11
14
|
alias _compare_and_set compare_and_set
|
12
15
|
include Concurrent::AtomicDirectUpdate
|
13
16
|
include Concurrent::AtomicNumericCompareAndSetWrapper
|
@@ -1,12 +1,15 @@
|
|
1
|
-
if defined? Concurrent::
|
2
|
-
|
1
|
+
if defined? Concurrent::CAtomicReference
|
2
|
+
require 'concurrent/utility/native_extension_loader'
|
3
3
|
require 'concurrent/atomic_reference/direct_update'
|
4
4
|
require 'concurrent/atomic_reference/numeric_cas_wrapper'
|
5
5
|
|
6
6
|
module Concurrent
|
7
7
|
|
8
8
|
# @!macro atomic_reference
|
9
|
-
|
9
|
+
#
|
10
|
+
# @!visibility private
|
11
|
+
# @!macro internal_implementation_note
|
12
|
+
class CAtomicReference
|
10
13
|
include Concurrent::AtomicDirectUpdate
|
11
14
|
include Concurrent::AtomicNumericCompareAndSetWrapper
|
12
15
|
|
data/lib/concurrent/atomics.rb
CHANGED
@@ -1,12 +1,82 @@
|
|
1
|
-
|
1
|
+
# @!macro [new] atomic_reference
|
2
|
+
#
|
3
|
+
# An object reference that may be updated atomically.
|
4
|
+
#
|
5
|
+
# Testing with ruby 2.1.2
|
6
|
+
#
|
7
|
+
# *** Sequential updates ***
|
8
|
+
# user system total real
|
9
|
+
# no lock 0.000000 0.000000 0.000000 ( 0.005502)
|
10
|
+
# mutex 0.030000 0.000000 0.030000 ( 0.025158)
|
11
|
+
# MutexAtomicReference 0.100000 0.000000 0.100000 ( 0.103096)
|
12
|
+
# CAtomicReference 0.040000 0.000000 0.040000 ( 0.034012)
|
13
|
+
#
|
14
|
+
# *** Parallel updates ***
|
15
|
+
# user system total real
|
16
|
+
# no lock 0.010000 0.000000 0.010000 ( 0.009387)
|
17
|
+
# mutex 0.030000 0.010000 0.040000 ( 0.032545)
|
18
|
+
# MutexAtomicReference 0.830000 2.280000 3.110000 ( 2.146622)
|
19
|
+
# CAtomicReference 0.040000 0.000000 0.040000 ( 0.038332)
|
20
|
+
#
|
21
|
+
# Testing with jruby 1.9.3
|
22
|
+
#
|
23
|
+
# *** Sequential updates ***
|
24
|
+
# user system total real
|
25
|
+
# no lock 0.170000 0.000000 0.170000 ( 0.051000)
|
26
|
+
# mutex 0.370000 0.010000 0.380000 ( 0.121000)
|
27
|
+
# MutexAtomicReference 1.530000 0.020000 1.550000 ( 0.471000)
|
28
|
+
# JavaAtomicReference 0.370000 0.010000 0.380000 ( 0.112000)
|
29
|
+
#
|
30
|
+
# *** Parallel updates ***
|
31
|
+
# user system total real
|
32
|
+
# no lock 0.390000 0.000000 0.390000 ( 0.105000)
|
33
|
+
# mutex 0.480000 0.040000 0.520000 ( 0.145000)
|
34
|
+
# MutexAtomicReference 1.600000 0.180000 1.780000 ( 0.511000)
|
35
|
+
# JavaAtomicReference 0.460000 0.010000 0.470000 ( 0.131000)
|
36
|
+
#
|
37
|
+
# @see http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/atomic/AtomicReference.html
|
38
|
+
# @see http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/atomic/package-summary.html
|
39
|
+
#
|
40
|
+
# @!method initialize
|
41
|
+
# @!macro [new] atomic_reference_method_initialize
|
42
|
+
# @param [Object] value The initial value.
|
43
|
+
#
|
44
|
+
# @!method get
|
45
|
+
# @!macro [new] atomic_reference_method_get
|
46
|
+
# Gets the current value.
|
47
|
+
# @return [Object] the current value
|
48
|
+
#
|
49
|
+
# @!method set
|
50
|
+
# @!macro [new] atomic_reference_method_set
|
51
|
+
# Sets to the given value.
|
52
|
+
# @param [Object] new_value the new value
|
53
|
+
# @return [Object] the new value
|
54
|
+
#
|
55
|
+
# @!method get_and_set
|
56
|
+
# @!macro [new] atomic_reference_method_get_and_set
|
57
|
+
# Atomically sets to the given value and returns the old value.
|
58
|
+
# @param [Object] new_value the new value
|
59
|
+
# @return [Object] the old value
|
60
|
+
#
|
61
|
+
# @!method compare_and_set
|
62
|
+
# @!macro [new] atomic_reference_method_compare_and_set
|
63
|
+
#
|
64
|
+
# Atomically sets the value to the given updated value if
|
65
|
+
# the current value == the expected value.
|
66
|
+
#
|
67
|
+
# @param [Object] old_value the expected value
|
68
|
+
# @param [Object] new_value the new value
|
69
|
+
#
|
70
|
+
# @return [Boolean] `true` if successful. A `false` return indicates
|
71
|
+
# that the actual value was not equal to the expected value.
|
72
|
+
|
73
|
+
require 'concurrent/atomic/atomic_reference'
|
2
74
|
require 'concurrent/atomic/atomic_boolean'
|
3
75
|
require 'concurrent/atomic/atomic_fixnum'
|
4
76
|
require 'concurrent/atomic/condition'
|
5
|
-
require 'concurrent/atomic/copy_on_notify_observer_set'
|
6
|
-
require 'concurrent/atomic/copy_on_write_observer_set'
|
7
77
|
require 'concurrent/atomic/cyclic_barrier'
|
8
78
|
require 'concurrent/atomic/count_down_latch'
|
9
79
|
require 'concurrent/atomic/event'
|
10
|
-
require 'concurrent/atomic/
|
80
|
+
require 'concurrent/atomic/read_write_lock'
|
11
81
|
require 'concurrent/atomic/semaphore'
|
12
82
|
require 'concurrent/atomic/thread_local_var'
|
@@ -0,0 +1,115 @@
|
|
1
|
+
require 'concurrent/synchronization'
|
2
|
+
|
3
|
+
module Concurrent
|
4
|
+
module Collection
|
5
|
+
|
6
|
+
# A thread safe observer set implemented using copy-on-read approach:
|
7
|
+
# observers are added and removed from a thread safe collection; every time
|
8
|
+
# a notification is required the internal data structure is copied to
|
9
|
+
# prevent concurrency issues
|
10
|
+
#
|
11
|
+
# @api private
|
12
|
+
class CopyOnNotifyObserverSet < Synchronization::Object
|
13
|
+
|
14
|
+
def initialize
|
15
|
+
super()
|
16
|
+
synchronize { ns_initialize }
|
17
|
+
end
|
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)
|
27
|
+
if observer.nil? && block.nil?
|
28
|
+
raise ArgumentError, 'should pass observer as a first argument or block'
|
29
|
+
elsif observer && block
|
30
|
+
raise ArgumentError.new('cannot provide both an observer and a block')
|
31
|
+
end
|
32
|
+
|
33
|
+
if block
|
34
|
+
observer = block
|
35
|
+
func = :call
|
36
|
+
end
|
37
|
+
|
38
|
+
synchronize do
|
39
|
+
@observers[observer] = func
|
40
|
+
observer
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# @param [Object] observer the observer to remove
|
45
|
+
# @return [Object] the deleted observer
|
46
|
+
def delete_observer(observer)
|
47
|
+
synchronize do
|
48
|
+
@observers.delete(observer)
|
49
|
+
observer
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# Deletes all observers
|
54
|
+
# @return [CopyOnWriteObserverSet] self
|
55
|
+
def delete_observers
|
56
|
+
synchronize do
|
57
|
+
@observers.clear
|
58
|
+
self
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# @return [Integer] the observers count
|
63
|
+
def count_observers
|
64
|
+
synchronize { @observers.count }
|
65
|
+
end
|
66
|
+
|
67
|
+
# Notifies all registered observers with optional args
|
68
|
+
# @param [Object] args arguments to be passed to each observer
|
69
|
+
# @return [CopyOnWriteObserverSet] self
|
70
|
+
def notify_observers(*args, &block)
|
71
|
+
observers = duplicate_observers
|
72
|
+
notify_to(observers, *args, &block)
|
73
|
+
self
|
74
|
+
end
|
75
|
+
|
76
|
+
# Notifies all registered observers with optional args and deletes them.
|
77
|
+
#
|
78
|
+
# @param [Object] args arguments to be passed to each observer
|
79
|
+
# @return [CopyOnWriteObserverSet] self
|
80
|
+
def notify_and_delete_observers(*args, &block)
|
81
|
+
observers = duplicate_and_clear_observers
|
82
|
+
notify_to(observers, *args, &block)
|
83
|
+
self
|
84
|
+
end
|
85
|
+
|
86
|
+
protected
|
87
|
+
|
88
|
+
def ns_initialize
|
89
|
+
@observers = {}
|
90
|
+
end
|
91
|
+
|
92
|
+
private
|
93
|
+
|
94
|
+
def duplicate_and_clear_observers
|
95
|
+
synchronize do
|
96
|
+
observers = @observers.dup
|
97
|
+
@observers.clear
|
98
|
+
observers
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def duplicate_observers
|
103
|
+
synchronize { observers = @observers.dup }
|
104
|
+
end
|
105
|
+
|
106
|
+
def notify_to(observers, *args)
|
107
|
+
raise ArgumentError.new('cannot give arguments and a block') if block_given? && !args.empty?
|
108
|
+
observers.each do |observer, function|
|
109
|
+
args = yield if block_given?
|
110
|
+
observer.send(function, *args)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
@@ -0,0 +1,119 @@
|
|
1
|
+
require 'concurrent/synchronization'
|
2
|
+
|
3
|
+
module Concurrent
|
4
|
+
module Collection
|
5
|
+
|
6
|
+
# A thread safe observer set implemented using copy-on-write approach:
|
7
|
+
# every time an observer is added or removed the whole internal data structure is
|
8
|
+
# duplicated and replaced with a new one.
|
9
|
+
#
|
10
|
+
# @api private
|
11
|
+
class CopyOnWriteObserverSet < Synchronization::Object
|
12
|
+
|
13
|
+
def initialize
|
14
|
+
super()
|
15
|
+
synchronize { ns_initialize }
|
16
|
+
end
|
17
|
+
|
18
|
+
# Adds an observer to this set
|
19
|
+
# If a block is passed, the observer will be created by this method and no
|
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)
|
26
|
+
if observer.nil? && block.nil?
|
27
|
+
raise ArgumentError, 'should pass observer as a first argument or block'
|
28
|
+
elsif observer && block
|
29
|
+
raise ArgumentError.new('cannot provide both an observer and a block')
|
30
|
+
end
|
31
|
+
|
32
|
+
if block
|
33
|
+
observer = block
|
34
|
+
func = :call
|
35
|
+
end
|
36
|
+
|
37
|
+
synchronize do
|
38
|
+
new_observers = @observers.dup
|
39
|
+
new_observers[observer] = func
|
40
|
+
@observers = new_observers
|
41
|
+
observer
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# @param [Object] observer the observer to remove
|
46
|
+
# @return [Object] the deleted observer
|
47
|
+
def delete_observer(observer)
|
48
|
+
synchronize do
|
49
|
+
new_observers = @observers.dup
|
50
|
+
new_observers.delete(observer)
|
51
|
+
@observers = new_observers
|
52
|
+
observer
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# Deletes all observers
|
57
|
+
# @return [CopyOnWriteObserverSet] self
|
58
|
+
def delete_observers
|
59
|
+
self.observers = {}
|
60
|
+
self
|
61
|
+
end
|
62
|
+
|
63
|
+
# @return [Integer] the observers count
|
64
|
+
def count_observers
|
65
|
+
observers.count
|
66
|
+
end
|
67
|
+
|
68
|
+
# Notifies all registered observers with optional args
|
69
|
+
# @param [Object] args arguments to be passed to each observer
|
70
|
+
# @return [CopyOnWriteObserverSet] self
|
71
|
+
def notify_observers(*args, &block)
|
72
|
+
notify_to(observers, *args, &block)
|
73
|
+
self
|
74
|
+
end
|
75
|
+
|
76
|
+
# Notifies all registered observers with optional args and deletes them.
|
77
|
+
#
|
78
|
+
# @param [Object] args arguments to be passed to each observer
|
79
|
+
# @return [CopyOnWriteObserverSet] self
|
80
|
+
def notify_and_delete_observers(*args, &block)
|
81
|
+
old = clear_observers_and_return_old
|
82
|
+
notify_to(old, *args, &block)
|
83
|
+
self
|
84
|
+
end
|
85
|
+
|
86
|
+
protected
|
87
|
+
|
88
|
+
def ns_initialize
|
89
|
+
@observers = {}
|
90
|
+
end
|
91
|
+
|
92
|
+
private
|
93
|
+
|
94
|
+
def notify_to(observers, *args)
|
95
|
+
raise ArgumentError.new('cannot give arguments and a block') if block_given? && !args.empty?
|
96
|
+
observers.each do |observer, function|
|
97
|
+
args = yield if block_given?
|
98
|
+
observer.send(function, *args)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def observers
|
103
|
+
synchronize { @observers }
|
104
|
+
end
|
105
|
+
|
106
|
+
def observers=(new_set)
|
107
|
+
synchronize { @observers = new_set }
|
108
|
+
end
|
109
|
+
|
110
|
+
def clear_observers_and_return_old
|
111
|
+
synchronize do
|
112
|
+
old_observers = @observers
|
113
|
+
@observers = {}
|
114
|
+
old_observers
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|