concurrent-ruby 0.8.0 → 0.9.0.pre2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (144) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +97 -2
  3. data/README.md +103 -54
  4. data/lib/concurrent.rb +34 -14
  5. data/lib/concurrent/async.rb +164 -50
  6. data/lib/concurrent/atom.rb +171 -0
  7. data/lib/concurrent/atomic/atomic_boolean.rb +57 -107
  8. data/lib/concurrent/atomic/atomic_fixnum.rb +73 -101
  9. data/lib/concurrent/atomic/atomic_reference.rb +49 -0
  10. data/lib/concurrent/atomic/condition.rb +23 -12
  11. data/lib/concurrent/atomic/count_down_latch.rb +23 -21
  12. data/lib/concurrent/atomic/cyclic_barrier.rb +47 -47
  13. data/lib/concurrent/atomic/event.rb +33 -42
  14. data/lib/concurrent/atomic/read_write_lock.rb +252 -0
  15. data/lib/concurrent/atomic/semaphore.rb +64 -89
  16. data/lib/concurrent/atomic/thread_local_var.rb +130 -58
  17. data/lib/concurrent/atomic/thread_local_var/weak_key_map.rb +236 -0
  18. data/lib/concurrent/atomic_reference/direct_update.rb +3 -0
  19. data/lib/concurrent/atomic_reference/jruby.rb +6 -3
  20. data/lib/concurrent/atomic_reference/mutex_atomic.rb +10 -32
  21. data/lib/concurrent/atomic_reference/numeric_cas_wrapper.rb +3 -0
  22. data/lib/concurrent/atomic_reference/rbx.rb +4 -1
  23. data/lib/concurrent/atomic_reference/ruby.rb +6 -3
  24. data/lib/concurrent/atomics.rb +74 -4
  25. data/lib/concurrent/collection/copy_on_notify_observer_set.rb +115 -0
  26. data/lib/concurrent/collection/copy_on_write_observer_set.rb +119 -0
  27. data/lib/concurrent/collection/priority_queue.rb +300 -245
  28. data/lib/concurrent/concern/deprecation.rb +27 -0
  29. data/lib/concurrent/concern/dereferenceable.rb +88 -0
  30. data/lib/concurrent/concern/logging.rb +25 -0
  31. data/lib/concurrent/concern/obligation.rb +228 -0
  32. data/lib/concurrent/concern/observable.rb +85 -0
  33. data/lib/concurrent/configuration.rb +226 -112
  34. data/lib/concurrent/dataflow.rb +2 -3
  35. data/lib/concurrent/delay.rb +141 -50
  36. data/lib/concurrent/edge.rb +30 -0
  37. data/lib/concurrent/errors.rb +10 -0
  38. data/lib/concurrent/exchanger.rb +25 -1
  39. data/lib/concurrent/executor/cached_thread_pool.rb +46 -33
  40. data/lib/concurrent/executor/executor.rb +46 -299
  41. data/lib/concurrent/executor/executor_service.rb +521 -0
  42. data/lib/concurrent/executor/fixed_thread_pool.rb +206 -26
  43. data/lib/concurrent/executor/immediate_executor.rb +9 -9
  44. data/lib/concurrent/executor/indirect_immediate_executor.rb +4 -3
  45. data/lib/concurrent/executor/java_cached_thread_pool.rb +18 -16
  46. data/lib/concurrent/executor/java_fixed_thread_pool.rb +11 -18
  47. data/lib/concurrent/executor/java_single_thread_executor.rb +17 -16
  48. data/lib/concurrent/executor/java_thread_pool_executor.rb +55 -102
  49. data/lib/concurrent/executor/ruby_cached_thread_pool.rb +9 -18
  50. data/lib/concurrent/executor/ruby_fixed_thread_pool.rb +10 -21
  51. data/lib/concurrent/executor/ruby_single_thread_executor.rb +14 -16
  52. data/lib/concurrent/executor/ruby_thread_pool_executor.rb +250 -166
  53. data/lib/concurrent/executor/safe_task_executor.rb +5 -4
  54. data/lib/concurrent/executor/serialized_execution.rb +22 -18
  55. data/lib/concurrent/executor/{per_thread_executor.rb → simple_executor_service.rb} +29 -20
  56. data/lib/concurrent/executor/single_thread_executor.rb +32 -21
  57. data/lib/concurrent/executor/thread_pool_executor.rb +72 -60
  58. data/lib/concurrent/executor/timer_set.rb +96 -84
  59. data/lib/concurrent/executors.rb +1 -1
  60. data/lib/concurrent/future.rb +70 -38
  61. data/lib/concurrent/immutable_struct.rb +89 -0
  62. data/lib/concurrent/ivar.rb +152 -60
  63. data/lib/concurrent/lazy_register.rb +40 -20
  64. data/lib/concurrent/maybe.rb +226 -0
  65. data/lib/concurrent/mutable_struct.rb +227 -0
  66. data/lib/concurrent/mvar.rb +44 -43
  67. data/lib/concurrent/promise.rb +208 -134
  68. data/lib/concurrent/scheduled_task.rb +339 -43
  69. data/lib/concurrent/settable_struct.rb +127 -0
  70. data/lib/concurrent/synchronization.rb +17 -0
  71. data/lib/concurrent/synchronization/abstract_object.rb +163 -0
  72. data/lib/concurrent/synchronization/abstract_struct.rb +158 -0
  73. data/lib/concurrent/synchronization/condition.rb +53 -0
  74. data/lib/concurrent/synchronization/java_object.rb +35 -0
  75. data/lib/concurrent/synchronization/lock.rb +32 -0
  76. data/lib/concurrent/synchronization/monitor_object.rb +24 -0
  77. data/lib/concurrent/synchronization/mutex_object.rb +43 -0
  78. data/lib/concurrent/synchronization/object.rb +78 -0
  79. data/lib/concurrent/synchronization/rbx_object.rb +75 -0
  80. data/lib/concurrent/timer_task.rb +87 -100
  81. data/lib/concurrent/tvar.rb +42 -38
  82. data/lib/concurrent/utilities.rb +3 -1
  83. data/lib/concurrent/utility/at_exit.rb +97 -0
  84. data/lib/concurrent/utility/engine.rb +40 -0
  85. data/lib/concurrent/utility/monotonic_time.rb +59 -0
  86. data/lib/concurrent/utility/native_extension_loader.rb +56 -0
  87. data/lib/concurrent/utility/processor_counter.rb +156 -0
  88. data/lib/concurrent/utility/timeout.rb +18 -14
  89. data/lib/concurrent/utility/timer.rb +11 -6
  90. data/lib/concurrent/version.rb +2 -1
  91. data/lib/concurrent_ruby.rb +1 -0
  92. metadata +47 -83
  93. data/lib/concurrent/actor.rb +0 -103
  94. data/lib/concurrent/actor/behaviour.rb +0 -70
  95. data/lib/concurrent/actor/behaviour/abstract.rb +0 -48
  96. data/lib/concurrent/actor/behaviour/awaits.rb +0 -21
  97. data/lib/concurrent/actor/behaviour/buffer.rb +0 -54
  98. data/lib/concurrent/actor/behaviour/errors_on_unknown_message.rb +0 -12
  99. data/lib/concurrent/actor/behaviour/executes_context.rb +0 -18
  100. data/lib/concurrent/actor/behaviour/linking.rb +0 -45
  101. data/lib/concurrent/actor/behaviour/pausing.rb +0 -77
  102. data/lib/concurrent/actor/behaviour/removes_child.rb +0 -16
  103. data/lib/concurrent/actor/behaviour/sets_results.rb +0 -36
  104. data/lib/concurrent/actor/behaviour/supervised.rb +0 -59
  105. data/lib/concurrent/actor/behaviour/supervising.rb +0 -34
  106. data/lib/concurrent/actor/behaviour/terminates_children.rb +0 -13
  107. data/lib/concurrent/actor/behaviour/termination.rb +0 -54
  108. data/lib/concurrent/actor/context.rb +0 -154
  109. data/lib/concurrent/actor/core.rb +0 -217
  110. data/lib/concurrent/actor/default_dead_letter_handler.rb +0 -9
  111. data/lib/concurrent/actor/envelope.rb +0 -41
  112. data/lib/concurrent/actor/errors.rb +0 -27
  113. data/lib/concurrent/actor/internal_delegations.rb +0 -49
  114. data/lib/concurrent/actor/public_delegations.rb +0 -40
  115. data/lib/concurrent/actor/reference.rb +0 -81
  116. data/lib/concurrent/actor/root.rb +0 -37
  117. data/lib/concurrent/actor/type_check.rb +0 -48
  118. data/lib/concurrent/actor/utils.rb +0 -10
  119. data/lib/concurrent/actor/utils/ad_hoc.rb +0 -21
  120. data/lib/concurrent/actor/utils/balancer.rb +0 -42
  121. data/lib/concurrent/actor/utils/broadcast.rb +0 -52
  122. data/lib/concurrent/actor/utils/pool.rb +0 -59
  123. data/lib/concurrent/actress.rb +0 -3
  124. data/lib/concurrent/agent.rb +0 -209
  125. data/lib/concurrent/atomic.rb +0 -92
  126. data/lib/concurrent/atomic/copy_on_notify_observer_set.rb +0 -118
  127. data/lib/concurrent/atomic/copy_on_write_observer_set.rb +0 -117
  128. data/lib/concurrent/atomic/synchronization.rb +0 -51
  129. data/lib/concurrent/channel/buffered_channel.rb +0 -85
  130. data/lib/concurrent/channel/channel.rb +0 -41
  131. data/lib/concurrent/channel/unbuffered_channel.rb +0 -35
  132. data/lib/concurrent/channel/waitable_list.rb +0 -40
  133. data/lib/concurrent/channels.rb +0 -5
  134. data/lib/concurrent/collection/blocking_ring_buffer.rb +0 -71
  135. data/lib/concurrent/collection/ring_buffer.rb +0 -59
  136. data/lib/concurrent/collections.rb +0 -3
  137. data/lib/concurrent/dereferenceable.rb +0 -108
  138. data/lib/concurrent/executor/ruby_thread_pool_worker.rb +0 -73
  139. data/lib/concurrent/logging.rb +0 -20
  140. data/lib/concurrent/obligation.rb +0 -171
  141. data/lib/concurrent/observable.rb +0 -73
  142. data/lib/concurrent/options_parser.rb +0 -52
  143. data/lib/concurrent/utility/processor_count.rb +0 -152
  144. data/lib/extension_helper.rb +0 -37
@@ -1,3 +0,0 @@
1
- require 'concurrent/actor'
2
-
3
- Concurrent::Actress = Concurrent::Actor
@@ -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
@@ -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