concurrent-ruby 0.8.0.pre2-java → 0.9.0-java

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 (145) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +114 -3
  3. data/README.md +111 -55
  4. data/lib/concurrent.rb +90 -14
  5. data/lib/concurrent/async.rb +143 -51
  6. data/lib/concurrent/atom.rb +131 -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 +34 -3
  19. data/lib/concurrent/atomic_reference/jruby.rb +6 -3
  20. data/lib/concurrent/atomic_reference/mutex_atomic.rb +17 -39
  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 +34 -0
  29. data/lib/concurrent/concern/dereferenceable.rb +88 -0
  30. data/lib/concurrent/concern/logging.rb +27 -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 +234 -109
  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 +19 -7
  38. data/lib/concurrent/exchanger.rb +25 -1
  39. data/lib/concurrent/executor/cached_thread_pool.rb +51 -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 +196 -23
  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_single_thread_executor.rb +17 -16
  46. data/lib/concurrent/executor/java_thread_pool_executor.rb +55 -102
  47. data/lib/concurrent/executor/ruby_single_thread_executor.rb +14 -16
  48. data/lib/concurrent/executor/ruby_thread_pool_executor.rb +250 -166
  49. data/lib/concurrent/executor/safe_task_executor.rb +5 -4
  50. data/lib/concurrent/executor/serialized_execution.rb +22 -18
  51. data/lib/concurrent/executor/{per_thread_executor.rb → simple_executor_service.rb} +29 -20
  52. data/lib/concurrent/executor/single_thread_executor.rb +32 -21
  53. data/lib/concurrent/executor/thread_pool_executor.rb +73 -60
  54. data/lib/concurrent/executor/timer_set.rb +96 -84
  55. data/lib/concurrent/executors.rb +1 -1
  56. data/lib/concurrent/future.rb +71 -38
  57. data/lib/concurrent/immutable_struct.rb +89 -0
  58. data/lib/concurrent/ivar.rb +152 -60
  59. data/lib/concurrent/lazy_register.rb +40 -20
  60. data/lib/concurrent/maybe.rb +226 -0
  61. data/lib/concurrent/mutable_struct.rb +227 -0
  62. data/lib/concurrent/mvar.rb +44 -43
  63. data/lib/concurrent/promise.rb +229 -136
  64. data/lib/concurrent/scheduled_task.rb +341 -43
  65. data/lib/concurrent/settable_struct.rb +127 -0
  66. data/lib/concurrent/synchronization.rb +17 -0
  67. data/lib/concurrent/synchronization/abstract_object.rb +163 -0
  68. data/lib/concurrent/synchronization/abstract_struct.rb +158 -0
  69. data/lib/concurrent/synchronization/condition.rb +53 -0
  70. data/lib/concurrent/synchronization/java_object.rb +34 -0
  71. data/lib/concurrent/synchronization/lock.rb +32 -0
  72. data/lib/concurrent/synchronization/monitor_object.rb +26 -0
  73. data/lib/concurrent/synchronization/mutex_object.rb +43 -0
  74. data/lib/concurrent/synchronization/object.rb +78 -0
  75. data/lib/concurrent/synchronization/rbx_object.rb +75 -0
  76. data/lib/concurrent/timer_task.rb +92 -103
  77. data/lib/concurrent/tvar.rb +42 -38
  78. data/lib/concurrent/utilities.rb +3 -1
  79. data/lib/concurrent/utility/at_exit.rb +97 -0
  80. data/lib/concurrent/utility/engine.rb +44 -0
  81. data/lib/concurrent/utility/monotonic_time.rb +59 -0
  82. data/lib/concurrent/utility/native_extension_loader.rb +56 -0
  83. data/lib/concurrent/utility/processor_counter.rb +156 -0
  84. data/lib/concurrent/utility/timeout.rb +18 -14
  85. data/lib/concurrent/utility/timer.rb +11 -6
  86. data/lib/concurrent/version.rb +2 -1
  87. data/lib/concurrent_ruby.rb +1 -0
  88. data/lib/concurrent_ruby_ext.jar +0 -0
  89. metadata +46 -66
  90. data/lib/concurrent/actor.rb +0 -103
  91. data/lib/concurrent/actor/behaviour.rb +0 -70
  92. data/lib/concurrent/actor/behaviour/abstract.rb +0 -48
  93. data/lib/concurrent/actor/behaviour/awaits.rb +0 -21
  94. data/lib/concurrent/actor/behaviour/buffer.rb +0 -54
  95. data/lib/concurrent/actor/behaviour/errors_on_unknown_message.rb +0 -12
  96. data/lib/concurrent/actor/behaviour/executes_context.rb +0 -18
  97. data/lib/concurrent/actor/behaviour/linking.rb +0 -45
  98. data/lib/concurrent/actor/behaviour/pausing.rb +0 -77
  99. data/lib/concurrent/actor/behaviour/removes_child.rb +0 -16
  100. data/lib/concurrent/actor/behaviour/sets_results.rb +0 -36
  101. data/lib/concurrent/actor/behaviour/supervised.rb +0 -59
  102. data/lib/concurrent/actor/behaviour/supervising.rb +0 -34
  103. data/lib/concurrent/actor/behaviour/terminates_children.rb +0 -13
  104. data/lib/concurrent/actor/behaviour/termination.rb +0 -54
  105. data/lib/concurrent/actor/context.rb +0 -154
  106. data/lib/concurrent/actor/core.rb +0 -217
  107. data/lib/concurrent/actor/default_dead_letter_handler.rb +0 -9
  108. data/lib/concurrent/actor/envelope.rb +0 -41
  109. data/lib/concurrent/actor/errors.rb +0 -27
  110. data/lib/concurrent/actor/internal_delegations.rb +0 -49
  111. data/lib/concurrent/actor/public_delegations.rb +0 -40
  112. data/lib/concurrent/actor/reference.rb +0 -81
  113. data/lib/concurrent/actor/root.rb +0 -37
  114. data/lib/concurrent/actor/type_check.rb +0 -48
  115. data/lib/concurrent/actor/utils.rb +0 -10
  116. data/lib/concurrent/actor/utils/ad_hoc.rb +0 -21
  117. data/lib/concurrent/actor/utils/balancer.rb +0 -42
  118. data/lib/concurrent/actor/utils/broadcast.rb +0 -52
  119. data/lib/concurrent/actor/utils/pool.rb +0 -59
  120. data/lib/concurrent/actress.rb +0 -3
  121. data/lib/concurrent/agent.rb +0 -209
  122. data/lib/concurrent/atomic.rb +0 -92
  123. data/lib/concurrent/atomic/copy_on_notify_observer_set.rb +0 -118
  124. data/lib/concurrent/atomic/copy_on_write_observer_set.rb +0 -117
  125. data/lib/concurrent/atomic/synchronization.rb +0 -51
  126. data/lib/concurrent/channel/buffered_channel.rb +0 -85
  127. data/lib/concurrent/channel/channel.rb +0 -41
  128. data/lib/concurrent/channel/unbuffered_channel.rb +0 -35
  129. data/lib/concurrent/channel/waitable_list.rb +0 -40
  130. data/lib/concurrent/channels.rb +0 -5
  131. data/lib/concurrent/collection/blocking_ring_buffer.rb +0 -71
  132. data/lib/concurrent/collection/ring_buffer.rb +0 -59
  133. data/lib/concurrent/collections.rb +0 -3
  134. data/lib/concurrent/dereferenceable.rb +0 -108
  135. data/lib/concurrent/executor/java_cached_thread_pool.rb +0 -32
  136. data/lib/concurrent/executor/java_fixed_thread_pool.rb +0 -31
  137. data/lib/concurrent/executor/ruby_cached_thread_pool.rb +0 -29
  138. data/lib/concurrent/executor/ruby_fixed_thread_pool.rb +0 -32
  139. data/lib/concurrent/executor/ruby_thread_pool_worker.rb +0 -73
  140. data/lib/concurrent/logging.rb +0 -20
  141. data/lib/concurrent/obligation.rb +0 -171
  142. data/lib/concurrent/observable.rb +0 -73
  143. data/lib/concurrent/options_parser.rb +0 -48
  144. data/lib/concurrent/utility/processor_count.rb +0 -152
  145. data/lib/extension_helper.rb +0 -37
@@ -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,88 @@
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
+
13
+ # Return the value this object represents after applying the options specified
14
+ # by the `#set_deref_options` method.
15
+ #
16
+ # @return [Object] the current value of the object
17
+ def value
18
+ mutex.synchronize { apply_deref_options(@value) }
19
+ end
20
+ alias_method :deref, :value
21
+
22
+ protected
23
+
24
+ # Set the internal value of this object
25
+ #
26
+ # @param [Object] value the new value
27
+ def value=(value)
28
+ mutex.synchronize{ @value = value }
29
+ end
30
+
31
+ # A mutex lock used for synchronizing thread-safe operations. Methods defined
32
+ # by `Dereferenceable` are synchronized using the `Mutex` returned from this
33
+ # method. Operations performed by the including class that operate on the
34
+ # `@value` instance variable should be locked with this `Mutex`.
35
+ #
36
+ # @return [Mutex] the synchronization object
37
+ def mutex
38
+ @mutex
39
+ end
40
+
41
+ # Initializes the internal `Mutex`.
42
+ #
43
+ # @note This method *must* be called from within the constructor of the including class.
44
+ #
45
+ # @see #mutex
46
+ def init_mutex(mutex = Mutex.new)
47
+ @mutex = mutex
48
+ end
49
+
50
+ # @!macro [attach] dereferenceable_set_deref_options
51
+ # Set the options which define the operations #value performs before
52
+ # returning data to the caller (dereferencing).
53
+ #
54
+ # @note Most classes that include this module will call `#set_deref_options`
55
+ # from within the constructor, thus allowing these options to be set at
56
+ # object creation.
57
+ #
58
+ # @param [Hash] opts the options defining dereference behavior.
59
+ # @option opts [String] :dup_on_deref (false) call `#dup` before returning the data
60
+ # @option opts [String] :freeze_on_deref (false) call `#freeze` before returning the data
61
+ # @option opts [String] :copy_on_deref (nil) call the given `Proc` passing
62
+ # the internal value and returning the value returned from the proc
63
+ def set_deref_options(opts = {})
64
+ mutex.synchronize{ ns_set_deref_options(opts) }
65
+ end
66
+
67
+ # @!macro dereferenceable_set_deref_options
68
+ # @!visibility private
69
+ def ns_set_deref_options(opts)
70
+ @dup_on_deref = opts[:dup_on_deref] || opts[:dup]
71
+ @freeze_on_deref = opts[:freeze_on_deref] || opts[:freeze]
72
+ @copy_on_deref = opts[:copy_on_deref] || opts[:copy]
73
+ @do_nothing_on_deref = !(@dup_on_deref || @freeze_on_deref || @copy_on_deref)
74
+ nil
75
+ end
76
+
77
+ # @!visibility private
78
+ def apply_deref_options(value)
79
+ return nil if value.nil?
80
+ return value if @do_nothing_on_deref
81
+ value = @copy_on_deref.call(value) if @copy_on_deref
82
+ value = value.dup if @dup_on_deref
83
+ value = value.freeze if @freeze_on_deref
84
+ value
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,27 @@
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 {Configuration#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 || Concurrent.global_logger).call level, progname, message, &block
21
+ rescue => error
22
+ $stderr.puts "`Concurrent.configuration.logger` failed to log #{[level, progname, message, block]}\n" +
23
+ "#{error.message} (#{error.class})\n#{error.backtrace.join "\n"}"
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,228 @@
1
+ require 'thread'
2
+ require 'timeout'
3
+
4
+ require 'concurrent/atomic/event'
5
+ require 'concurrent/concern/dereferenceable'
6
+ require 'concurrent/concern/deprecation'
7
+
8
+ module Concurrent
9
+ module Concern
10
+
11
+ module Obligation
12
+ include Concern::Dereferenceable
13
+ include Concern::Deprecation
14
+
15
+ # Has the obligation been fulfilled?
16
+ #
17
+ # @return [Boolean]
18
+ def fulfilled?
19
+ state == :fulfilled
20
+ end
21
+ alias_method :realized?, :fulfilled?
22
+
23
+ # Has the obligation been rejected?
24
+ #
25
+ # @return [Boolean]
26
+ def rejected?
27
+ state == :rejected
28
+ end
29
+
30
+ # Is obligation completion still pending?
31
+ #
32
+ # @return [Boolean]
33
+ def pending?
34
+ state == :pending
35
+ end
36
+
37
+ # Is the obligation still unscheduled?
38
+ #
39
+ # @return [Boolean]
40
+ def unscheduled?
41
+ state == :unscheduled
42
+ end
43
+
44
+ # Has the obligation completed processing?
45
+ #
46
+ # @return [Boolean]
47
+ def complete?
48
+ [:fulfilled, :rejected].include? state
49
+ end
50
+
51
+ # Has the obligation completed processing?
52
+ #
53
+ # @return [Boolean]
54
+ #
55
+ # @deprecated
56
+ def completed?
57
+ deprecated_method 'completed?', 'complete?'
58
+ complete?
59
+ end
60
+
61
+ # Is the obligation still awaiting completion of processing?
62
+ #
63
+ # @return [Boolean]
64
+ def incomplete?
65
+ ! complete?
66
+ end
67
+
68
+ # The current value of the obligation. Will be `nil` while the state is
69
+ # pending or the operation has been rejected.
70
+ #
71
+ # @param [Numeric] timeout the maximum time in seconds to wait.
72
+ # @return [Object] see Dereferenceable#deref
73
+ def value(timeout = nil)
74
+ wait timeout
75
+ deref
76
+ end
77
+
78
+ # Wait until obligation is complete or the timeout has been reached.
79
+ #
80
+ # @param [Numeric] timeout the maximum time in seconds to wait.
81
+ # @return [Obligation] self
82
+ def wait(timeout = nil)
83
+ event.wait(timeout) if timeout != 0 && incomplete?
84
+ self
85
+ end
86
+
87
+ # Wait until obligation is complete or the timeout is reached. Will re-raise
88
+ # any exceptions raised during processing (but will not raise an exception
89
+ # on timeout).
90
+ #
91
+ # @param [Numeric] timeout the maximum time in seconds to wait.
92
+ # @return [Obligation] self
93
+ # @raise [Exception] raises the reason when rejected
94
+ def wait!(timeout = nil)
95
+ wait(timeout).tap { raise self if rejected? }
96
+ end
97
+ alias_method :no_error!, :wait!
98
+
99
+ # The current value of the obligation. Will be `nil` while the state is
100
+ # pending or the operation has been rejected. Will re-raise any exceptions
101
+ # raised during processing (but will not raise an exception on timeout).
102
+ #
103
+ # @param [Numeric] timeout the maximum time in seconds to wait.
104
+ # @return [Object] see Dereferenceable#deref
105
+ # @raise [Exception] raises the reason when rejected
106
+ def value!(timeout = nil)
107
+ wait(timeout)
108
+ if rejected?
109
+ raise self
110
+ else
111
+ deref
112
+ end
113
+ end
114
+
115
+ # The current state of the obligation.
116
+ #
117
+ # @return [Symbol] the current state
118
+ def state
119
+ mutex.synchronize { @state }
120
+ end
121
+
122
+ # If an exception was raised during processing this will return the
123
+ # exception object. Will return `nil` when the state is pending or if
124
+ # the obligation has been successfully fulfilled.
125
+ #
126
+ # @return [Exception] the exception raised during processing or `nil`
127
+ def reason
128
+ mutex.synchronize { @reason }
129
+ end
130
+
131
+ # @example allows Obligation to be risen
132
+ # rejected_ivar = Ivar.new.fail
133
+ # raise rejected_ivar
134
+ def exception(*args)
135
+ raise 'obligation is not rejected' unless rejected?
136
+ reason.exception(*args)
137
+ end
138
+
139
+ protected
140
+
141
+ # @!visibility private
142
+ def get_arguments_from(opts = {})
143
+ [*opts.fetch(:args, [])]
144
+ end
145
+
146
+ # @!visibility private
147
+ def init_obligation(*args)
148
+ init_mutex(*args)
149
+ @event = Event.new
150
+ end
151
+
152
+ # @!visibility private
153
+ def event
154
+ @event
155
+ end
156
+
157
+ # @!visibility private
158
+ def set_state(success, value, reason)
159
+ if success
160
+ @value = value
161
+ @state = :fulfilled
162
+ else
163
+ @reason = reason
164
+ @state = :rejected
165
+ end
166
+ end
167
+
168
+ # @!visibility private
169
+ def state=(value)
170
+ mutex.synchronize { ns_set_state(value) }
171
+ end
172
+
173
+ # Atomic compare and set operation
174
+ # State is set to `next_state` only if `current state == expected_current`.
175
+ #
176
+ # @param [Symbol] next_state
177
+ # @param [Symbol] expected_current
178
+ #
179
+ # @return [Boolean] true is state is changed, false otherwise
180
+ #
181
+ # @!visibility private
182
+ def compare_and_set_state(next_state, *expected_current)
183
+ mutex.synchronize do
184
+ if expected_current.include? @state
185
+ @state = next_state
186
+ true
187
+ else
188
+ false
189
+ end
190
+ end
191
+ end
192
+
193
+ # Executes the block within mutex if current state is included in expected_states
194
+ #
195
+ # @return block value if executed, false otherwise
196
+ #
197
+ # @!visibility private
198
+ def if_state(*expected_states)
199
+ mutex.synchronize do
200
+ raise ArgumentError.new('no block given') unless block_given?
201
+
202
+ if expected_states.include? @state
203
+ yield
204
+ else
205
+ false
206
+ end
207
+ end
208
+ end
209
+
210
+ protected
211
+
212
+ # Am I in the current state?
213
+ #
214
+ # @param [Symbol] expected The state to check against
215
+ # @return [Boolean] true if in the expected state else false
216
+ #
217
+ # @!visibility private
218
+ def ns_check_state?(expected)
219
+ @state == expected
220
+ end
221
+
222
+ # @!visibility private
223
+ def ns_set_state(value)
224
+ @state = value
225
+ end
226
+ end
227
+ end
228
+ end
@@ -0,0 +1,85 @@
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: CopyOnWriteObserverSet and
25
+ # 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
+ # @return [Object] the added observer
53
+ def add_observer(*args, &block)
54
+ observers.add_observer(*args, &block)
55
+ end
56
+
57
+ # as #add_observer but it can be used for chaining
58
+ # @return [Observable] self
59
+ def with_observer(*args, &block)
60
+ add_observer(*args, &block)
61
+ self
62
+ end
63
+
64
+ # @return [Object] the deleted observer
65
+ def delete_observer(*args)
66
+ observers.delete_observer(*args)
67
+ end
68
+
69
+ # @return [Observable] self
70
+ def delete_observers
71
+ observers.delete_observers
72
+ self
73
+ end
74
+
75
+ # @return [Integer] the observers count
76
+ def count_observers
77
+ observers.count_observers
78
+ end
79
+
80
+ protected
81
+
82
+ attr_accessor :observers
83
+ end
84
+ end
85
+ end