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.
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
OSZAR »