concurrent-ruby 1.1.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (143) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +478 -0
  3. data/Gemfile +41 -0
  4. data/LICENSE.md +23 -0
  5. data/README.md +381 -0
  6. data/Rakefile +327 -0
  7. data/ext/concurrent-ruby/ConcurrentRubyService.java +17 -0
  8. data/ext/concurrent-ruby/com/concurrent_ruby/ext/AtomicReferenceLibrary.java +175 -0
  9. data/ext/concurrent-ruby/com/concurrent_ruby/ext/JRubyMapBackendLibrary.java +248 -0
  10. data/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaAtomicBooleanLibrary.java +93 -0
  11. data/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaAtomicFixnumLibrary.java +113 -0
  12. data/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaSemaphoreLibrary.java +159 -0
  13. data/ext/concurrent-ruby/com/concurrent_ruby/ext/SynchronizationLibrary.java +307 -0
  14. data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/ConcurrentHashMap.java +31 -0
  15. data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/ConcurrentHashMapV8.java +3863 -0
  16. data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/LongAdder.java +203 -0
  17. data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/Striped64.java +342 -0
  18. data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/ConcurrentHashMapV8.java +3800 -0
  19. data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/LongAdder.java +204 -0
  20. data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/Striped64.java +291 -0
  21. data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166y/ThreadLocalRandom.java +199 -0
  22. data/lib/concurrent-ruby.rb +1 -0
  23. data/lib/concurrent.rb +134 -0
  24. data/lib/concurrent/agent.rb +587 -0
  25. data/lib/concurrent/array.rb +66 -0
  26. data/lib/concurrent/async.rb +459 -0
  27. data/lib/concurrent/atom.rb +222 -0
  28. data/lib/concurrent/atomic/abstract_thread_local_var.rb +66 -0
  29. data/lib/concurrent/atomic/atomic_boolean.rb +126 -0
  30. data/lib/concurrent/atomic/atomic_fixnum.rb +143 -0
  31. data/lib/concurrent/atomic/atomic_markable_reference.rb +164 -0
  32. data/lib/concurrent/atomic/atomic_reference.rb +204 -0
  33. data/lib/concurrent/atomic/count_down_latch.rb +100 -0
  34. data/lib/concurrent/atomic/cyclic_barrier.rb +128 -0
  35. data/lib/concurrent/atomic/event.rb +109 -0
  36. data/lib/concurrent/atomic/java_count_down_latch.rb +42 -0
  37. data/lib/concurrent/atomic/java_thread_local_var.rb +37 -0
  38. data/lib/concurrent/atomic/mutex_atomic_boolean.rb +62 -0
  39. data/lib/concurrent/atomic/mutex_atomic_fixnum.rb +75 -0
  40. data/lib/concurrent/atomic/mutex_count_down_latch.rb +44 -0
  41. data/lib/concurrent/atomic/mutex_semaphore.rb +115 -0
  42. data/lib/concurrent/atomic/read_write_lock.rb +254 -0
  43. data/lib/concurrent/atomic/reentrant_read_write_lock.rb +379 -0
  44. data/lib/concurrent/atomic/ruby_thread_local_var.rb +161 -0
  45. data/lib/concurrent/atomic/semaphore.rb +145 -0
  46. data/lib/concurrent/atomic/thread_local_var.rb +104 -0
  47. data/lib/concurrent/atomic_reference/mutex_atomic.rb +56 -0
  48. data/lib/concurrent/atomic_reference/numeric_cas_wrapper.rb +28 -0
  49. data/lib/concurrent/atomics.rb +10 -0
  50. data/lib/concurrent/collection/copy_on_notify_observer_set.rb +107 -0
  51. data/lib/concurrent/collection/copy_on_write_observer_set.rb +111 -0
  52. data/lib/concurrent/collection/java_non_concurrent_priority_queue.rb +84 -0
  53. data/lib/concurrent/collection/lock_free_stack.rb +158 -0
  54. data/lib/concurrent/collection/map/atomic_reference_map_backend.rb +927 -0
  55. data/lib/concurrent/collection/map/mri_map_backend.rb +66 -0
  56. data/lib/concurrent/collection/map/non_concurrent_map_backend.rb +140 -0
  57. data/lib/concurrent/collection/map/synchronized_map_backend.rb +82 -0
  58. data/lib/concurrent/collection/non_concurrent_priority_queue.rb +143 -0
  59. data/lib/concurrent/collection/ruby_non_concurrent_priority_queue.rb +150 -0
  60. data/lib/concurrent/concern/deprecation.rb +34 -0
  61. data/lib/concurrent/concern/dereferenceable.rb +73 -0
  62. data/lib/concurrent/concern/logging.rb +32 -0
  63. data/lib/concurrent/concern/obligation.rb +220 -0
  64. data/lib/concurrent/concern/observable.rb +110 -0
  65. data/lib/concurrent/concurrent_ruby.jar +0 -0
  66. data/lib/concurrent/configuration.rb +184 -0
  67. data/lib/concurrent/constants.rb +8 -0
  68. data/lib/concurrent/dataflow.rb +81 -0
  69. data/lib/concurrent/delay.rb +199 -0
  70. data/lib/concurrent/errors.rb +69 -0
  71. data/lib/concurrent/exchanger.rb +352 -0
  72. data/lib/concurrent/executor/abstract_executor_service.rb +134 -0
  73. data/lib/concurrent/executor/cached_thread_pool.rb +62 -0
  74. data/lib/concurrent/executor/executor_service.rb +185 -0
  75. data/lib/concurrent/executor/fixed_thread_pool.rb +206 -0
  76. data/lib/concurrent/executor/immediate_executor.rb +66 -0
  77. data/lib/concurrent/executor/indirect_immediate_executor.rb +44 -0
  78. data/lib/concurrent/executor/java_executor_service.rb +91 -0
  79. data/lib/concurrent/executor/java_single_thread_executor.rb +29 -0
  80. data/lib/concurrent/executor/java_thread_pool_executor.rb +123 -0
  81. data/lib/concurrent/executor/ruby_executor_service.rb +78 -0
  82. data/lib/concurrent/executor/ruby_single_thread_executor.rb +22 -0
  83. data/lib/concurrent/executor/ruby_thread_pool_executor.rb +362 -0
  84. data/lib/concurrent/executor/safe_task_executor.rb +35 -0
  85. data/lib/concurrent/executor/serial_executor_service.rb +34 -0
  86. data/lib/concurrent/executor/serialized_execution.rb +107 -0
  87. data/lib/concurrent/executor/serialized_execution_delegator.rb +28 -0
  88. data/lib/concurrent/executor/simple_executor_service.rb +100 -0
  89. data/lib/concurrent/executor/single_thread_executor.rb +56 -0
  90. data/lib/concurrent/executor/thread_pool_executor.rb +87 -0
  91. data/lib/concurrent/executor/timer_set.rb +173 -0
  92. data/lib/concurrent/executors.rb +20 -0
  93. data/lib/concurrent/future.rb +141 -0
  94. data/lib/concurrent/hash.rb +59 -0
  95. data/lib/concurrent/immutable_struct.rb +93 -0
  96. data/lib/concurrent/ivar.rb +207 -0
  97. data/lib/concurrent/map.rb +337 -0
  98. data/lib/concurrent/maybe.rb +229 -0
  99. data/lib/concurrent/mutable_struct.rb +229 -0
  100. data/lib/concurrent/mvar.rb +242 -0
  101. data/lib/concurrent/options.rb +42 -0
  102. data/lib/concurrent/promise.rb +579 -0
  103. data/lib/concurrent/promises.rb +2167 -0
  104. data/lib/concurrent/re_include.rb +58 -0
  105. data/lib/concurrent/scheduled_task.rb +318 -0
  106. data/lib/concurrent/set.rb +66 -0
  107. data/lib/concurrent/settable_struct.rb +129 -0
  108. data/lib/concurrent/synchronization.rb +30 -0
  109. data/lib/concurrent/synchronization/abstract_lockable_object.rb +98 -0
  110. data/lib/concurrent/synchronization/abstract_object.rb +24 -0
  111. data/lib/concurrent/synchronization/abstract_struct.rb +160 -0
  112. data/lib/concurrent/synchronization/condition.rb +60 -0
  113. data/lib/concurrent/synchronization/jruby_lockable_object.rb +13 -0
  114. data/lib/concurrent/synchronization/jruby_object.rb +45 -0
  115. data/lib/concurrent/synchronization/lock.rb +36 -0
  116. data/lib/concurrent/synchronization/lockable_object.rb +74 -0
  117. data/lib/concurrent/synchronization/mri_object.rb +44 -0
  118. data/lib/concurrent/synchronization/mutex_lockable_object.rb +76 -0
  119. data/lib/concurrent/synchronization/object.rb +183 -0
  120. data/lib/concurrent/synchronization/rbx_lockable_object.rb +65 -0
  121. data/lib/concurrent/synchronization/rbx_object.rb +49 -0
  122. data/lib/concurrent/synchronization/truffleruby_object.rb +47 -0
  123. data/lib/concurrent/synchronization/volatile.rb +36 -0
  124. data/lib/concurrent/thread_safe/synchronized_delegator.rb +50 -0
  125. data/lib/concurrent/thread_safe/util.rb +16 -0
  126. data/lib/concurrent/thread_safe/util/adder.rb +74 -0
  127. data/lib/concurrent/thread_safe/util/cheap_lockable.rb +118 -0
  128. data/lib/concurrent/thread_safe/util/data_structures.rb +63 -0
  129. data/lib/concurrent/thread_safe/util/power_of_two_tuple.rb +38 -0
  130. data/lib/concurrent/thread_safe/util/striped64.rb +246 -0
  131. data/lib/concurrent/thread_safe/util/volatile.rb +75 -0
  132. data/lib/concurrent/thread_safe/util/xor_shift_random.rb +50 -0
  133. data/lib/concurrent/timer_task.rb +334 -0
  134. data/lib/concurrent/tuple.rb +86 -0
  135. data/lib/concurrent/tvar.rb +258 -0
  136. data/lib/concurrent/utility/at_exit.rb +97 -0
  137. data/lib/concurrent/utility/engine.rb +56 -0
  138. data/lib/concurrent/utility/monotonic_time.rb +58 -0
  139. data/lib/concurrent/utility/native_extension_loader.rb +79 -0
  140. data/lib/concurrent/utility/native_integer.rb +53 -0
  141. data/lib/concurrent/utility/processor_counter.rb +158 -0
  142. data/lib/concurrent/version.rb +3 -0
  143. metadata +193 -0
@@ -0,0 +1,34 @@
1
+ require 'concurrent/concern/logging'
2
+
3
+ module Concurrent
4
+ module Concern
5
+
6
+ # @!visibility private
7
+ # @!macro internal_implementation_note
8
+ module Deprecation
9
+ # TODO require additional parameter: a version. Display when it'll be removed based on that. Error if not removed.
10
+ include Concern::Logging
11
+
12
+ def deprecated(message, strip = 2)
13
+ caller_line = caller(strip).first if strip > 0
14
+ klass = if Module === self
15
+ self
16
+ else
17
+ self.class
18
+ end
19
+ message = if strip > 0
20
+ format("[DEPRECATED] %s\ncalled on: %s", message, caller_line)
21
+ else
22
+ format('[DEPRECATED] %s', message)
23
+ end
24
+ log WARN, klass.to_s, message
25
+ end
26
+
27
+ def deprecated_method(old_name, new_name)
28
+ deprecated "`#{old_name}` is deprecated and it'll removed in next release, use `#{new_name}` instead", 3
29
+ end
30
+
31
+ extend self
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,73 @@
1
+ module Concurrent
2
+ module Concern
3
+
4
+ # Object references in Ruby are mutable. This can lead to serious problems when
5
+ # the `#value` of a concurrent object is a mutable reference. Which is always the
6
+ # case unless the value is a `Fixnum`, `Symbol`, or similar "primitive" data type.
7
+ # Most classes in this library that expose a `#value` getter method do so using the
8
+ # `Dereferenceable` mixin module.
9
+ #
10
+ # @!macro copy_options
11
+ module Dereferenceable
12
+ # NOTE: This module is going away in 2.0. In the mean time we need it to
13
+ # play nicely with the synchronization layer. This means that the
14
+ # including class SHOULD be synchronized and it MUST implement a
15
+ # `#synchronize` method. Not doing so will lead to runtime errors.
16
+
17
+ # Return the value this object represents after applying the options specified
18
+ # by the `#set_deref_options` method.
19
+ #
20
+ # @return [Object] the current value of the object
21
+ def value
22
+ synchronize { apply_deref_options(@value) }
23
+ end
24
+ alias_method :deref, :value
25
+
26
+ protected
27
+
28
+ # Set the internal value of this object
29
+ #
30
+ # @param [Object] value the new value
31
+ def value=(value)
32
+ synchronize{ @value = value }
33
+ end
34
+
35
+ # @!macro dereferenceable_set_deref_options
36
+ # Set the options which define the operations #value performs before
37
+ # returning data to the caller (dereferencing).
38
+ #
39
+ # @note Most classes that include this module will call `#set_deref_options`
40
+ # from within the constructor, thus allowing these options to be set at
41
+ # object creation.
42
+ #
43
+ # @param [Hash] opts the options defining dereference behavior.
44
+ # @option opts [String] :dup_on_deref (false) call `#dup` before returning the data
45
+ # @option opts [String] :freeze_on_deref (false) call `#freeze` before returning the data
46
+ # @option opts [String] :copy_on_deref (nil) call the given `Proc` passing
47
+ # the internal value and returning the value returned from the proc
48
+ def set_deref_options(opts = {})
49
+ synchronize{ ns_set_deref_options(opts) }
50
+ end
51
+
52
+ # @!macro dereferenceable_set_deref_options
53
+ # @!visibility private
54
+ def ns_set_deref_options(opts)
55
+ @dup_on_deref = opts[:dup_on_deref] || opts[:dup]
56
+ @freeze_on_deref = opts[:freeze_on_deref] || opts[:freeze]
57
+ @copy_on_deref = opts[:copy_on_deref] || opts[:copy]
58
+ @do_nothing_on_deref = !(@dup_on_deref || @freeze_on_deref || @copy_on_deref)
59
+ nil
60
+ end
61
+
62
+ # @!visibility private
63
+ def apply_deref_options(value)
64
+ return nil if value.nil?
65
+ return value if @do_nothing_on_deref
66
+ value = @copy_on_deref.call(value) if @copy_on_deref
67
+ value = value.dup if @dup_on_deref
68
+ value = value.freeze if @freeze_on_deref
69
+ value
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,32 @@
1
+ require 'logger'
2
+
3
+ module Concurrent
4
+ module Concern
5
+
6
+ # Include where logging is needed
7
+ #
8
+ # @!visibility private
9
+ module Logging
10
+ include Logger::Severity
11
+
12
+ # Logs through {Concurrent.global_logger}, it can be overridden by setting @logger
13
+ # @param [Integer] level one of Logger::Severity constants
14
+ # @param [String] progname e.g. a path of an Actor
15
+ # @param [String, nil] message when nil block is used to generate the message
16
+ # @yieldreturn [String] a message
17
+ def log(level, progname, message = nil, &block)
18
+ #NOTE: Cannot require 'concurrent/configuration' above due to circular references.
19
+ # Assume that the gem has been initialized if we've gotten this far.
20
+ logger = if defined?(@logger) && @logger
21
+ @logger
22
+ else
23
+ Concurrent.global_logger
24
+ end
25
+ logger.call level, progname, message, &block
26
+ rescue => error
27
+ $stderr.puts "`Concurrent.configuration.logger` failed to log #{[level, progname, message, block]}\n" +
28
+ "#{error.message} (#{error.class})\n#{error.backtrace.join "\n"}"
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,220 @@
1
+ require 'thread'
2
+ require 'timeout'
3
+
4
+ require 'concurrent/atomic/event'
5
+ require 'concurrent/concern/dereferenceable'
6
+
7
+ module Concurrent
8
+ module Concern
9
+
10
+ module Obligation
11
+ include Concern::Dereferenceable
12
+ # NOTE: The Dereferenceable module is going away in 2.0. In the mean time
13
+ # we need it to place nicely with the synchronization layer. This means
14
+ # that the including class SHOULD be synchronized and it MUST implement a
15
+ # `#synchronize` method. Not doing so will lead to runtime errors.
16
+
17
+ # Has the obligation been fulfilled?
18
+ #
19
+ # @return [Boolean]
20
+ def fulfilled?
21
+ state == :fulfilled
22
+ end
23
+ alias_method :realized?, :fulfilled?
24
+
25
+ # Has the obligation been rejected?
26
+ #
27
+ # @return [Boolean]
28
+ def rejected?
29
+ state == :rejected
30
+ end
31
+
32
+ # Is obligation completion still pending?
33
+ #
34
+ # @return [Boolean]
35
+ def pending?
36
+ state == :pending
37
+ end
38
+
39
+ # Is the obligation still unscheduled?
40
+ #
41
+ # @return [Boolean]
42
+ def unscheduled?
43
+ state == :unscheduled
44
+ end
45
+
46
+ # Has the obligation completed processing?
47
+ #
48
+ # @return [Boolean]
49
+ def complete?
50
+ [:fulfilled, :rejected].include? state
51
+ end
52
+
53
+ # Is the obligation still awaiting completion of processing?
54
+ #
55
+ # @return [Boolean]
56
+ def incomplete?
57
+ ! complete?
58
+ end
59
+
60
+ # The current value of the obligation. Will be `nil` while the state is
61
+ # pending or the operation has been rejected.
62
+ #
63
+ # @param [Numeric] timeout the maximum time in seconds to wait.
64
+ # @return [Object] see Dereferenceable#deref
65
+ def value(timeout = nil)
66
+ wait timeout
67
+ deref
68
+ end
69
+
70
+ # Wait until obligation is complete or the timeout has been reached.
71
+ #
72
+ # @param [Numeric] timeout the maximum time in seconds to wait.
73
+ # @return [Obligation] self
74
+ def wait(timeout = nil)
75
+ event.wait(timeout) if timeout != 0 && incomplete?
76
+ self
77
+ end
78
+
79
+ # Wait until obligation is complete or the timeout is reached. Will re-raise
80
+ # any exceptions raised during processing (but will not raise an exception
81
+ # on timeout).
82
+ #
83
+ # @param [Numeric] timeout the maximum time in seconds to wait.
84
+ # @return [Obligation] self
85
+ # @raise [Exception] raises the reason when rejected
86
+ def wait!(timeout = nil)
87
+ wait(timeout).tap { raise self if rejected? }
88
+ end
89
+ alias_method :no_error!, :wait!
90
+
91
+ # The current value of the obligation. Will be `nil` while the state is
92
+ # pending or the operation has been rejected. Will re-raise any exceptions
93
+ # raised during processing (but will not raise an exception on timeout).
94
+ #
95
+ # @param [Numeric] timeout the maximum time in seconds to wait.
96
+ # @return [Object] see Dereferenceable#deref
97
+ # @raise [Exception] raises the reason when rejected
98
+ def value!(timeout = nil)
99
+ wait(timeout)
100
+ if rejected?
101
+ raise self
102
+ else
103
+ deref
104
+ end
105
+ end
106
+
107
+ # The current state of the obligation.
108
+ #
109
+ # @return [Symbol] the current state
110
+ def state
111
+ synchronize { @state }
112
+ end
113
+
114
+ # If an exception was raised during processing this will return the
115
+ # exception object. Will return `nil` when the state is pending or if
116
+ # the obligation has been successfully fulfilled.
117
+ #
118
+ # @return [Exception] the exception raised during processing or `nil`
119
+ def reason
120
+ synchronize { @reason }
121
+ end
122
+
123
+ # @example allows Obligation to be risen
124
+ # rejected_ivar = Ivar.new.fail
125
+ # raise rejected_ivar
126
+ def exception(*args)
127
+ raise 'obligation is not rejected' unless rejected?
128
+ reason.exception(*args)
129
+ end
130
+
131
+ protected
132
+
133
+ # @!visibility private
134
+ def get_arguments_from(opts = {})
135
+ [*opts.fetch(:args, [])]
136
+ end
137
+
138
+ # @!visibility private
139
+ def init_obligation
140
+ @event = Event.new
141
+ @value = @reason = nil
142
+ end
143
+
144
+ # @!visibility private
145
+ def event
146
+ @event
147
+ end
148
+
149
+ # @!visibility private
150
+ def set_state(success, value, reason)
151
+ if success
152
+ @value = value
153
+ @state = :fulfilled
154
+ else
155
+ @reason = reason
156
+ @state = :rejected
157
+ end
158
+ end
159
+
160
+ # @!visibility private
161
+ def state=(value)
162
+ synchronize { ns_set_state(value) }
163
+ end
164
+
165
+ # Atomic compare and set operation
166
+ # State is set to `next_state` only if `current state == expected_current`.
167
+ #
168
+ # @param [Symbol] next_state
169
+ # @param [Symbol] expected_current
170
+ #
171
+ # @return [Boolean] true is state is changed, false otherwise
172
+ #
173
+ # @!visibility private
174
+ def compare_and_set_state(next_state, *expected_current)
175
+ synchronize do
176
+ if expected_current.include? @state
177
+ @state = next_state
178
+ true
179
+ else
180
+ false
181
+ end
182
+ end
183
+ end
184
+
185
+ # Executes the block within mutex if current state is included in expected_states
186
+ #
187
+ # @return block value if executed, false otherwise
188
+ #
189
+ # @!visibility private
190
+ def if_state(*expected_states)
191
+ synchronize do
192
+ raise ArgumentError.new('no block given') unless block_given?
193
+
194
+ if expected_states.include? @state
195
+ yield
196
+ else
197
+ false
198
+ end
199
+ end
200
+ end
201
+
202
+ protected
203
+
204
+ # Am I in the current state?
205
+ #
206
+ # @param [Symbol] expected The state to check against
207
+ # @return [Boolean] true if in the expected state else false
208
+ #
209
+ # @!visibility private
210
+ def ns_check_state?(expected)
211
+ @state == expected
212
+ end
213
+
214
+ # @!visibility private
215
+ def ns_set_state(value)
216
+ @state = value
217
+ end
218
+ end
219
+ end
220
+ end
@@ -0,0 +1,110 @@
1
+ require 'concurrent/collection/copy_on_notify_observer_set'
2
+ require 'concurrent/collection/copy_on_write_observer_set'
3
+
4
+ module Concurrent
5
+ module Concern
6
+
7
+ # The [observer pattern](http://en.wikipedia.org/wiki/Observer_pattern) is one
8
+ # of the most useful design patterns.
9
+ #
10
+ # The workflow is very simple:
11
+ # - an `observer` can register itself to a `subject` via a callback
12
+ # - many `observers` can be registered to the same `subject`
13
+ # - the `subject` notifies all registered observers when its status changes
14
+ # - an `observer` can deregister itself when is no more interested to receive
15
+ # event notifications
16
+ #
17
+ # In a single threaded environment the whole pattern is very easy: the
18
+ # `subject` can use a simple data structure to manage all its subscribed
19
+ # `observer`s and every `observer` can react directly to every event without
20
+ # caring about synchronization.
21
+ #
22
+ # In a multi threaded environment things are more complex. The `subject` must
23
+ # synchronize the access to its data structure and to do so currently we're
24
+ # using two specialized ObserverSet: {Concurrent::Concern::CopyOnWriteObserverSet}
25
+ # and {Concurrent::Concern::CopyOnNotifyObserverSet}.
26
+ #
27
+ # When implementing and `observer` there's a very important rule to remember:
28
+ # **there are no guarantees about the thread that will execute the callback**
29
+ #
30
+ # Let's take this example
31
+ # ```
32
+ # class Observer
33
+ # def initialize
34
+ # @count = 0
35
+ # end
36
+ #
37
+ # def update
38
+ # @count += 1
39
+ # end
40
+ # end
41
+ #
42
+ # obs = Observer.new
43
+ # [obj1, obj2, obj3, obj4].each { |o| o.add_observer(obs) }
44
+ # # execute [obj1, obj2, obj3, obj4]
45
+ # ```
46
+ #
47
+ # `obs` is wrong because the variable `@count` can be accessed by different
48
+ # threads at the same time, so it should be synchronized (using either a Mutex
49
+ # or an AtomicFixum)
50
+ module Observable
51
+
52
+ # @!macro observable_add_observer
53
+ #
54
+ # Adds an observer to this set. If a block is passed, the observer will be
55
+ # created by this method and no other params should be passed.
56
+ #
57
+ # @param [Object] observer the observer to add
58
+ # @param [Symbol] func the function to call on the observer during notification.
59
+ # Default is :update
60
+ # @return [Object] the added observer
61
+ def add_observer(observer = nil, func = :update, &block)
62
+ observers.add_observer(observer, func, &block)
63
+ end
64
+
65
+ # As `#add_observer` but can be used for chaining.
66
+ #
67
+ # @param [Object] observer the observer to add
68
+ # @param [Symbol] func the function to call on the observer during notification.
69
+ # @return [Observable] self
70
+ def with_observer(observer = nil, func = :update, &block)
71
+ add_observer(observer, func, &block)
72
+ self
73
+ end
74
+
75
+ # @!macro observable_delete_observer
76
+ #
77
+ # Remove `observer` as an observer on this object so that it will no
78
+ # longer receive notifications.
79
+ #
80
+ # @param [Object] observer the observer to remove
81
+ # @return [Object] the deleted observer
82
+ def delete_observer(observer)
83
+ observers.delete_observer(observer)
84
+ end
85
+
86
+ # @!macro observable_delete_observers
87
+ #
88
+ # Remove all observers associated with this object.
89
+ #
90
+ # @return [Observable] self
91
+ def delete_observers
92
+ observers.delete_observers
93
+ self
94
+ end
95
+
96
+ # @!macro observable_count_observers
97
+ #
98
+ # Return the number of observers associated with this object.
99
+ #
100
+ # @return [Integer] the observers count
101
+ def count_observers
102
+ observers.count_observers
103
+ end
104
+
105
+ protected
106
+
107
+ attr_accessor :observers
108
+ end
109
+ end
110
+ end