concurrent-ruby 0.6.0.pre.2 → 0.6.0
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.
- checksums.yaml +4 -4
- data/README.md +3 -8
- data/lib/concurrent.rb +2 -0
- data/lib/concurrent/actor/actor.rb +3 -3
- data/lib/concurrent/actor/postable.rb +1 -1
- data/lib/concurrent/actors.rb +0 -3
- data/lib/concurrent/actress.rb +75 -0
- data/lib/concurrent/actress/ad_hoc.rb +14 -0
- data/lib/concurrent/actress/context.rb +96 -0
- data/lib/concurrent/actress/core.rb +204 -0
- data/lib/concurrent/actress/core_delegations.rb +37 -0
- data/lib/concurrent/actress/doc.md +53 -0
- data/lib/concurrent/actress/envelope.rb +25 -0
- data/lib/concurrent/actress/errors.rb +14 -0
- data/lib/concurrent/actress/reference.rb +64 -0
- data/lib/concurrent/actress/type_check.rb +48 -0
- data/lib/concurrent/agent.rb +20 -11
- data/lib/concurrent/async.rb +54 -25
- data/lib/concurrent/atomic/atomic.rb +48 -0
- data/lib/concurrent/atomic/atomic_boolean.rb +13 -13
- data/lib/concurrent/atomic/atomic_fixnum.rb +9 -17
- data/lib/concurrent/atomic/copy_on_notify_observer_set.rb +7 -4
- data/lib/concurrent/atomic/copy_on_write_observer_set.rb +16 -14
- data/lib/concurrent/atomic/event.rb +11 -16
- data/lib/concurrent/atomics.rb +1 -0
- data/lib/concurrent/channel/channel.rb +4 -2
- data/lib/concurrent/collection/blocking_ring_buffer.rb +1 -1
- data/lib/concurrent/configuration.rb +59 -47
- data/lib/concurrent/delay.rb +28 -12
- data/lib/concurrent/dereferenceable.rb +6 -6
- data/lib/concurrent/errors.rb +30 -0
- data/lib/concurrent/executor/executor.rb +11 -4
- data/lib/concurrent/executor/immediate_executor.rb +1 -0
- data/lib/concurrent/executor/java_thread_pool_executor.rb +4 -0
- data/lib/concurrent/executor/one_by_one.rb +24 -12
- data/lib/concurrent/executor/per_thread_executor.rb +1 -0
- data/lib/concurrent/executor/ruby_single_thread_executor.rb +2 -1
- data/lib/concurrent/executor/ruby_thread_pool_executor.rb +7 -2
- data/lib/concurrent/executor/ruby_thread_pool_worker.rb +3 -0
- data/lib/concurrent/executor/timer_set.rb +1 -1
- data/lib/concurrent/future.rb +0 -2
- data/lib/concurrent/ivar.rb +31 -6
- data/lib/concurrent/logging.rb +17 -0
- data/lib/concurrent/mvar.rb +45 -0
- data/lib/concurrent/obligation.rb +61 -20
- data/lib/concurrent/observable.rb +7 -0
- data/lib/concurrent/promise.rb +1 -0
- data/lib/concurrent/runnable.rb +2 -2
- data/lib/concurrent/supervisor.rb +1 -2
- data/lib/concurrent/timer_task.rb +17 -13
- data/lib/concurrent/tvar.rb +113 -73
- data/lib/concurrent/utility/processor_count.rb +141 -116
- data/lib/concurrent/utility/timeout.rb +4 -5
- data/lib/concurrent/version.rb +1 -1
- data/spec/concurrent/actor/postable_shared.rb +1 -1
- data/spec/concurrent/actress_spec.rb +191 -0
- data/spec/concurrent/async_spec.rb +35 -3
- data/spec/concurrent/atomic/atomic_boolean_spec.rb +1 -1
- data/spec/concurrent/atomic/atomic_fixnum_spec.rb +1 -1
- data/spec/concurrent/atomic/atomic_spec.rb +133 -0
- data/spec/concurrent/atomic/count_down_latch_spec.rb +1 -1
- data/spec/concurrent/collection/priority_queue_spec.rb +1 -1
- data/spec/concurrent/configuration_spec.rb +5 -2
- data/spec/concurrent/delay_spec.rb +14 -0
- data/spec/concurrent/exchanger_spec.rb +4 -9
- data/spec/concurrent/executor/java_cached_thread_pool_spec.rb +1 -1
- data/spec/concurrent/executor/java_fixed_thread_pool_spec.rb +1 -1
- data/spec/concurrent/executor/java_single_thread_executor_spec.rb +1 -1
- data/spec/concurrent/executor/java_thread_pool_executor_spec.rb +1 -1
- data/spec/concurrent/executor/ruby_thread_pool_executor_spec.rb +4 -4
- data/spec/concurrent/ivar_spec.rb +2 -2
- data/spec/concurrent/obligation_spec.rb +113 -24
- data/spec/concurrent/observable_shared.rb +4 -0
- data/spec/concurrent/observable_spec.rb +8 -3
- data/spec/concurrent/runnable_spec.rb +2 -2
- data/spec/concurrent/scheduled_task_spec.rb +1 -0
- data/spec/concurrent/supervisor_spec.rb +26 -11
- data/spec/concurrent/timer_task_spec.rb +36 -35
- data/spec/concurrent/tvar_spec.rb +1 -1
- data/spec/concurrent/utility/timer_spec.rb +8 -8
- data/spec/spec_helper.rb +8 -18
- data/spec/support/example_group_extensions.rb +48 -0
- metadata +23 -16
- data/lib/concurrent/actor/actor_context.rb +0 -77
- data/lib/concurrent/actor/actor_ref.rb +0 -67
- data/lib/concurrent/actor/simple_actor_ref.rb +0 -94
- data/lib/concurrent_ruby_ext.bundle +0 -0
- data/spec/concurrent/actor/actor_context_spec.rb +0 -29
- data/spec/concurrent/actor/actor_ref_shared.rb +0 -263
- data/spec/concurrent/actor/simple_actor_ref_spec.rb +0 -135
- data/spec/support/functions.rb +0 -25
@@ -0,0 +1,37 @@
|
|
1
|
+
module Concurrent
|
2
|
+
module Actress
|
3
|
+
|
4
|
+
# Provides publicly expose-able methods from {Core}.
|
5
|
+
module CoreDelegations
|
6
|
+
def name
|
7
|
+
core.name
|
8
|
+
end
|
9
|
+
|
10
|
+
def path
|
11
|
+
core.path
|
12
|
+
end
|
13
|
+
|
14
|
+
def parent
|
15
|
+
core.parent
|
16
|
+
end
|
17
|
+
|
18
|
+
def terminated?
|
19
|
+
core.terminated?
|
20
|
+
end
|
21
|
+
|
22
|
+
def terminated
|
23
|
+
core.terminated
|
24
|
+
end
|
25
|
+
|
26
|
+
def reference
|
27
|
+
core.reference
|
28
|
+
end
|
29
|
+
|
30
|
+
def executor
|
31
|
+
core.executor
|
32
|
+
end
|
33
|
+
|
34
|
+
alias_method :ref, :reference
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# Light-weighted implement of Actors. Inspired by Akka and Erlang.
|
2
|
+
|
3
|
+
Actors are using a thread-pool by default which makes them very cheap to create and discard.
|
4
|
+
Thousands of actors can be created allowing to brake the program to small maintainable pieces
|
5
|
+
without breaking single responsibility principles.
|
6
|
+
|
7
|
+
## Quick example
|
8
|
+
|
9
|
+
class Counter
|
10
|
+
include Context
|
11
|
+
|
12
|
+
def initialize(initial_value)
|
13
|
+
@count = initial_value
|
14
|
+
end
|
15
|
+
|
16
|
+
def on_message(message)
|
17
|
+
case message
|
18
|
+
when Integer
|
19
|
+
@count += message
|
20
|
+
when :terminate
|
21
|
+
terminate!
|
22
|
+
else
|
23
|
+
raise 'unknown'
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# create new actor
|
29
|
+
counter = Counter.spawn(:test_counter, 5) # => a Reference
|
30
|
+
|
31
|
+
# send messages
|
32
|
+
counter.tell(1) # => counter
|
33
|
+
counter << 1 # => counter
|
34
|
+
|
35
|
+
# send messages getting an IVar back for synchronization
|
36
|
+
counter.ask(0) # => an ivar
|
37
|
+
counter.ask(0).value # => 7
|
38
|
+
|
39
|
+
# terminate the actor
|
40
|
+
counter.ask(:terminate).wait
|
41
|
+
counter.terminated? # => true
|
42
|
+
counter.ask(5).wait.rejected? # => true
|
43
|
+
|
44
|
+
# failure on message processing will terminate the actor
|
45
|
+
counter = Counter.spawn(:test_counter, 0)
|
46
|
+
counter.ask('boom').wait.rejected? # => true
|
47
|
+
counter.terminated? # => true
|
48
|
+
|
49
|
+
|
50
|
+
|
51
|
+
|
52
|
+
|
53
|
+
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Concurrent
|
2
|
+
module Actress
|
3
|
+
Envelope = Struct.new :message, :ivar, :sender do
|
4
|
+
include TypeCheck
|
5
|
+
|
6
|
+
def initialize(message, ivar, sender)
|
7
|
+
super message,
|
8
|
+
(Type! ivar, IVar, NilClass),
|
9
|
+
(Type! sender, Reference, Thread)
|
10
|
+
end
|
11
|
+
|
12
|
+
def sender_path
|
13
|
+
if sender.is_a? Reference
|
14
|
+
sender.path
|
15
|
+
else
|
16
|
+
sender.to_s
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def reject!(error)
|
21
|
+
ivar.fail error unless ivar.nil?
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module Concurrent
|
2
|
+
module Actress
|
3
|
+
|
4
|
+
# Reference is public interface of Actor instances. It is used for sending messages and can
|
5
|
+
# be freely passed around the program. It also provides some basic information about the actor
|
6
|
+
# see {CoreDelegations}
|
7
|
+
class Reference
|
8
|
+
include TypeCheck
|
9
|
+
include CoreDelegations
|
10
|
+
|
11
|
+
attr_reader :core
|
12
|
+
private :core
|
13
|
+
|
14
|
+
# @!visibility private
|
15
|
+
def initialize(core)
|
16
|
+
@core = Type! core, Core
|
17
|
+
end
|
18
|
+
|
19
|
+
# tells message to the actor
|
20
|
+
# @param [Object] message
|
21
|
+
# @return [Reference] self
|
22
|
+
def tell(message)
|
23
|
+
message message, nil
|
24
|
+
end
|
25
|
+
|
26
|
+
alias_method :<<, :tell
|
27
|
+
|
28
|
+
# tells message to the actor
|
29
|
+
# @param [Object] message
|
30
|
+
# @param [Ivar] ivar to be fulfilled be message's processing result
|
31
|
+
# @return [IVar] supplied ivar
|
32
|
+
def ask(message, ivar = IVar.new)
|
33
|
+
message message, ivar
|
34
|
+
end
|
35
|
+
|
36
|
+
# @note can lead to deadlocks, use only in tests or when you are sure it won't deadlock
|
37
|
+
# tells message to the actor
|
38
|
+
# @param [Object] message
|
39
|
+
# @param [Ivar] ivar to be fulfilled be message's processing result
|
40
|
+
# @return [Object] message's processing result
|
41
|
+
# @raise [Exception] ivar.reason if ivar is #rejected?
|
42
|
+
def ask!(message, ivar = IVar.new)
|
43
|
+
ask(message, ivar).value!
|
44
|
+
end
|
45
|
+
|
46
|
+
# behaves as #tell when no ivar and as #ask when ivar
|
47
|
+
def message(message, ivar = nil)
|
48
|
+
core.on_envelope Envelope.new(message, ivar, Actress.current || Thread.current)
|
49
|
+
return ivar || self
|
50
|
+
end
|
51
|
+
|
52
|
+
def to_s
|
53
|
+
"#<#{self.class} #{path}>"
|
54
|
+
end
|
55
|
+
|
56
|
+
alias_method :inspect, :to_s
|
57
|
+
|
58
|
+
def ==(other)
|
59
|
+
Type? other, self.class and other.send(:core) == core
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Concurrent
|
2
|
+
module Actress
|
3
|
+
|
4
|
+
# taken from Algebrick
|
5
|
+
# supplies type-checking helpers whenever included
|
6
|
+
module TypeCheck
|
7
|
+
|
8
|
+
def Type?(value, *types)
|
9
|
+
types.any? { |t| value.is_a? t }
|
10
|
+
end
|
11
|
+
|
12
|
+
def Type!(value, *types)
|
13
|
+
Type?(value, *types) or
|
14
|
+
TypeCheck.error(value, 'is not', types)
|
15
|
+
value
|
16
|
+
end
|
17
|
+
|
18
|
+
def Match?(value, *types)
|
19
|
+
types.any? { |t| t === value }
|
20
|
+
end
|
21
|
+
|
22
|
+
def Match!(value, *types)
|
23
|
+
Match?(value, *types) or
|
24
|
+
TypeCheck.error(value, 'is not matching', types)
|
25
|
+
value
|
26
|
+
end
|
27
|
+
|
28
|
+
def Child?(value, *types)
|
29
|
+
Type?(value, Class) &&
|
30
|
+
types.any? { |t| value <= t }
|
31
|
+
end
|
32
|
+
|
33
|
+
def Child!(value, *types)
|
34
|
+
Child?(value, *types) or
|
35
|
+
TypeCheck.error(value, 'is not child', types)
|
36
|
+
value
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def self.error(value, message, types)
|
42
|
+
raise TypeError,
|
43
|
+
"Value (#{value.class}) '#{value}' #{message} any of: #{types.join('; ')}."
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
data/lib/concurrent/agent.rb
CHANGED
@@ -4,6 +4,7 @@ require 'concurrent/dereferenceable'
|
|
4
4
|
require 'concurrent/observable'
|
5
5
|
require 'concurrent/options_parser'
|
6
6
|
require 'concurrent/utility/timeout'
|
7
|
+
require 'concurrent/logging'
|
7
8
|
|
8
9
|
module Concurrent
|
9
10
|
|
@@ -35,6 +36,7 @@ module Concurrent
|
|
35
36
|
class Agent
|
36
37
|
include Dereferenceable
|
37
38
|
include Concurrent::Observable
|
39
|
+
include Logging
|
38
40
|
|
39
41
|
# The default timeout value (in seconds); used when no timeout option
|
40
42
|
# is given at initialization
|
@@ -113,10 +115,14 @@ module Concurrent
|
|
113
115
|
# @yieldparam [Object] value the result of the last update operation
|
114
116
|
# @yieldreturn [Boolean] true if the value is valid else false
|
115
117
|
def validate(&block)
|
118
|
+
|
116
119
|
unless block.nil?
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
+
begin
|
121
|
+
mutex.lock
|
122
|
+
@validator = block
|
123
|
+
ensure
|
124
|
+
mutex.unlock
|
125
|
+
end
|
120
126
|
end
|
121
127
|
self
|
122
128
|
end
|
@@ -188,7 +194,8 @@ module Concurrent
|
|
188
194
|
end
|
189
195
|
rescuer.block.call(ex) if rescuer
|
190
196
|
rescue Exception => ex
|
191
|
-
#
|
197
|
+
# suppress
|
198
|
+
log DEBUG, ex
|
192
199
|
end
|
193
200
|
|
194
201
|
# @!visibility private
|
@@ -196,7 +203,6 @@ module Concurrent
|
|
196
203
|
validator, value = mutex.synchronize { [@validator, @value] }
|
197
204
|
|
198
205
|
begin
|
199
|
-
# FIXME creates second thread
|
200
206
|
result, valid = Concurrent::timeout(@timeout) do
|
201
207
|
result = handler.call(value)
|
202
208
|
[result, validator.call(result)]
|
@@ -205,12 +211,15 @@ module Concurrent
|
|
205
211
|
exception = ex
|
206
212
|
end
|
207
213
|
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
+
begin
|
215
|
+
mutex.lock
|
216
|
+
should_notify = if !exception && valid
|
217
|
+
@value = result
|
218
|
+
true
|
219
|
+
end
|
220
|
+
ensure
|
221
|
+
mutex.unlock
|
222
|
+
end
|
214
223
|
|
215
224
|
if should_notify
|
216
225
|
time = Time.now
|
data/lib/concurrent/async.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
require 'thread'
|
2
2
|
require 'concurrent/configuration'
|
3
|
+
require 'concurrent/delay'
|
4
|
+
require 'concurrent/errors'
|
3
5
|
require 'concurrent/ivar'
|
4
6
|
require 'concurrent/future'
|
5
7
|
require 'concurrent/executor/thread_pool_executor'
|
@@ -36,6 +38,10 @@ module Concurrent
|
|
36
38
|
# class Echo
|
37
39
|
# include Concurrent::Async
|
38
40
|
#
|
41
|
+
# def initialize
|
42
|
+
# init_mutex # initialize the internal synchronization objects
|
43
|
+
# end
|
44
|
+
#
|
39
45
|
# def echo(msg)
|
40
46
|
# sleep(rand)
|
41
47
|
# print "#{msg}\n"
|
@@ -52,6 +58,7 @@ module Concurrent
|
|
52
58
|
# @example Monkey-patching an existing object
|
53
59
|
# numbers = 1_000_000.times.collect{ rand }
|
54
60
|
# numbers.extend(Concurrent::Async)
|
61
|
+
# numbers.init_mutex # initialize the internal synchronization objects
|
55
62
|
#
|
56
63
|
# future = numbers.async.max
|
57
64
|
# future.state #=> :pending
|
@@ -61,6 +68,16 @@ module Concurrent
|
|
61
68
|
# future.state #=> :fulfilled
|
62
69
|
# future.value #=> 0.999999138918843
|
63
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
|
+
#
|
64
81
|
# @note Thread safe guarantees can only be made when asynchronous method calls
|
65
82
|
# are not mixed with synchronous method calls. Use only synchronous calls
|
66
83
|
# when the object is used exclusively on a single thread. Use only
|
@@ -142,7 +159,7 @@ module Concurrent
|
|
142
159
|
ivar = Concurrent::IVar.new
|
143
160
|
value, reason = nil, nil
|
144
161
|
begin
|
145
|
-
mutex.synchronize do
|
162
|
+
@mutex.synchronize do
|
146
163
|
value = @delegate.send(method, *args, &block)
|
147
164
|
end
|
148
165
|
rescue => reason
|
@@ -154,11 +171,6 @@ module Concurrent
|
|
154
171
|
|
155
172
|
self.send(method, *args)
|
156
173
|
end
|
157
|
-
|
158
|
-
# The lock used when delegating methods to the wrapped object.
|
159
|
-
#
|
160
|
-
# @!visibility private
|
161
|
-
attr_reader :mutex # :nodoc:
|
162
174
|
end
|
163
175
|
|
164
176
|
# Delegates asynchronous, thread-safe method calls to the wrapped object.
|
@@ -194,8 +206,8 @@ module Concurrent
|
|
194
206
|
|
195
207
|
self.define_singleton_method(method) do |*args|
|
196
208
|
Async::validate_argc(@delegate, method, *args)
|
197
|
-
Concurrent::Future.execute(executor: @executor) do
|
198
|
-
mutex.synchronize do
|
209
|
+
Concurrent::Future.execute(executor: @executor.value) do
|
210
|
+
@mutex.synchronize do
|
199
211
|
@delegate.send(method, *args, &block)
|
200
212
|
end
|
201
213
|
end
|
@@ -203,13 +215,6 @@ module Concurrent
|
|
203
215
|
|
204
216
|
self.send(method, *args)
|
205
217
|
end
|
206
|
-
|
207
|
-
private
|
208
|
-
|
209
|
-
# The lock used when delegating methods to the wrapped object.
|
210
|
-
#
|
211
|
-
# @!visibility private
|
212
|
-
attr_reader :mutex # :nodoc:
|
213
218
|
end
|
214
219
|
|
215
220
|
# Causes the chained method call to be performed asynchronously on the
|
@@ -230,17 +235,19 @@ module Concurrent
|
|
230
235
|
# either `async` or `await`. The mutable nature of Ruby references
|
231
236
|
# (and object orientation in general) prevent any other thread safety
|
232
237
|
# guarantees. Do NOT mix non-protected method calls with protected
|
233
|
-
# method call. Use
|
238
|
+
# method call. Use *only* protected method calls when sharing the object
|
234
239
|
# between threads.
|
235
240
|
#
|
236
241
|
# @return [Concurrent::Future] the pending result of the asynchronous operation
|
237
242
|
#
|
243
|
+
# @raise [Concurrent::InitializationError] `#init_mutex` has not been called
|
238
244
|
# @raise [NameError] the object does not respond to `method` method
|
239
245
|
# @raise [ArgumentError] the given `args` do not match the arity of `method`
|
240
246
|
#
|
241
247
|
# @see Concurrent::Future
|
242
248
|
def async
|
243
|
-
|
249
|
+
raise InitializationError.new('#init_mutex was never called') unless @__async__mutex__
|
250
|
+
@__async_delegator__.value
|
244
251
|
end
|
245
252
|
alias_method :future, :async
|
246
253
|
|
@@ -262,29 +269,51 @@ module Concurrent
|
|
262
269
|
# either `async` or `await`. The mutable nature of Ruby references
|
263
270
|
# (and object orientation in general) prevent any other thread safety
|
264
271
|
# guarantees. Do NOT mix non-protected method calls with protected
|
265
|
-
# method call. Use
|
272
|
+
# method call. Use *only* protected method calls when sharing the object
|
266
273
|
# between threads.
|
267
274
|
#
|
268
275
|
# @return [Concurrent::IVar] the completed result of the synchronous operation
|
269
276
|
#
|
277
|
+
# @raise [Concurrent::InitializationError] `#init_mutex` has not been called
|
270
278
|
# @raise [NameError] the object does not respond to `method` method
|
271
279
|
# @raise [ArgumentError] the given `args` do not match the arity of `method`
|
272
280
|
#
|
273
281
|
# @see Concurrent::IVar
|
274
282
|
def await
|
275
|
-
|
283
|
+
raise InitializationError.new('#init_mutex was never called') unless @__async__mutex__
|
284
|
+
@__await_delegator__.value
|
276
285
|
end
|
277
286
|
alias_method :delay, :await
|
278
287
|
|
288
|
+
# Set a new executor
|
289
|
+
#
|
290
|
+
# @raise [Concurrent::InitializationError] `#init_mutex` has not been called
|
291
|
+
# @raise [ArgumentError] executor has already been set
|
279
292
|
def executor=(executor)
|
280
|
-
raise
|
281
|
-
@__async__executor__
|
293
|
+
raise InitializationError.new('#init_mutex was never called') unless @__async__mutex__
|
294
|
+
@__async__executor__.reconfigure { executor } or
|
295
|
+
raise ArgumentError.new('executor has already been set')
|
282
296
|
end
|
283
297
|
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
298
|
+
# Initialize the internal mutex and other synchronization objects. This method
|
299
|
+
# *must* be called from the constructor of the including class or explicitly
|
300
|
+
# by the caller prior to calling any other methods. If `init_mutex` is *not*
|
301
|
+
# called explicitly the async/await/executor methods will raize a
|
302
|
+
# `Concurrent::InitializationError`. This is the only way thread-safe
|
303
|
+
# initialization can be guaranteed.
|
304
|
+
#
|
305
|
+
# @note This method *must* be called from the constructor of the including
|
306
|
+
# class or explicitly by the caller prior to calling any other methods.
|
307
|
+
# This is the only way thread-safe initialization can be guaranteed.
|
308
|
+
#
|
309
|
+
# @raise [Concurrent::InitializationError] when called more than once
|
310
|
+
def init_mutex
|
311
|
+
raise InitializationError.new('#init_mutex was already called') if @__async__mutex__
|
312
|
+
(@__async__mutex__ = Mutex.new).lock
|
313
|
+
@__async__executor__ = Delay.new{ Concurrent.configuration.global_operation_pool }
|
314
|
+
@__await_delegator__ = Delay.new{ AwaitDelegator.new(self, @__async__mutex__) }
|
315
|
+
@__async_delegator__ = Delay.new{ AsyncDelegator.new(self, @__async__executor__, @__async__mutex__) }
|
316
|
+
@__async__mutex__.unlock
|
288
317
|
end
|
289
318
|
end
|
290
319
|
end
|