concurrent-ruby 0.7.0-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (112) hide show
  1. data/LICENSE.txt +21 -0
  2. data/README.md +217 -0
  3. data/lib/concurrent.rb +45 -0
  4. data/lib/concurrent/actor.rb +104 -0
  5. data/lib/concurrent/actor/behaviour.rb +70 -0
  6. data/lib/concurrent/actor/behaviour/abstract.rb +48 -0
  7. data/lib/concurrent/actor/behaviour/awaits.rb +21 -0
  8. data/lib/concurrent/actor/behaviour/buffer.rb +54 -0
  9. data/lib/concurrent/actor/behaviour/errors_on_unknown_message.rb +12 -0
  10. data/lib/concurrent/actor/behaviour/executes_context.rb +18 -0
  11. data/lib/concurrent/actor/behaviour/linking.rb +42 -0
  12. data/lib/concurrent/actor/behaviour/pausing.rb +77 -0
  13. data/lib/concurrent/actor/behaviour/removes_child.rb +16 -0
  14. data/lib/concurrent/actor/behaviour/sets_results.rb +36 -0
  15. data/lib/concurrent/actor/behaviour/supervised.rb +58 -0
  16. data/lib/concurrent/actor/behaviour/supervising.rb +34 -0
  17. data/lib/concurrent/actor/behaviour/terminates_children.rb +13 -0
  18. data/lib/concurrent/actor/behaviour/termination.rb +54 -0
  19. data/lib/concurrent/actor/context.rb +153 -0
  20. data/lib/concurrent/actor/core.rb +213 -0
  21. data/lib/concurrent/actor/default_dead_letter_handler.rb +9 -0
  22. data/lib/concurrent/actor/envelope.rb +41 -0
  23. data/lib/concurrent/actor/errors.rb +27 -0
  24. data/lib/concurrent/actor/internal_delegations.rb +49 -0
  25. data/lib/concurrent/actor/public_delegations.rb +40 -0
  26. data/lib/concurrent/actor/reference.rb +81 -0
  27. data/lib/concurrent/actor/root.rb +37 -0
  28. data/lib/concurrent/actor/type_check.rb +48 -0
  29. data/lib/concurrent/actor/utils.rb +10 -0
  30. data/lib/concurrent/actor/utils/ad_hoc.rb +21 -0
  31. data/lib/concurrent/actor/utils/balancer.rb +40 -0
  32. data/lib/concurrent/actor/utils/broadcast.rb +52 -0
  33. data/lib/concurrent/actor/utils/pool.rb +59 -0
  34. data/lib/concurrent/actress.rb +3 -0
  35. data/lib/concurrent/agent.rb +230 -0
  36. data/lib/concurrent/async.rb +284 -0
  37. data/lib/concurrent/atomic.rb +91 -0
  38. data/lib/concurrent/atomic/atomic_boolean.rb +202 -0
  39. data/lib/concurrent/atomic/atomic_fixnum.rb +203 -0
  40. data/lib/concurrent/atomic/condition.rb +67 -0
  41. data/lib/concurrent/atomic/copy_on_notify_observer_set.rb +118 -0
  42. data/lib/concurrent/atomic/copy_on_write_observer_set.rb +117 -0
  43. data/lib/concurrent/atomic/count_down_latch.rb +116 -0
  44. data/lib/concurrent/atomic/cyclic_barrier.rb +106 -0
  45. data/lib/concurrent/atomic/event.rb +98 -0
  46. data/lib/concurrent/atomic/synchronization.rb +51 -0
  47. data/lib/concurrent/atomic/thread_local_var.rb +82 -0
  48. data/lib/concurrent/atomic_reference/concurrent_update_error.rb +8 -0
  49. data/lib/concurrent/atomic_reference/direct_update.rb +50 -0
  50. data/lib/concurrent/atomic_reference/jruby.rb +14 -0
  51. data/lib/concurrent/atomic_reference/mutex_atomic.rb +77 -0
  52. data/lib/concurrent/atomic_reference/numeric_cas_wrapper.rb +25 -0
  53. data/lib/concurrent/atomic_reference/rbx.rb +19 -0
  54. data/lib/concurrent/atomic_reference/ruby.rb +37 -0
  55. data/lib/concurrent/atomics.rb +11 -0
  56. data/lib/concurrent/channel/buffered_channel.rb +85 -0
  57. data/lib/concurrent/channel/channel.rb +41 -0
  58. data/lib/concurrent/channel/unbuffered_channel.rb +35 -0
  59. data/lib/concurrent/channel/waitable_list.rb +40 -0
  60. data/lib/concurrent/channels.rb +5 -0
  61. data/lib/concurrent/collection/blocking_ring_buffer.rb +71 -0
  62. data/lib/concurrent/collection/priority_queue.rb +305 -0
  63. data/lib/concurrent/collection/ring_buffer.rb +59 -0
  64. data/lib/concurrent/collections.rb +3 -0
  65. data/lib/concurrent/configuration.rb +161 -0
  66. data/lib/concurrent/dataflow.rb +108 -0
  67. data/lib/concurrent/delay.rb +104 -0
  68. data/lib/concurrent/dereferenceable.rb +101 -0
  69. data/lib/concurrent/errors.rb +30 -0
  70. data/lib/concurrent/exchanger.rb +34 -0
  71. data/lib/concurrent/executor/cached_thread_pool.rb +44 -0
  72. data/lib/concurrent/executor/executor.rb +282 -0
  73. data/lib/concurrent/executor/fixed_thread_pool.rb +33 -0
  74. data/lib/concurrent/executor/immediate_executor.rb +65 -0
  75. data/lib/concurrent/executor/java_cached_thread_pool.rb +31 -0
  76. data/lib/concurrent/executor/java_fixed_thread_pool.rb +41 -0
  77. data/lib/concurrent/executor/java_single_thread_executor.rb +22 -0
  78. data/lib/concurrent/executor/java_thread_pool_executor.rb +180 -0
  79. data/lib/concurrent/executor/per_thread_executor.rb +100 -0
  80. data/lib/concurrent/executor/ruby_cached_thread_pool.rb +29 -0
  81. data/lib/concurrent/executor/ruby_fixed_thread_pool.rb +32 -0
  82. data/lib/concurrent/executor/ruby_single_thread_executor.rb +74 -0
  83. data/lib/concurrent/executor/ruby_thread_pool_executor.rb +288 -0
  84. data/lib/concurrent/executor/ruby_thread_pool_worker.rb +72 -0
  85. data/lib/concurrent/executor/safe_task_executor.rb +35 -0
  86. data/lib/concurrent/executor/serialized_execution.rb +126 -0
  87. data/lib/concurrent/executor/single_thread_executor.rb +35 -0
  88. data/lib/concurrent/executor/thread_pool_executor.rb +68 -0
  89. data/lib/concurrent/executor/timer_set.rb +143 -0
  90. data/lib/concurrent/executors.rb +9 -0
  91. data/lib/concurrent/future.rb +125 -0
  92. data/lib/concurrent/ivar.rb +111 -0
  93. data/lib/concurrent/lazy_register.rb +58 -0
  94. data/lib/concurrent/logging.rb +17 -0
  95. data/lib/concurrent/mvar.rb +200 -0
  96. data/lib/concurrent/obligation.rb +171 -0
  97. data/lib/concurrent/observable.rb +40 -0
  98. data/lib/concurrent/options_parser.rb +48 -0
  99. data/lib/concurrent/promise.rb +170 -0
  100. data/lib/concurrent/scheduled_task.rb +79 -0
  101. data/lib/concurrent/timer_task.rb +341 -0
  102. data/lib/concurrent/tvar.rb +248 -0
  103. data/lib/concurrent/utilities.rb +3 -0
  104. data/lib/concurrent/utility/processor_count.rb +152 -0
  105. data/lib/concurrent/utility/timeout.rb +35 -0
  106. data/lib/concurrent/utility/timer.rb +21 -0
  107. data/lib/concurrent/version.rb +3 -0
  108. data/lib/concurrent_ruby.rb +1 -0
  109. data/lib/concurrent_ruby_ext.jar +0 -0
  110. data/lib/concurrent_ruby_ext.so +0 -0
  111. data/lib/extension_helper.rb +28 -0
  112. metadata +163 -0
@@ -0,0 +1,3 @@
1
+ require 'concurrent/actor'
2
+
3
+ Concurrent::Actress = Concurrent::Actor
@@ -0,0 +1,230 @@
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
+ # An agent is a single atomic value that represents an identity. The current value
12
+ # of the agent can be requested at any time (`#deref`). Each agent has a work queue and operates on
13
+ # the global thread pool. Consumers can `#post` code blocks to the agent. The code block (function)
14
+ # will receive the current value of the agent as its sole parameter. The return value of the block
15
+ # will become the new value of the agent. Agents support two error handling modes: fail and continue.
16
+ # A good example of an agent is a shared incrementing counter, such as the score in a video game.
17
+ #
18
+ # @example Basic usage
19
+ # score = Concurrent::Agent.new(10)
20
+ # score.value #=> 10
21
+ #
22
+ # score << proc{|current| current + 100 }
23
+ # sleep(0.1)
24
+ # score.value #=> 110
25
+ #
26
+ # score << proc{|current| current * 2 }
27
+ # sleep(0.1)
28
+ # score.value #=> 220
29
+ #
30
+ # score << proc{|current| current - 50 }
31
+ # sleep(0.1)
32
+ # score.value #=> 170
33
+ #
34
+ # @!attribute [r] timeout
35
+ # @return [Fixnum] the maximum number of seconds before an update is cancelled
36
+ class Agent
37
+ include Dereferenceable
38
+ include Concurrent::Observable
39
+ include Logging
40
+
41
+ attr_reader :timeout, :task_executor, :operation_executor
42
+
43
+ # Initialize a new Agent with the given initial value and provided options.
44
+ #
45
+ # @param [Object] initial the initial value
46
+ # @param [Hash] opts the options used to define the behavior at update and deref
47
+ #
48
+ # @option opts [Boolean] :operation (false) when `true` will execute the future on the global
49
+ # operation pool (for long-running operations), when `false` will execute the future on the
50
+ # global task pool (for short-running tasks)
51
+ # @option opts [object] :executor when provided will run all operations on
52
+ # this executor rather than the global thread pool (overrides :operation)
53
+ #
54
+ # @option opts [String] :dup_on_deref (false) call `#dup` before returning the data
55
+ # @option opts [String] :freeze_on_deref (false) call `#freeze` before returning the data
56
+ # @option opts [String] :copy_on_deref (nil) call the given `Proc` passing the internal value and
57
+ # returning the value returned from the proc
58
+ def initialize(initial, opts = {})
59
+ @value = initial
60
+ @rescuers = []
61
+ @validator = Proc.new { |result| true }
62
+ self.observers = CopyOnWriteObserverSet.new
63
+ @serialized_execution = SerializedExecution.new
64
+ @task_executor = OptionsParser.get_task_executor_from(opts)
65
+ @operation_executor = OptionsParser.get_operation_executor_from(opts)
66
+ init_mutex
67
+ set_deref_options(opts)
68
+ end
69
+
70
+ # Specifies a block operation to be performed when an update operation raises
71
+ # an exception. Rescue blocks will be checked in order they were added. The first
72
+ # block for which the raised exception "is-a" subclass of the given `clazz` will
73
+ # be called. If no `clazz` is given the block will match any caught exception.
74
+ # This behavior is intended to be identical to Ruby's `begin/rescue/end` behavior.
75
+ # Any number of rescue handlers can be added. If no rescue handlers are added then
76
+ # caught exceptions will be suppressed.
77
+ #
78
+ # @param [Exception] clazz the class of exception to catch
79
+ # @yield the block to be called when a matching exception is caught
80
+ # @yieldparam [StandardError] ex the caught exception
81
+ #
82
+ # @example
83
+ # score = Concurrent::Agent.new(0).
84
+ # rescue(NoMethodError){|ex| puts "Bam!" }.
85
+ # rescue(ArgumentError){|ex| puts "Pow!" }.
86
+ # rescue{|ex| puts "Boom!" }
87
+ #
88
+ # score << proc{|current| raise ArgumentError }
89
+ # sleep(0.1)
90
+ # #=> puts "Pow!"
91
+ def rescue(clazz = StandardError, &block)
92
+ unless block.nil?
93
+ mutex.synchronize do
94
+ @rescuers << Rescuer.new(clazz, block)
95
+ end
96
+ end
97
+ self
98
+ end
99
+ alias_method :catch, :rescue
100
+ alias_method :on_error, :rescue
101
+
102
+ # A block operation to be performed after every update to validate if the new
103
+ # value is valid. If the new value is not valid then the current value is not
104
+ # updated. If no validator is provided then all updates are considered valid.
105
+ #
106
+ # @yield the block to be called after every update operation to determine if
107
+ # the result is valid
108
+ # @yieldparam [Object] value the result of the last update operation
109
+ # @yieldreturn [Boolean] true if the value is valid else false
110
+ def validate(&block)
111
+
112
+ unless block.nil?
113
+ begin
114
+ mutex.lock
115
+ @validator = block
116
+ ensure
117
+ mutex.unlock
118
+ end
119
+ end
120
+ self
121
+ end
122
+ alias_method :validates, :validate
123
+ alias_method :validate_with, :validate
124
+ alias_method :validates_with, :validate
125
+
126
+ # Update the current value with the result of the given block operation,
127
+ # block should not do blocking calls, use #post_off for blocking calls
128
+ #
129
+ # @yield the operation to be performed with the current value in order to calculate
130
+ # the new value
131
+ # @yieldparam [Object] value the current value
132
+ # @yieldreturn [Object] the new value
133
+ # @return [true, nil] nil when no block is given
134
+ def post(&block)
135
+ post_on(@task_executor, &block)
136
+ end
137
+
138
+ # Update the current value with the result of the given block operation,
139
+ # block can do blocking calls
140
+ #
141
+ # @param [Fixnum, nil] timeout maximum number of seconds before an update is cancelled
142
+ #
143
+ # @yield the operation to be performed with the current value in order to calculate
144
+ # the new value
145
+ # @yieldparam [Object] value the current value
146
+ # @yieldreturn [Object] the new value
147
+ # @return [true, nil] nil when no block is given
148
+ def post_off(timeout = nil, &block)
149
+ block = if timeout
150
+ lambda { |value| Concurrent::timeout(timeout) { block.call(value) } }
151
+ else
152
+ block
153
+ end
154
+ post_on(@operation_executor, &block)
155
+ end
156
+
157
+ # Update the current value with the result of the given block operation,
158
+ # block should not do blocking calls, use #post_off for blocking calls
159
+ #
160
+ # @yield the operation to be performed with the current value in order to calculate
161
+ # the new value
162
+ # @yieldparam [Object] value the current value
163
+ # @yieldreturn [Object] the new value
164
+ def <<(block)
165
+ post(&block)
166
+ self
167
+ end
168
+
169
+ # Waits/blocks until all the updates sent before this call are done.
170
+ #
171
+ # @param [Numeric] timeout the maximum time in second to wait.
172
+ # @return [Boolean] false on timeout, true otherwise
173
+ def await(timeout = nil)
174
+ done = Event.new
175
+ post { |val| done.set; val }
176
+ done.wait timeout
177
+ end
178
+
179
+ private
180
+
181
+ def post_on(executor, &block)
182
+ return nil if block.nil?
183
+ @serialized_execution.post(executor) { work(&block) }
184
+ true
185
+ end
186
+
187
+ # @!visibility private
188
+ Rescuer = Struct.new(:clazz, :block) # :nodoc:
189
+
190
+ # @!visibility private
191
+ def try_rescue(ex) # :nodoc:
192
+ rescuer = mutex.synchronize do
193
+ @rescuers.find { |r| ex.is_a?(r.clazz) }
194
+ end
195
+ rescuer.block.call(ex) if rescuer
196
+ rescue Exception => ex
197
+ # suppress
198
+ log DEBUG, ex
199
+ end
200
+
201
+ # @!visibility private
202
+ def work(&handler) # :nodoc:
203
+ validator, value = mutex.synchronize { [@validator, @value] }
204
+
205
+ begin
206
+ result = handler.call(value)
207
+ valid = validator.call(result)
208
+ rescue Exception => ex
209
+ exception = ex
210
+ end
211
+
212
+ begin
213
+ mutex.lock
214
+ should_notify = if !exception && valid
215
+ @value = result
216
+ true
217
+ end
218
+ ensure
219
+ mutex.unlock
220
+ end
221
+
222
+ if should_notify
223
+ time = Time.now
224
+ observers.notify_observers { [time, self.value] }
225
+ end
226
+
227
+ try_rescue(exception)
228
+ end
229
+ end
230
+ end
@@ -0,0 +1,284 @@
1
+ require 'thread'
2
+ require 'concurrent/configuration'
3
+ require 'concurrent/delay'
4
+ require 'concurrent/errors'
5
+ require 'concurrent/ivar'
6
+ require 'concurrent/executor/immediate_executor'
7
+ require 'concurrent/executor/serialized_execution'
8
+
9
+ module Concurrent
10
+
11
+ # A mixin module that provides simple asynchronous behavior to any standard
12
+ # class/object or object.
13
+ #
14
+ # Scenario:
15
+ # As a stateful, plain old Ruby class/object
16
+ # I want safe, asynchronous behavior
17
+ # So my long-running methods don't block the main thread
18
+ #
19
+ # Stateful, mutable objects must be managed carefully when used asynchronously.
20
+ # But Ruby is an object-oriented language so designing with objects and classes
21
+ # plays to Ruby's strengths and is often more natural to many Ruby programmers.
22
+ # The `Async` module is a way to mix simple yet powerful asynchronous capabilities
23
+ # into any plain old Ruby object or class. These capabilities provide a reasonable
24
+ # level of thread safe guarantees when used correctly.
25
+ #
26
+ # When this module is mixed into a class or object it provides to new methods:
27
+ # `async` and `await`. These methods are thread safe with respect to the enclosing
28
+ # object. The former method allows methods to be called asynchronously by posting
29
+ # to the global thread pool. The latter allows a method to be called synchronously
30
+ # on the current thread but does so safely with respect to any pending asynchronous
31
+ # method calls. Both methods return an `Obligation` which can be inspected for
32
+ # the result of the method call. Calling a method with `async` will return a
33
+ # `:pending` `Obligation` whereas `await` will return a `:complete` `Obligation`.
34
+ #
35
+ # Very loosely based on the `async` and `await` keywords in C#.
36
+ #
37
+ # @example Defining an asynchronous class
38
+ # class Echo
39
+ # include Concurrent::Async
40
+ #
41
+ # def initialize
42
+ # init_mutex # initialize the internal synchronization objects
43
+ # end
44
+ #
45
+ # def echo(msg)
46
+ # sleep(rand)
47
+ # print "#{msg}\n"
48
+ # nil
49
+ # end
50
+ # end
51
+ #
52
+ # horn = Echo.new
53
+ # horn.echo('zero') # synchronous, not thread-safe
54
+ #
55
+ # horn.async.echo('one') # asynchronous, non-blocking, thread-safe
56
+ # horn.await.echo('two') # synchronous, blocking, thread-safe
57
+ #
58
+ # @example Monkey-patching an existing object
59
+ # numbers = 1_000_000.times.collect{ rand }
60
+ # numbers.extend(Concurrent::Async)
61
+ # numbers.init_mutex # initialize the internal synchronization objects
62
+ #
63
+ # future = numbers.async.max
64
+ # future.state #=> :pending
65
+ #
66
+ # sleep(2)
67
+ #
68
+ # future.state #=> :fulfilled
69
+ # future.value #=> 0.999999138918843
70
+ #
71
+ # @note This module depends on several internal synchronization objects that
72
+ # must be initialized prior to calling any of the async/await/executor methods.
73
+ # The best practice is to call `init_mutex` from within the constructor
74
+ # of the including class. A less ideal but acceptable practice is for the
75
+ # thread creating the asynchronous object to explicitly call the `init_mutex`
76
+ # method prior to calling any of the async/await/executor methods. If
77
+ # `init_mutex` is *not* called explicitly the async/await/executor methods
78
+ # will raize a `Concurrent::InitializationError`. This is the only way
79
+ # thread-safe initialization can be guaranteed.
80
+ #
81
+ # @note Thread safe guarantees can only be made when asynchronous method calls
82
+ # are not mixed with synchronous method calls. Use only synchronous calls
83
+ # when the object is used exclusively on a single thread. Use only
84
+ # `async` and `await` when the object is shared between threads. Once you
85
+ # call a method using `async`, you should no longer call any methods
86
+ # directly on the object. Use `async` and `await` exclusively from then on.
87
+ # With careful programming it is possible to switch back and forth but it's
88
+ # also very easy to create race conditions and break your application.
89
+ # Basically, it's "async all the way down."
90
+ #
91
+ # @since 0.6.0
92
+ #
93
+ # @see Concurrent::Obligation
94
+ module Async
95
+
96
+ # Check for the presence of a method on an object and determine if a given
97
+ # set of arguments matches the required arity.
98
+ #
99
+ # @param [Object] obj the object to check against
100
+ # @param [Symbol] method the method to check the object for
101
+ # @param [Array] args zero or more arguments for the arity check
102
+ #
103
+ # @raise [NameError] the object does not respond to `method` method
104
+ # @raise [ArgumentError] the given `args` do not match the arity of `method`
105
+ #
106
+ # @note This check is imperfect because of the way Ruby reports the arity of
107
+ # methods with a variable number of arguments. It is possible to determine
108
+ # if too few arguments are given but impossible to determine if too many
109
+ # arguments are given. This check may also fail to recognize dynamic behavior
110
+ # of the object, such as methods simulated with `method_missing`.
111
+ #
112
+ # @see http://www.ruby-doc.org/core-2.1.1/Method.html#method-i-arity Method#arity
113
+ # @see http://ruby-doc.org/core-2.1.0/Object.html#method-i-respond_to-3F Object#respond_to?
114
+ # @see http://www.ruby-doc.org/core-2.1.0/BasicObject.html#method-i-method_missing BasicObject#method_missing
115
+ def validate_argc(obj, method, *args)
116
+ argc = args.length
117
+ arity = obj.method(method).arity
118
+
119
+ if arity >= 0 && argc != arity
120
+ raise ArgumentError.new("wrong number of arguments (#{argc} for #{arity})")
121
+ elsif arity < 0 && (arity = (arity + 1).abs) > argc
122
+ raise ArgumentError.new("wrong number of arguments (#{argc} for #{arity}..*)")
123
+ end
124
+ end
125
+ module_function :validate_argc
126
+
127
+ # Delegates asynchronous, thread-safe method calls to the wrapped object.
128
+ #
129
+ # @!visibility private
130
+ class AsyncDelegator # :nodoc:
131
+
132
+ # Create a new delegator object wrapping the given delegate,
133
+ # protecting it with the given serializer, and executing it on the
134
+ # given executor. Block if necessary.
135
+ #
136
+ # @param [Object] delegate the object to wrap and delegate method calls to
137
+ # @param [Concurrent::Delay] executor a `Delay` wrapping the executor on which to execute delegated method calls
138
+ # @param [Concurrent::SerializedExecution] serializer the serializer to use when delegating method calls
139
+ # @param [Boolean] blocking will block awaiting result when `true`
140
+ def initialize(delegate, executor, serializer, blocking = false)
141
+ @delegate = delegate
142
+ @executor = executor
143
+ @serializer = serializer
144
+ @blocking = blocking
145
+ end
146
+
147
+ # Delegates method calls to the wrapped object. For performance,
148
+ # dynamically defines the given method on the delegator so that
149
+ # all future calls to `method` will not be directed here.
150
+ #
151
+ # @param [Symbol] method the method being called
152
+ # @param [Array] args zero or more arguments to the method
153
+ #
154
+ # @return [IVar] the result of the method call
155
+ #
156
+ # @raise [NameError] the object does not respond to `method` method
157
+ # @raise [ArgumentError] the given `args` do not match the arity of `method`
158
+ def method_missing(method, *args, &block)
159
+ super unless @delegate.respond_to?(method)
160
+ Async::validate_argc(@delegate, method, *args)
161
+
162
+ self.define_singleton_method(method) do |*args|
163
+ Async::validate_argc(@delegate, method, *args)
164
+ ivar = Concurrent::IVar.new
165
+ value, reason = nil, nil
166
+ @serializer.post(@executor.value) do
167
+ begin
168
+ value = @delegate.send(method, *args, &block)
169
+ rescue => reason
170
+ # caught
171
+ ensure
172
+ ivar.complete(reason.nil?, value, reason)
173
+ end
174
+ end
175
+ ivar.value if @blocking
176
+ ivar
177
+ end
178
+
179
+ self.send(method, *args)
180
+ end
181
+ end
182
+
183
+ # Causes the chained method call to be performed asynchronously on the
184
+ # global thread pool. The method called by this method will return a
185
+ # future object in the `:pending` state and the method call will have
186
+ # been scheduled on the global thread pool. The final disposition of the
187
+ # method call can be obtained by inspecting the returned future.
188
+ #
189
+ # Before scheduling the method on the global thread pool a best-effort
190
+ # attempt will be made to validate that the method exists on the object
191
+ # and that the given arguments match the arity of the requested function.
192
+ # Due to the dynamic nature of Ruby and limitations of its reflection
193
+ # library, some edge cases will be missed. For more information see
194
+ # the documentation for the `validate_argc` method.
195
+ #
196
+ # @note The method call is guaranteed to be thread safe with respect to
197
+ # all other method calls against the same object that are called with
198
+ # either `async` or `await`. The mutable nature of Ruby references
199
+ # (and object orientation in general) prevent any other thread safety
200
+ # guarantees. Do NOT mix non-protected method calls with protected
201
+ # method call. Use *only* protected method calls when sharing the object
202
+ # between threads.
203
+ #
204
+ # @return [Concurrent::IVar] the pending result of the asynchronous operation
205
+ #
206
+ # @raise [Concurrent::InitializationError] `#init_mutex` has not been called
207
+ # @raise [NameError] the object does not respond to `method` method
208
+ # @raise [ArgumentError] the given `args` do not match the arity of `method`
209
+ #
210
+ # @see Concurrent::IVar
211
+ def async
212
+ raise InitializationError.new('#init_mutex was never called') unless @__async_initialized__
213
+ @__async_delegator__.value
214
+ end
215
+ alias_method :future, :async
216
+
217
+ # Causes the chained method call to be performed synchronously on the
218
+ # current thread. The method called by this method will return an
219
+ # `IVar` object in either the `:fulfilled` or `rejected` state and the
220
+ # method call will have completed. The final disposition of the
221
+ # method call can be obtained by inspecting the returned `IVar`.
222
+ #
223
+ # Before scheduling the method on the global thread pool a best-effort
224
+ # attempt will be made to validate that the method exists on the object
225
+ # and that the given arguments match the arity of the requested function.
226
+ # Due to the dynamic nature of Ruby and limitations of its reflection
227
+ # library, some edge cases will be missed. For more information see
228
+ # the documentation for the `validate_argc` method.
229
+ #
230
+ # @note The method call is guaranteed to be thread safe with respect to
231
+ # all other method calls against the same object that are called with
232
+ # either `async` or `await`. The mutable nature of Ruby references
233
+ # (and object orientation in general) prevent any other thread safety
234
+ # guarantees. Do NOT mix non-protected method calls with protected
235
+ # method call. Use *only* protected method calls when sharing the object
236
+ # between threads.
237
+ #
238
+ # @return [Concurrent::IVar] the completed result of the synchronous operation
239
+ #
240
+ # @raise [Concurrent::InitializationError] `#init_mutex` has not been called
241
+ # @raise [NameError] the object does not respond to `method` method
242
+ # @raise [ArgumentError] the given `args` do not match the arity of `method`
243
+ #
244
+ # @see Concurrent::IVar
245
+ def await
246
+ raise InitializationError.new('#init_mutex was never called') unless @__async_initialized__
247
+ @__await_delegator__.value
248
+ end
249
+ alias_method :delay, :await
250
+
251
+ # Set a new executor
252
+ #
253
+ # @raise [Concurrent::InitializationError] `#init_mutex` has not been called
254
+ # @raise [ArgumentError] executor has already been set
255
+ def executor=(executor)
256
+ raise InitializationError.new('#init_mutex was never called') unless @__async_initialized__
257
+ @__async_executor__.reconfigure { executor } or
258
+ raise ArgumentError.new('executor has already been set')
259
+ end
260
+
261
+ # Initialize the internal serializer and other synchronization objects. This method
262
+ # *must* be called from the constructor of the including class or explicitly
263
+ # by the caller prior to calling any other methods. If `init_mutex` is *not*
264
+ # called explicitly the async/await/executor methods will raize a
265
+ # `Concurrent::InitializationError`. This is the only way thread-safe
266
+ # initialization can be guaranteed.
267
+ #
268
+ # @note This method *must* be called from the constructor of the including
269
+ # class or explicitly by the caller prior to calling any other methods.
270
+ # This is the only way thread-safe initialization can be guaranteed.
271
+ #
272
+ # @raise [Concurrent::InitializationError] when called more than once
273
+ def init_mutex
274
+ raise InitializationError.new('#init_mutex was already called') if @__async_initialized__
275
+ @__async_initialized__ = true
276
+ serializer = Concurrent::SerializedExecution.new
277
+ @__async_executor__ = Delay.new{ Concurrent.configuration.global_operation_pool }
278
+ @__await_delegator__ = Delay.new{ AsyncDelegator.new(
279
+ self, Delay.new{ Concurrent::ImmediateExecutor.new }, serializer, true) }
280
+ @__async_delegator__ = Delay.new{ AsyncDelegator.new(
281
+ self, @__async_executor__, serializer, false) }
282
+ end
283
+ end
284
+ end