concurrent-ruby 0.6.0.pre.2 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|