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
data/lib/concurrent/actress.rb
DELETED
data/lib/concurrent/agent.rb
DELETED
@@ -1,209 +0,0 @@
|
|
1
|
-
require 'thread'
|
2
|
-
|
3
|
-
require 'concurrent/dereferenceable'
|
4
|
-
require 'concurrent/observable'
|
5
|
-
require 'concurrent/options_parser'
|
6
|
-
require 'concurrent/utility/timeout'
|
7
|
-
require 'concurrent/logging'
|
8
|
-
|
9
|
-
module Concurrent
|
10
|
-
|
11
|
-
# {include:file:doc/agent.md}
|
12
|
-
#
|
13
|
-
# @!attribute [r] timeout
|
14
|
-
# @return [Fixnum] the maximum number of seconds before an update is cancelled
|
15
|
-
class Agent
|
16
|
-
include Dereferenceable
|
17
|
-
include Concurrent::Observable
|
18
|
-
include Logging
|
19
|
-
|
20
|
-
attr_reader :timeout, :task_executor, :operation_executor
|
21
|
-
|
22
|
-
# Initialize a new Agent with the given initial value and provided options.
|
23
|
-
#
|
24
|
-
# @param [Object] initial the initial value
|
25
|
-
# @param [Hash] opts the options used to define the behavior at update and deref
|
26
|
-
#
|
27
|
-
# @option opts [Boolean] :operation (false) when `true` will execute the future on the global
|
28
|
-
# operation pool (for long-running operations), when `false` will execute the future on the
|
29
|
-
# global task pool (for short-running tasks)
|
30
|
-
# @option opts [object] :executor when provided will run all operations on
|
31
|
-
# this executor rather than the global thread pool (overrides :operation)
|
32
|
-
#
|
33
|
-
# @option opts [String] :dup_on_deref (false) call `#dup` before returning the data
|
34
|
-
# @option opts [String] :freeze_on_deref (false) call `#freeze` before returning the data
|
35
|
-
# @option opts [String] :copy_on_deref (nil) call the given `Proc` passing the internal value and
|
36
|
-
# returning the value returned from the proc
|
37
|
-
def initialize(initial, opts = {})
|
38
|
-
@value = initial
|
39
|
-
@rescuers = []
|
40
|
-
@validator = Proc.new { |result| true }
|
41
|
-
self.observers = CopyOnWriteObserverSet.new
|
42
|
-
@serialized_execution = SerializedExecution.new
|
43
|
-
@task_executor = OptionsParser.get_task_executor_from(opts)
|
44
|
-
@operation_executor = OptionsParser.get_operation_executor_from(opts)
|
45
|
-
init_mutex
|
46
|
-
set_deref_options(opts)
|
47
|
-
end
|
48
|
-
|
49
|
-
# Specifies a block operation to be performed when an update operation raises
|
50
|
-
# an exception. Rescue blocks will be checked in order they were added. The first
|
51
|
-
# block for which the raised exception "is-a" subclass of the given `clazz` will
|
52
|
-
# be called. If no `clazz` is given the block will match any caught exception.
|
53
|
-
# This behavior is intended to be identical to Ruby's `begin/rescue/end` behavior.
|
54
|
-
# Any number of rescue handlers can be added. If no rescue handlers are added then
|
55
|
-
# caught exceptions will be suppressed.
|
56
|
-
#
|
57
|
-
# @param [Exception] clazz the class of exception to catch
|
58
|
-
# @yield the block to be called when a matching exception is caught
|
59
|
-
# @yieldparam [StandardError] ex the caught exception
|
60
|
-
#
|
61
|
-
# @example
|
62
|
-
# score = Concurrent::Agent.new(0).
|
63
|
-
# rescue(NoMethodError){|ex| puts "Bam!" }.
|
64
|
-
# rescue(ArgumentError){|ex| puts "Pow!" }.
|
65
|
-
# rescue{|ex| puts "Boom!" }
|
66
|
-
#
|
67
|
-
# score << proc{|current| raise ArgumentError }
|
68
|
-
# sleep(0.1)
|
69
|
-
# #=> puts "Pow!"
|
70
|
-
def rescue(clazz = StandardError, &block)
|
71
|
-
unless block.nil?
|
72
|
-
mutex.synchronize do
|
73
|
-
@rescuers << Rescuer.new(clazz, block)
|
74
|
-
end
|
75
|
-
end
|
76
|
-
self
|
77
|
-
end
|
78
|
-
alias_method :catch, :rescue
|
79
|
-
alias_method :on_error, :rescue
|
80
|
-
|
81
|
-
# A block operation to be performed after every update to validate if the new
|
82
|
-
# value is valid. If the new value is not valid then the current value is not
|
83
|
-
# updated. If no validator is provided then all updates are considered valid.
|
84
|
-
#
|
85
|
-
# @yield the block to be called after every update operation to determine if
|
86
|
-
# the result is valid
|
87
|
-
# @yieldparam [Object] value the result of the last update operation
|
88
|
-
# @yieldreturn [Boolean] true if the value is valid else false
|
89
|
-
def validate(&block)
|
90
|
-
|
91
|
-
unless block.nil?
|
92
|
-
begin
|
93
|
-
mutex.lock
|
94
|
-
@validator = block
|
95
|
-
ensure
|
96
|
-
mutex.unlock
|
97
|
-
end
|
98
|
-
end
|
99
|
-
self
|
100
|
-
end
|
101
|
-
alias_method :validates, :validate
|
102
|
-
alias_method :validate_with, :validate
|
103
|
-
alias_method :validates_with, :validate
|
104
|
-
|
105
|
-
# Update the current value with the result of the given block operation,
|
106
|
-
# block should not do blocking calls, use #post_off for blocking calls
|
107
|
-
#
|
108
|
-
# @yield the operation to be performed with the current value in order to calculate
|
109
|
-
# the new value
|
110
|
-
# @yieldparam [Object] value the current value
|
111
|
-
# @yieldreturn [Object] the new value
|
112
|
-
# @return [true, nil] nil when no block is given
|
113
|
-
def post(&block)
|
114
|
-
post_on(@task_executor, &block)
|
115
|
-
end
|
116
|
-
|
117
|
-
# Update the current value with the result of the given block operation,
|
118
|
-
# block can do blocking calls
|
119
|
-
#
|
120
|
-
# @param [Fixnum, nil] timeout maximum number of seconds before an update is cancelled
|
121
|
-
#
|
122
|
-
# @yield the operation to be performed with the current value in order to calculate
|
123
|
-
# the new value
|
124
|
-
# @yieldparam [Object] value the current value
|
125
|
-
# @yieldreturn [Object] the new value
|
126
|
-
# @return [true, nil] nil when no block is given
|
127
|
-
def post_off(timeout = nil, &block)
|
128
|
-
block = if timeout
|
129
|
-
lambda { |value| Concurrent::timeout(timeout) { block.call(value) } }
|
130
|
-
else
|
131
|
-
block
|
132
|
-
end
|
133
|
-
post_on(@operation_executor, &block)
|
134
|
-
end
|
135
|
-
|
136
|
-
# Update the current value with the result of the given block operation,
|
137
|
-
# block should not do blocking calls, use #post_off for blocking calls
|
138
|
-
#
|
139
|
-
# @yield the operation to be performed with the current value in order to calculate
|
140
|
-
# the new value
|
141
|
-
# @yieldparam [Object] value the current value
|
142
|
-
# @yieldreturn [Object] the new value
|
143
|
-
def <<(block)
|
144
|
-
post(&block)
|
145
|
-
self
|
146
|
-
end
|
147
|
-
|
148
|
-
# Waits/blocks until all the updates sent before this call are done.
|
149
|
-
#
|
150
|
-
# @param [Numeric] timeout the maximum time in second to wait.
|
151
|
-
# @return [Boolean] false on timeout, true otherwise
|
152
|
-
def await(timeout = nil)
|
153
|
-
done = Event.new
|
154
|
-
post { |val| done.set; val }
|
155
|
-
done.wait timeout
|
156
|
-
end
|
157
|
-
|
158
|
-
private
|
159
|
-
|
160
|
-
def post_on(executor, &block)
|
161
|
-
return nil if block.nil?
|
162
|
-
@serialized_execution.post(executor) { work(&block) }
|
163
|
-
true
|
164
|
-
end
|
165
|
-
|
166
|
-
# @!visibility private
|
167
|
-
Rescuer = Struct.new(:clazz, :block) # :nodoc:
|
168
|
-
|
169
|
-
# @!visibility private
|
170
|
-
def try_rescue(ex) # :nodoc:
|
171
|
-
rescuer = mutex.synchronize do
|
172
|
-
@rescuers.find { |r| ex.is_a?(r.clazz) }
|
173
|
-
end
|
174
|
-
rescuer.block.call(ex) if rescuer
|
175
|
-
rescue Exception => ex
|
176
|
-
# suppress
|
177
|
-
log DEBUG, ex
|
178
|
-
end
|
179
|
-
|
180
|
-
# @!visibility private
|
181
|
-
def work(&handler) # :nodoc:
|
182
|
-
validator, value = mutex.synchronize { [@validator, @value] }
|
183
|
-
|
184
|
-
begin
|
185
|
-
result = handler.call(value)
|
186
|
-
valid = validator.call(result)
|
187
|
-
rescue Exception => ex
|
188
|
-
exception = ex
|
189
|
-
end
|
190
|
-
|
191
|
-
begin
|
192
|
-
mutex.lock
|
193
|
-
should_notify = if !exception && valid
|
194
|
-
@value = result
|
195
|
-
true
|
196
|
-
end
|
197
|
-
ensure
|
198
|
-
mutex.unlock
|
199
|
-
end
|
200
|
-
|
201
|
-
if should_notify
|
202
|
-
time = Time.now
|
203
|
-
observers.notify_observers { [time, self.value] }
|
204
|
-
end
|
205
|
-
|
206
|
-
try_rescue(exception)
|
207
|
-
end
|
208
|
-
end
|
209
|
-
end
|
data/lib/concurrent/atomic.rb
DELETED
@@ -1,92 +0,0 @@
|
|
1
|
-
#####################################################################
|
2
|
-
# Attempt to check for the deprecated ruby-atomic gem and warn the
|
3
|
-
# user that they should use the new implementation instead.
|
4
|
-
|
5
|
-
if defined?(Atomic)
|
6
|
-
warn <<-RUBY
|
7
|
-
[ATOMIC] Detected an `Atomic` class, which may indicate a dependency
|
8
|
-
on the ruby-atomic gem. That gem has been deprecated and merged into
|
9
|
-
the concurrent-ruby gem. Please use the Concurrent::Atomic class for
|
10
|
-
atomic references and not the Atomic class.
|
11
|
-
RUBY
|
12
|
-
end
|
13
|
-
#####################################################################
|
14
|
-
|
15
|
-
require_relative '../extension_helper'
|
16
|
-
require 'concurrent/atomic_reference/concurrent_update_error'
|
17
|
-
require 'concurrent/atomic_reference/mutex_atomic'
|
18
|
-
|
19
|
-
begin
|
20
|
-
# force fallback impl with FORCE_ATOMIC_FALLBACK=1
|
21
|
-
if /[^0fF]/ =~ ENV['FORCE_ATOMIC_FALLBACK']
|
22
|
-
ruby_engine = 'mutex_atomic'
|
23
|
-
else
|
24
|
-
ruby_engine = defined?(RUBY_ENGINE)? RUBY_ENGINE : 'ruby'
|
25
|
-
end
|
26
|
-
|
27
|
-
require "concurrent/atomic_reference/#{ruby_engine}"
|
28
|
-
rescue LoadError
|
29
|
-
#warn 'Compiled extensions not installed, pure Ruby Atomic will be used.'
|
30
|
-
end
|
31
|
-
|
32
|
-
if defined? Concurrent::JavaAtomic
|
33
|
-
|
34
|
-
# @!macro [attach] atomic_reference
|
35
|
-
#
|
36
|
-
# An object reference that may be updated atomically.
|
37
|
-
#
|
38
|
-
# Testing with ruby 2.1.2
|
39
|
-
#
|
40
|
-
# *** Sequential updates ***
|
41
|
-
# user system total real
|
42
|
-
# no lock 0.000000 0.000000 0.000000 ( 0.005502)
|
43
|
-
# mutex 0.030000 0.000000 0.030000 ( 0.025158)
|
44
|
-
# MutexAtomic 0.100000 0.000000 0.100000 ( 0.103096)
|
45
|
-
# CAtomic 0.040000 0.000000 0.040000 ( 0.034012)
|
46
|
-
#
|
47
|
-
# *** Parallel updates ***
|
48
|
-
# user system total real
|
49
|
-
# no lock 0.010000 0.000000 0.010000 ( 0.009387)
|
50
|
-
# mutex 0.030000 0.010000 0.040000 ( 0.032545)
|
51
|
-
# MutexAtomic 0.830000 2.280000 3.110000 ( 2.146622)
|
52
|
-
# CAtomic 0.040000 0.000000 0.040000 ( 0.038332)
|
53
|
-
#
|
54
|
-
# Testing with jruby 1.9.3
|
55
|
-
#
|
56
|
-
# *** Sequential updates ***
|
57
|
-
# user system total real
|
58
|
-
# no lock 0.170000 0.000000 0.170000 ( 0.051000)
|
59
|
-
# mutex 0.370000 0.010000 0.380000 ( 0.121000)
|
60
|
-
# MutexAtomic 1.530000 0.020000 1.550000 ( 0.471000)
|
61
|
-
# JavaAtomic 0.370000 0.010000 0.380000 ( 0.112000)
|
62
|
-
#
|
63
|
-
# *** Parallel updates ***
|
64
|
-
# user system total real
|
65
|
-
# no lock 0.390000 0.000000 0.390000 ( 0.105000)
|
66
|
-
# mutex 0.480000 0.040000 0.520000 ( 0.145000)
|
67
|
-
# MutexAtomic 1.600000 0.180000 1.780000 ( 0.511000)
|
68
|
-
# JavaAtomic 0.460000 0.010000 0.470000 ( 0.131000)
|
69
|
-
#
|
70
|
-
# @see http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/atomic/AtomicReference.html
|
71
|
-
# @see http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/atomic/package-summary.html
|
72
|
-
class Concurrent::Atomic < Concurrent::JavaAtomic
|
73
|
-
end
|
74
|
-
|
75
|
-
elsif defined? Concurrent::RbxAtomic
|
76
|
-
|
77
|
-
# @!macro atomic_reference
|
78
|
-
class Concurrent::Atomic < Concurrent::RbxAtomic
|
79
|
-
end
|
80
|
-
|
81
|
-
elsif defined? Concurrent::CAtomic
|
82
|
-
|
83
|
-
# @!macro atomic_reference
|
84
|
-
class Concurrent::Atomic < Concurrent::CAtomic
|
85
|
-
end
|
86
|
-
|
87
|
-
else
|
88
|
-
|
89
|
-
# @!macro atomic_reference
|
90
|
-
class Concurrent::Atomic < Concurrent::MutexAtomic
|
91
|
-
end
|
92
|
-
end
|
@@ -1,118 +0,0 @@
|
|
1
|
-
module Concurrent
|
2
|
-
|
3
|
-
# A thread safe observer set implemented using copy-on-read approach:
|
4
|
-
# observers are added and removed from a thread safe collection; every time
|
5
|
-
# a notification is required the internal data structure is copied to
|
6
|
-
# prevent concurrency issues
|
7
|
-
class CopyOnNotifyObserverSet
|
8
|
-
|
9
|
-
def initialize
|
10
|
-
@mutex = Mutex.new
|
11
|
-
@observers = {}
|
12
|
-
end
|
13
|
-
|
14
|
-
# Adds an observer to this set
|
15
|
-
# If a block is passed, the observer will be created by this method and no other params should be passed
|
16
|
-
# @param [Object] observer the observer to add
|
17
|
-
# @param [Symbol] func the function to call on the observer during notification. Default is :update
|
18
|
-
# @return [Object] the added observer
|
19
|
-
def add_observer(observer=nil, func=:update, &block)
|
20
|
-
if observer.nil? && block.nil?
|
21
|
-
raise ArgumentError, 'should pass observer as a first argument or block'
|
22
|
-
elsif observer && block
|
23
|
-
raise ArgumentError.new('cannot provide both an observer and a block')
|
24
|
-
end
|
25
|
-
|
26
|
-
if block
|
27
|
-
observer = block
|
28
|
-
func = :call
|
29
|
-
end
|
30
|
-
|
31
|
-
begin
|
32
|
-
@mutex.lock
|
33
|
-
@observers[observer] = func
|
34
|
-
ensure
|
35
|
-
@mutex.unlock
|
36
|
-
end
|
37
|
-
|
38
|
-
observer
|
39
|
-
end
|
40
|
-
|
41
|
-
# @param [Object] observer the observer to remove
|
42
|
-
# @return [Object] the deleted observer
|
43
|
-
def delete_observer(observer)
|
44
|
-
@mutex.lock
|
45
|
-
@observers.delete(observer)
|
46
|
-
@mutex.unlock
|
47
|
-
|
48
|
-
observer
|
49
|
-
end
|
50
|
-
|
51
|
-
# Deletes all observers
|
52
|
-
# @return [CopyOnWriteObserverSet] self
|
53
|
-
def delete_observers
|
54
|
-
@mutex.lock
|
55
|
-
@observers.clear
|
56
|
-
@mutex.unlock
|
57
|
-
|
58
|
-
self
|
59
|
-
end
|
60
|
-
|
61
|
-
# @return [Integer] the observers count
|
62
|
-
def count_observers
|
63
|
-
@mutex.lock
|
64
|
-
result = @observers.count
|
65
|
-
@mutex.unlock
|
66
|
-
|
67
|
-
result
|
68
|
-
end
|
69
|
-
|
70
|
-
# Notifies all registered observers with optional args
|
71
|
-
# @param [Object] args arguments to be passed to each observer
|
72
|
-
# @return [CopyOnWriteObserverSet] self
|
73
|
-
def notify_observers(*args, &block)
|
74
|
-
observers = duplicate_observers
|
75
|
-
notify_to(observers, *args, &block)
|
76
|
-
|
77
|
-
self
|
78
|
-
end
|
79
|
-
|
80
|
-
# Notifies all registered observers with optional args and deletes them.
|
81
|
-
#
|
82
|
-
# @param [Object] args arguments to be passed to each observer
|
83
|
-
# @return [CopyOnWriteObserverSet] self
|
84
|
-
def notify_and_delete_observers(*args, &block)
|
85
|
-
observers = duplicate_and_clear_observers
|
86
|
-
notify_to(observers, *args, &block)
|
87
|
-
|
88
|
-
self
|
89
|
-
end
|
90
|
-
|
91
|
-
private
|
92
|
-
|
93
|
-
def duplicate_and_clear_observers
|
94
|
-
@mutex.lock
|
95
|
-
observers = @observers.dup
|
96
|
-
@observers.clear
|
97
|
-
@mutex.unlock
|
98
|
-
|
99
|
-
observers
|
100
|
-
end
|
101
|
-
|
102
|
-
def duplicate_observers
|
103
|
-
@mutex.lock
|
104
|
-
observers = @observers.dup
|
105
|
-
@mutex.unlock
|
106
|
-
|
107
|
-
observers
|
108
|
-
end
|
109
|
-
|
110
|
-
def notify_to(observers, *args)
|
111
|
-
raise ArgumentError.new('cannot give arguments and a block') if block_given? && !args.empty?
|
112
|
-
observers.each do |observer, function|
|
113
|
-
args = yield if block_given?
|
114
|
-
observer.send(function, *args)
|
115
|
-
end
|
116
|
-
end
|
117
|
-
end
|
118
|
-
end
|
@@ -1,117 +0,0 @@
|
|
1
|
-
module Concurrent
|
2
|
-
|
3
|
-
# A thread safe observer set implemented using copy-on-write approach:
|
4
|
-
# every time an observer is added or removed the whole internal data structure is
|
5
|
-
# duplicated and replaced with a new one.
|
6
|
-
class CopyOnWriteObserverSet
|
7
|
-
|
8
|
-
def initialize
|
9
|
-
@mutex = Mutex.new
|
10
|
-
@observers = {}
|
11
|
-
end
|
12
|
-
|
13
|
-
# Adds an observer to this set
|
14
|
-
# If a block is passed, the observer will be created by this method and no other params should be passed
|
15
|
-
# @param [Object] observer the observer to add
|
16
|
-
# @param [Symbol] func the function to call on the observer during notification. Default is :update
|
17
|
-
# @return [Object] the added observer
|
18
|
-
def add_observer(observer=nil, func=:update, &block)
|
19
|
-
if observer.nil? && block.nil?
|
20
|
-
raise ArgumentError, 'should pass observer as a first argument or block'
|
21
|
-
elsif observer && block
|
22
|
-
raise ArgumentError.new('cannot provide both an observer and a block')
|
23
|
-
end
|
24
|
-
|
25
|
-
if block
|
26
|
-
observer = block
|
27
|
-
func = :call
|
28
|
-
end
|
29
|
-
|
30
|
-
begin
|
31
|
-
@mutex.lock
|
32
|
-
new_observers = @observers.dup
|
33
|
-
new_observers[observer] = func
|
34
|
-
@observers = new_observers
|
35
|
-
observer
|
36
|
-
ensure
|
37
|
-
@mutex.unlock
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
# @param [Object] observer the observer to remove
|
42
|
-
# @return [Object] the deleted observer
|
43
|
-
def delete_observer(observer)
|
44
|
-
@mutex.lock
|
45
|
-
new_observers = @observers.dup
|
46
|
-
new_observers.delete(observer)
|
47
|
-
@observers = new_observers
|
48
|
-
observer
|
49
|
-
ensure
|
50
|
-
@mutex.unlock
|
51
|
-
end
|
52
|
-
|
53
|
-
# Deletes all observers
|
54
|
-
# @return [CopyOnWriteObserverSet] self
|
55
|
-
def delete_observers
|
56
|
-
self.observers = {}
|
57
|
-
self
|
58
|
-
end
|
59
|
-
|
60
|
-
|
61
|
-
# @return [Integer] the observers count
|
62
|
-
def count_observers
|
63
|
-
observers.count
|
64
|
-
end
|
65
|
-
|
66
|
-
# Notifies all registered observers with optional args
|
67
|
-
# @param [Object] args arguments to be passed to each observer
|
68
|
-
# @return [CopyOnWriteObserverSet] self
|
69
|
-
def notify_observers(*args, &block)
|
70
|
-
notify_to(observers, *args, &block)
|
71
|
-
self
|
72
|
-
end
|
73
|
-
|
74
|
-
# Notifies all registered observers with optional args and deletes them.
|
75
|
-
#
|
76
|
-
# @param [Object] args arguments to be passed to each observer
|
77
|
-
# @return [CopyOnWriteObserverSet] self
|
78
|
-
def notify_and_delete_observers(*args, &block)
|
79
|
-
old = clear_observers_and_return_old
|
80
|
-
notify_to(old, *args, &block)
|
81
|
-
self
|
82
|
-
end
|
83
|
-
|
84
|
-
private
|
85
|
-
|
86
|
-
def notify_to(observers, *args)
|
87
|
-
raise ArgumentError.new('cannot give arguments and a block') if block_given? && !args.empty?
|
88
|
-
observers.each do |observer, function|
|
89
|
-
args = yield if block_given?
|
90
|
-
observer.send(function, *args)
|
91
|
-
end
|
92
|
-
end
|
93
|
-
|
94
|
-
def observers
|
95
|
-
@mutex.lock
|
96
|
-
@observers
|
97
|
-
ensure
|
98
|
-
@mutex.unlock
|
99
|
-
end
|
100
|
-
|
101
|
-
def observers=(new_set)
|
102
|
-
@mutex.lock
|
103
|
-
@observers = new_set
|
104
|
-
ensure
|
105
|
-
@mutex.unlock
|
106
|
-
end
|
107
|
-
|
108
|
-
def clear_observers_and_return_old
|
109
|
-
@mutex.lock
|
110
|
-
old_observers = @observers
|
111
|
-
@observers = {}
|
112
|
-
old_observers
|
113
|
-
ensure
|
114
|
-
@mutex.unlock
|
115
|
-
end
|
116
|
-
end
|
117
|
-
end
|