concurrent-ruby 1.0.0.pre1 → 1.0.0.pre2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (76) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +14 -1
  3. data/README.md +16 -18
  4. data/lib/concurrent.rb +3 -3
  5. data/lib/concurrent/agent.rb +583 -0
  6. data/lib/concurrent/array.rb +1 -0
  7. data/lib/concurrent/async.rb +236 -111
  8. data/lib/concurrent/atom.rb +101 -46
  9. data/lib/concurrent/atomic/atomic_boolean.rb +2 -0
  10. data/lib/concurrent/atomic/atomic_fixnum.rb +2 -0
  11. data/lib/concurrent/atomic/cyclic_barrier.rb +1 -1
  12. data/lib/concurrent/atomic/event.rb +1 -1
  13. data/lib/concurrent/atomic/mutex_atomic_boolean.rb +1 -1
  14. data/lib/concurrent/atomic/mutex_atomic_fixnum.rb +1 -1
  15. data/lib/concurrent/atomic/mutex_count_down_latch.rb +1 -1
  16. data/lib/concurrent/atomic/mutex_semaphore.rb +2 -2
  17. data/lib/concurrent/atomic/read_write_lock.rb +5 -4
  18. data/lib/concurrent/atomic/reentrant_read_write_lock.rb +3 -1
  19. data/lib/concurrent/atomic/thread_local_var.rb +2 -0
  20. data/lib/concurrent/atomic_reference/mutex_atomic.rb +1 -1
  21. data/lib/concurrent/atomics.rb +6 -4
  22. data/lib/concurrent/collection/copy_on_notify_observer_set.rb +7 -15
  23. data/lib/concurrent/collection/copy_on_write_observer_set.rb +7 -15
  24. data/lib/concurrent/collection/map/atomic_reference_map_backend.rb +5 -0
  25. data/lib/concurrent/concern/observable.rb +38 -13
  26. data/lib/concurrent/configuration.rb +5 -4
  27. data/lib/concurrent/delay.rb +9 -8
  28. data/lib/concurrent/exchanger.rb +2 -0
  29. data/lib/concurrent/executor/abstract_executor_service.rb +2 -2
  30. data/lib/concurrent/executor/java_single_thread_executor.rb +0 -1
  31. data/lib/concurrent/executor/ruby_executor_service.rb +10 -4
  32. data/lib/concurrent/executor/ruby_single_thread_executor.rb +10 -68
  33. data/lib/concurrent/executor/safe_task_executor.rb +7 -8
  34. data/lib/concurrent/executor/serialized_execution.rb +4 -4
  35. data/lib/concurrent/executor/single_thread_executor.rb +20 -10
  36. data/lib/concurrent/executor/timer_set.rb +4 -2
  37. data/lib/concurrent/executors.rb +0 -1
  38. data/lib/concurrent/future.rb +3 -2
  39. data/lib/concurrent/hash.rb +1 -1
  40. data/lib/concurrent/immutable_struct.rb +5 -1
  41. data/lib/concurrent/ivar.rb +1 -1
  42. data/lib/concurrent/mutable_struct.rb +7 -6
  43. data/lib/concurrent/{executor/executor.rb → options.rb} +4 -3
  44. data/lib/concurrent/promise.rb +3 -2
  45. data/lib/concurrent/scheduled_task.rb +3 -2
  46. data/lib/concurrent/settable_struct.rb +5 -4
  47. data/lib/concurrent/synchronization.rb +11 -3
  48. data/lib/concurrent/synchronization/abstract_lockable_object.rb +117 -0
  49. data/lib/concurrent/synchronization/abstract_object.rb +16 -129
  50. data/lib/concurrent/synchronization/abstract_struct.rb +2 -3
  51. data/lib/concurrent/synchronization/condition.rb +6 -4
  52. data/lib/concurrent/synchronization/jruby_lockable_object.rb +13 -0
  53. data/lib/concurrent/synchronization/{java_object.rb → jruby_object.rb} +5 -3
  54. data/lib/concurrent/synchronization/lock.rb +3 -2
  55. data/lib/concurrent/synchronization/lockable_object.rb +59 -0
  56. data/lib/concurrent/synchronization/mri_lockable_object.rb +71 -0
  57. data/lib/concurrent/synchronization/mri_object.rb +35 -0
  58. data/lib/concurrent/synchronization/object.rb +111 -39
  59. data/lib/concurrent/synchronization/rbx_lockable_object.rb +64 -0
  60. data/lib/concurrent/synchronization/rbx_object.rb +17 -68
  61. data/lib/concurrent/thread_safe/util.rb +0 -9
  62. data/lib/concurrent/thread_safe/util/adder.rb +3 -0
  63. data/lib/concurrent/thread_safe/util/array_hash_rbx.rb +3 -1
  64. data/lib/concurrent/thread_safe/util/cheap_lockable.rb +3 -0
  65. data/lib/concurrent/thread_safe/util/power_of_two_tuple.rb +1 -0
  66. data/lib/concurrent/thread_safe/util/striped64.rb +6 -1
  67. data/lib/concurrent/thread_safe/util/volatile.rb +2 -0
  68. data/lib/concurrent/thread_safe/util/xor_shift_random.rb +2 -0
  69. data/lib/concurrent/tvar.rb +36 -0
  70. data/lib/concurrent/utility/at_exit.rb +1 -1
  71. data/lib/concurrent/utility/monotonic_time.rb +3 -4
  72. data/lib/concurrent/utility/native_extension_loader.rb +1 -1
  73. data/lib/concurrent/version.rb +2 -2
  74. metadata +12 -7
  75. data/lib/concurrent/synchronization/monitor_object.rb +0 -27
  76. data/lib/concurrent/synchronization/mutex_object.rb +0 -43
@@ -27,6 +27,7 @@ module Concurrent
27
27
 
28
28
  elsif Concurrent.on_rbx?
29
29
  require 'monitor'
30
+ require 'concurrent/thread_safe/util/array_hash_rbx'
30
31
 
31
32
  # @!macro concurrent_array
32
33
  class Array < ::Array
@@ -1,52 +1,214 @@
1
- require 'thread'
2
- require 'concurrent/configuration'
3
- require 'concurrent/delay'
4
- require 'concurrent/errors'
5
1
  require 'concurrent/ivar'
6
- require 'concurrent/executor/immediate_executor'
7
- require 'concurrent/executor/serialized_execution'
2
+ require 'concurrent/executor/single_thread_executor'
8
3
 
9
4
  module Concurrent
10
5
 
11
- # A mixin module that provides simple asynchronous behavior to any standard
12
- # class/object or object.
6
+ # A mixin module that provides simple asynchronous behavior to a class,
7
+ # turning it into a simple actor. Loosely based on Erlang's
8
+ # [gen_server](http://www.erlang.org/doc/man/gen_server.html), but without
9
+ # supervision or linking.
10
+ #
11
+ # A more feature-rich {Concurrent::Actor} is also available when the
12
+ # capabilities of `Async` are too limited.
13
13
  #
14
14
  # ```cucumber
15
15
  # Feature:
16
- # As a stateful, plain old Ruby class/object
16
+ # As a stateful, plain old Ruby class
17
17
  # I want safe, asynchronous behavior
18
18
  # So my long-running methods don't block the main thread
19
19
  # ```
20
20
  #
21
- # Stateful, mutable objects must be managed carefully when used asynchronously.
22
- # But Ruby is an object-oriented language so designing with objects and classes
23
- # plays to Ruby's strengths and is often more natural to many Ruby programmers.
24
- # The `Async` module is a way to mix simple yet powerful asynchronous capabilities
25
- # into any plain old Ruby object or class. These capabilities provide a reasonable
26
- # level of thread safe guarantees when used correctly.
21
+ # The `Async` module is a way to mix simple yet powerful asynchronous
22
+ # capabilities into any plain old Ruby object or class, turning each object
23
+ # into a simple Actor. Method calls are processed on a background thread. The
24
+ # caller is free to perform other actions while processing occurs in the
25
+ # background.
26
+ #
27
+ # Method calls to the asynchronous object are made via two proxy methods:
28
+ # `async` (alias `cast`) and `await` (alias `call`). These proxy methods post
29
+ # the method call to the object's background thread and return a "future"
30
+ # which will eventually contain the result of the method call.
31
+ #
32
+ # This behavior is loosely patterned after Erlang's `gen_server` behavior.
33
+ # When an Erlang module implements the `gen_server` behavior it becomes
34
+ # inherently asynchronous. The `start` or `start_link` function spawns a
35
+ # process (similar to a thread but much more lightweight and efficient) and
36
+ # reurns the ID of the process. Using the process ID, other processes can
37
+ # send messages to the `gen_server` via the `cast` and `call` methods. Unlike
38
+ # Erlang's `gen_server`, however, `Async` classes do not support linking or
39
+ # supervision trees.
27
40
  #
28
- # When this module is mixed into a class or object it provides to new methods:
29
- # `async` and `await`. These methods are thread safe with respect to the enclosing
30
- # object. The former method allows methods to be called asynchronously by posting
31
- # to the global thread pool. The latter allows a method to be called synchronously
32
- # on the current thread but does so safely with respect to any pending asynchronous
33
- # method calls. Both methods return an `IVar` which can be inspected for
34
- # the result of the method call. Calling a method with `async` will return a
41
+ # ## Basic Usage
42
+ #
43
+ # When this module is mixed into a class, objects of the class become inherently
44
+ # asynchronous. Each object gets its own background thread (specifically,
45
+ # `SingleThreadExecutor`) on which to post asynchronous method calls.
46
+ # Asynchronous method calls are executed in the background one at a time in
47
+ # the order they are received.
48
+ #
49
+ # To create an asynchronous class, simply mix in the `Concurrent::Async` module:
50
+ #
51
+ # ```
52
+ # class Hello
53
+ # include Concurrent::Async
54
+ #
55
+ # def hello(name)
56
+ # "Hello, #{name}!"
57
+ # end
58
+ # end
59
+ # ```
60
+ #
61
+ # When defining a constructor it is critica that the first line be a call to
62
+ # `super` with no arguments. The `super` method initializes the background
63
+ # thread and other asynchronous components.
64
+ #
65
+ # ```
66
+ # class BackgroundLogger
67
+ # include Concurrent::Async
68
+ #
69
+ # def initialize(level)
70
+ # super()
71
+ # @logger = Logger.new(STDOUT)
72
+ # @logger.level = level
73
+ # end
74
+ #
75
+ # def info(msg)
76
+ # @logger.info(msg)
77
+ # end
78
+ # end
79
+ # ```
80
+ #
81
+ # Mixing this module into a class provides each object two proxy methods:
82
+ # `async` and `await`. These methods are thread safe with respect to the
83
+ # enclosing object. The former proxy allows methods to be called
84
+ # asynchronously by posting to the object's internal thread. The latter proxy
85
+ # allows a method to be called synchronously but does so safely with respect
86
+ # to any pending asynchronous method calls and ensures proper ordering. Both
87
+ # methods return a {Concurrent::IVar} which can be inspected for the result
88
+ # of the proxied method call. Calling a method with `async` will return a
35
89
  # `:pending` `IVar` whereas `await` will return a `:complete` `IVar`.
90
+ #
91
+ # ```
92
+ # class Echo
93
+ # include Concurrent::Async
94
+ #
95
+ # def echo(msg)
96
+ # print "#{msg}\n"
97
+ # end
98
+ # end
99
+ #
100
+ # horn = Echo.new
101
+ # horn.echo('zero') # synchronous, not thread-safe
102
+ # # returns the actual return value of the method
103
+ #
104
+ # horn.async.echo('one') # asynchronous, non-blocking, thread-safe
105
+ # # returns an IVar in the :pending state
106
+ #
107
+ # horn.await.echo('two') # synchronous, blocking, thread-safe
108
+ # # returns an IVar in the :complete state
109
+ # ```
110
+ #
111
+ # ## Let It Fail
112
+ #
113
+ # The `async` and `await` proxy methods have built-in error protection based
114
+ # on Erlang's famous "let it fail" philosophy. Instance methods should not be
115
+ # programmed defensively. When an exception is raised by a delegated method
116
+ # the proxy will rescue the exception, expose it to the caller as the `reason`
117
+ # attribute of the returned future, then process the next method call.
118
+ #
119
+ # ## Calling Methods Internally
120
+ #
121
+ # External method calls should *always* use the `async` and `await` proxy
122
+ # methods. When one method calls another method, the `async` proxy should
123
+ # rarely be used and the `await` proxy should *never* be used.
124
+ #
125
+ # When an object calls one of its own methods using the `await` proxy the
126
+ # second call will be enqueued *behind* the currently running method call.
127
+ # Any attempt to wait on the result will fail as the second call will never
128
+ # run until after the current call completes.
129
+ #
130
+ # Calling a method using the `await` proxy from within a method that was
131
+ # itself called using `async` or `await` will irreversibly deadlock the
132
+ # object. Do *not* do this, ever.
133
+ #
134
+ # ## Instance Variables and Attribute Accessors
135
+ #
136
+ # Instance variables do not need to be thread-safe so long as they are private.
137
+ # Asynchronous method calls are processed in the order they are received and
138
+ # are processed one at a time. Therefore private instance variables can only
139
+ # be accessed by one thread at a time. This is inherently thread-safe.
140
+ #
141
+ # When using private instance variables within asynchronous methods, the best
142
+ # practice is to read the instance variable into a local variable at the start
143
+ # of the method then update the instance variable at the *end* of the method.
144
+ # This way, should an exception be raised during method execution the internal
145
+ # state of the boject will not have been changed.
146
+ #
147
+ # ### Reader Attributes
148
+ #
149
+ # The use of `attr_reader` is discouraged. Internal state exposed externally,
150
+ # when necessary, should be done through accessor methods. The instance
151
+ # variables exposed by these methods *must* be thread-safe, or they must be
152
+ # called using the `async` and `await` proxy methods. These two approaches are
153
+ # subtly different.
154
+ #
155
+ # When internal state is accessed via the `async` and `await` proxy methods,
156
+ # the returned value represents the object's sate *at the time the call is
157
+ # processed*, which may *not* be the state of the object at the time the call
158
+ # is made.
159
+ #
160
+ # To get the state *at the current* time, irrespective of an enqueued method
161
+ # calls, a reader method must be called directly. This is inherently unsafe
162
+ # unless the instance variable is itself thread-safe, preferrably using one
163
+ # of the thread-safe classes within this library. Because the thread-safe
164
+ # classes within this library are internally-locking or non-locking, they can
165
+ # be safely used from within asynchronous methods without causing deadlocks.
166
+ #
167
+ # Generally speaking, the best practice is to *not* expose internal state via
168
+ # reader methods. The best practice is to simply use the method's return value.
169
+ #
170
+ # ### Writer Attributes
171
+ #
172
+ # Writer attributes should never be used with asynchronous classes. Changing
173
+ # the state externally, even when done in the thread-safe way, is not logically
174
+ # consistent. Changes to state need to be timed with respect to all asynchronous
175
+ # method calls which my be in-process or enqueued. The only safe practice is to
176
+ # pass all necessary data to each method as arguments and let the method update
177
+ # the internal state as necessary.
178
+ #
179
+ # ## Class Constants, Variables, and Methods
180
+ #
181
+ # ### Class Constants
182
+ #
183
+ # Class constants do not need to be thread-safe. Since they are read-only and
184
+ # immutable they may be safely read both externally and from within
185
+ # asynchronous methods.
186
+ #
187
+ # ### Class Variables
188
+ #
189
+ # Class variables should be avoided. Class variables represent shared state.
190
+ # Shared state is anathema to concurrency. Should there be a need to share
191
+ # state using class variables they *must* be thread-safe, preferrably
192
+ # using the thread-safe classes within this library. When updating class
193
+ # variables, never assign a new value/object to the variable itself. Assignment
194
+ # is not thread-safe in Ruby. Instead, use the thread-safe update functions
195
+ # of the variable itself to change the value.
196
+ #
197
+ # The best practice is to *never* use class variables with `Async` classes.
198
+ #
199
+ # ### Class Methods
200
+ #
201
+ # Class methods which are pure functions are safe. Class methods which modify
202
+ # class variables should be avoided, for all the reasons listed above.
36
203
  #
37
- # Very loosely based on the `async` and `await` keywords in C#.
38
- #
39
- # ### An Important Note About Thread Safe Guarantees
204
+ # ## An Important Note About Thread Safe Guarantees
40
205
  #
41
206
  # > Thread safe guarantees can only be made when asynchronous method calls
42
- # > are not mixed with synchronous method calls. Use only synchronous calls
207
+ # > are not mixed with direct method calls. Use only direct method calls
43
208
  # > when the object is used exclusively on a single thread. Use only
44
209
  # > `async` and `await` when the object is shared between threads. Once you
45
- # > call a method using `async`, you should no longer call any methods
210
+ # > call a method using `async` or `await`, you should no longer call methods
46
211
  # > directly on the object. Use `async` and `await` exclusively from then on.
47
- # > With careful programming it is possible to switch back and forth but it's
48
- # > also very easy to create race conditions and break your application.
49
- # > Basically, it's "async all the way down."
50
212
  #
51
213
  # @example
52
214
  #
@@ -54,19 +216,25 @@ module Concurrent
54
216
  # include Concurrent::Async
55
217
  #
56
218
  # def echo(msg)
57
- # sleep(rand)
58
219
  # print "#{msg}\n"
59
- # nil
60
220
  # end
61
221
  # end
62
222
  #
63
223
  # horn = Echo.new
64
224
  # horn.echo('zero') # synchronous, not thread-safe
225
+ # # returns the actual return value of the method
65
226
  #
66
227
  # horn.async.echo('one') # asynchronous, non-blocking, thread-safe
228
+ # # returns an IVar in the :pending state
229
+ #
67
230
  # horn.await.echo('two') # synchronous, blocking, thread-safe
231
+ # # returns an IVar in the :complete state
68
232
  #
69
- # @see Concurrent::IVar
233
+ # @see Concurrent::Actor
234
+ # @see Concurrent::SingleThreadExecutor
235
+ # @see https://en.wikipedia.org/wiki/Actor_model "Actor Model" at Wikipedia
236
+ # @see http://www.erlang.org/doc/man/gen_server.html Erlang gen_server
237
+ # @see http://c2.com/cgi/wiki?LetItCrash "Let It Crash" at http://c2.com/
70
238
  module Async
71
239
 
72
240
  # @!method self.new(*args, &block)
@@ -76,7 +244,7 @@ module Concurrent
76
244
  #
77
245
  # @param [Array<Object>] args Zero or more arguments to be passed to the
78
246
  # object's initializer.
79
- # @param [Proc] bloc Optional block to pass to the object's initializer.
247
+ # @param [Proc] block Optional block to pass to the object's initializer.
80
248
  # @return [Object] A properly initialized object of the asynchronous class.
81
249
 
82
250
  # Check for the presence of a method on an object and determine if a given
@@ -138,13 +306,11 @@ module Concurrent
138
306
  # given executor. Block if necessary.
139
307
  #
140
308
  # @param [Object] delegate the object to wrap and delegate method calls to
141
- # @param [Concurrent::Delay] executor a `Delay` wrapping the executor on which to execute delegated method calls
142
- # @param [Concurrent::SerializedExecution] serializer the serializer to use when delegating method calls
309
+ # @param [Concurrent::ExecutorService] executor the executor on which to execute delegated method calls
143
310
  # @param [Boolean] blocking will block awaiting result when `true`
144
- def initialize(delegate, executor, serializer, blocking = false)
311
+ def initialize(delegate, executor, blocking)
145
312
  @delegate = delegate
146
313
  @executor = executor
147
- @serializer = serializer
148
314
  @blocking = blocking
149
315
  end
150
316
 
@@ -163,89 +329,61 @@ module Concurrent
163
329
  super unless @delegate.respond_to?(method)
164
330
  Async::validate_argc(@delegate, method, *args)
165
331
 
166
- self.define_singleton_method(method) do |*args2|
167
- Async::validate_argc(@delegate, method, *args2)
168
- ivar = Concurrent::IVar.new
169
- @serializer.post(@executor.value) do
170
- begin
171
- ivar.set(@delegate.send(method, *args2, &block))
172
- rescue => reason
173
- ivar.fail(reason)
174
- end
332
+ ivar = Concurrent::IVar.new
333
+ @executor.post(args) do |arguments|
334
+ begin
335
+ ivar.set(@delegate.send(method, *arguments, &block))
336
+ rescue => error
337
+ ivar.fail(error)
175
338
  end
176
- ivar.value if @blocking
177
- ivar
178
339
  end
179
-
180
- self.send(method, *args)
340
+ ivar.wait if @blocking
341
+ ivar
181
342
  end
182
343
  end
183
344
  private_constant :AsyncDelegator
184
345
 
185
346
  # Causes the chained method call to be performed asynchronously on the
186
- # global thread pool. The method called by this method will return a
187
- # future object in the `:pending` state and the method call will have
188
- # been scheduled on the global thread pool. The final disposition of the
189
- # method call can be obtained by inspecting the returned future.
190
- #
191
- # Before scheduling the method on the global thread pool a best-effort
192
- # attempt will be made to validate that the method exists on the object
193
- # and that the given arguments match the arity of the requested function.
194
- # Due to the dynamic nature of Ruby and limitations of its reflection
195
- # library, some edge cases will be missed. For more information see
196
- # the documentation for the `validate_argc` method.
347
+ # object's thread. The delegated method will return a future in the
348
+ # `:pending` state and the method call will have been scheduled on the
349
+ # object's thread. The final disposition of the method call can be obtained
350
+ # by inspecting the returned future.
197
351
  #
198
352
  # @!macro [attach] async_thread_safety_warning
199
353
  # @note The method call is guaranteed to be thread safe with respect to
200
354
  # all other method calls against the same object that are called with
201
355
  # either `async` or `await`. The mutable nature of Ruby references
202
356
  # (and object orientation in general) prevent any other thread safety
203
- # guarantees. Do NOT mix non-protected method calls with protected
204
- # method call. Use *only* protected method calls when sharing the object
205
- # between threads.
357
+ # guarantees. Do NOT mix direct method calls with delegated method calls.
358
+ # Use *only* delegated method calls when sharing the object between threads.
206
359
  #
207
360
  # @return [Concurrent::IVar] the pending result of the asynchronous operation
208
361
  #
209
- # @raise [NameError] the object does not respond to `method` method
210
- # @raise [ArgumentError] the given `args` do not match the arity of `method`
211
- #
212
- # @see Concurrent::IVar
362
+ # @raise [NameError] the object does not respond to the requested method
363
+ # @raise [ArgumentError] the given `args` do not match the arity of
364
+ # the requested method
213
365
  def async
214
- @__async_delegator__.value
366
+ @__async_delegator__
215
367
  end
368
+ alias_method :cast, :async
216
369
 
217
370
  # 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.
371
+ # current thread. The delegated will return a future in either the
372
+ # `:fulfilled` or `:rejected` state and the delegated method will have
373
+ # completed. The final disposition of the delegated method can be obtained
374
+ # by inspecting the returned future.
229
375
  #
230
376
  # @!macro async_thread_safety_warning
231
377
  #
232
378
  # @return [Concurrent::IVar] the completed result of the synchronous operation
233
379
  #
234
- # @raise [NameError] the object does not respond to `method` method
235
- # @raise [ArgumentError] the given `args` do not match the arity of `method`
236
- #
237
- # @see Concurrent::IVar
380
+ # @raise [NameError] the object does not respond to the requested method
381
+ # @raise [ArgumentError] the given `args` do not match the arity of the
382
+ # requested method
238
383
  def await
239
- @__await_delegator__.value
240
- end
241
-
242
- # Set a new executor.
243
- #
244
- # @raise [ArgumentError] executor has already been set.
245
- def executor=(executor)
246
- @__async_executor__.reconfigure { executor } or
247
- raise ArgumentError.new('executor has already been set')
384
+ @__await_delegator__
248
385
  end
386
+ alias_method :call, :await
249
387
 
250
388
  private
251
389
 
@@ -254,27 +392,14 @@ module Concurrent
254
392
  # @note This method *must* be called immediately upon object construction.
255
393
  # This is the only way thread-safe initialization can be guaranteed.
256
394
  #
257
- # @raise [Concurrent::InitializationError] when called more than once
258
- #
259
395
  # @!visibility private
260
396
  def init_synchronization
261
397
  return self if @__async_initialized__
262
-
263
398
  @__async_initialized__ = true
264
- serializer = Concurrent::SerializedExecution.new
265
-
266
- @__async_executor__ = Delay.new {
267
- Concurrent.global_io_executor
268
- }
269
-
270
- @__await_delegator__ = Delay.new {
271
- AsyncDelegator.new(self, Delay.new{ Concurrent::ImmediateExecutor.new }, serializer, true)
272
- }
273
-
274
- @__async_delegator__ = Delay.new {
275
- AsyncDelegator.new(self, @__async_executor__, serializer, false)
276
- }
277
-
399
+ @__async_executor__ = Concurrent::SingleThreadExecutor.new(
400
+ fallback_policy: :caller_runs, auto_terminate: true)
401
+ @__await_delegator__ = AsyncDelegator.new(self, @__async_executor__, true)
402
+ @__async_delegator__ = AsyncDelegator.new(self, @__async_executor__, false)
278
403
  self
279
404
  end
280
405
  end