Skip to content

Commit 7041187

Browse files
committed
Changed my mind about Async.new vs Async.create
1 parent d5b2635 commit 7041187

File tree

2 files changed

+26
-59
lines changed

2 files changed

+26
-59
lines changed

lib/concurrent/async.rb

Lines changed: 7 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
require 'concurrent/ivar'
66
require 'concurrent/executor/immediate_executor'
77
require 'concurrent/executor/serialized_execution'
8+
require 'concurrent/concern/deprecation'
89

910
module Concurrent
1011

@@ -30,23 +31,12 @@ module Concurrent
3031
# object. The former method allows methods to be called asynchronously by posting
3132
# to the global thread pool. The latter allows a method to be called synchronously
3233
# on the current thread but does so safely with respect to any pending asynchronous
33-
# method calls. Both methods return an `Obligation` which can be inspected for
34+
# method calls. Both methods return an `IVar` which can be inspected for
3435
# the result of the method call. Calling a method with `async` will return a
35-
# `:pending` `Obligation` whereas `await` will return a `:complete` `Obligation`.
36+
# `:pending` `IVar` whereas `await` will return a `:complete` `IVar`.
3637
#
3738
# Very loosely based on the `async` and `await` keywords in C#.
3839
#
39-
# ### An Important Note About Initialization
40-
#
41-
# > This module depends on several internal stnchronization mechanisms that
42-
# > must be initialized prior to calling any of the async/await/executor methods.
43-
# > To ensure thread-safe initialization the class `new` method will be made
44-
# > private when the `Concurrent::Async` module is included. A factory method
45-
# > called `create` will be defined in its place. The `create`factory will
46-
# > create a new object instance, passing all arguments to the constructor,
47-
# > and will initialize all stnchronization mechanisms. This is the only way
48-
# > thread-safe initialization can be guaranteed.
49-
#
5040
# ### An Important Note About Thread Safe Guarantees
5141
#
5242
# > Thread safe guarantees can only be made when asynchronous method calls
@@ -70,23 +60,19 @@ module Concurrent
7060
# nil
7161
# end
7262
# end
73-
#
74-
# horn = Echo.new #=> NoMethodError: private method `new' called for Echo:Class
7563
#
76-
# horn = Echo.create
64+
# horn = Echo.new
7765
# horn.echo('zero') # synchronous, not thread-safe
7866
#
7967
# horn.async.echo('one') # asynchronous, non-blocking, thread-safe
8068
# horn.await.echo('two') # synchronous, blocking, thread-safe
8169
#
82-
# @see Concurrent::Concern::Obligation
8370
# @see Concurrent::IVar
8471
module Async
8572

86-
# @!method self.create(*args, &block)
73+
# @!method self.new(*args, &block)
8774
#
88-
# The factory method used to create new instances of the asynchronous
89-
# class. Used instead of `new` to ensure proper initialization of the
75+
# Instanciate a new object and ensure proper initialization of the
9076
# synchronization mechanisms.
9177
#
9278
# @param [Array<Object>] args Zero or more arguments to be passed to the
@@ -129,21 +115,13 @@ def self.validate_argc(obj, method, *args)
129115
# @!visibility private
130116
def self.included(base)
131117
base.singleton_class.send(:alias_method, :original_new, :new)
132-
base.send(:private_class_method, :original_new)
133118
base.extend(ClassMethods)
134119
super(base)
135120
end
136121

137122
# @!visibility private
138123
module ClassMethods
139-
140-
# @deprecated
141124
def new(*args, &block)
142-
warn '[DEPRECATED] use the `create` method instead'
143-
create(*args, &block)
144-
end
145-
146-
def create(*args, &block)
147125
obj = original_new(*args, &block)
148126
obj.send(:init_synchronization)
149127
obj
@@ -280,7 +258,7 @@ def executor=(executor)
280258
# @!visibility private
281259
# @deprecated
282260
def init_mutex
283-
warn '[DEPRECATED] use the `create` method instead'
261+
deprecated 'mutex synchronization now happens automatically'
284262
init_synchronization
285263
rescue InitializationError
286264
# suppress

spec/concurrent/async_spec.rb

Lines changed: 19 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -29,38 +29,20 @@ def with_block
2929
end
3030

3131
subject do
32-
obj = async_class.create
32+
obj = async_class.new
3333
obj.executor = executor
3434
obj
3535
end
3636

3737
context 'object creation' do
3838

39-
# Will be added in 1.0 once deprecated methods are removed
40-
#it 'makes #new private' do
41-
# expect{ async_class.new }.to raise_error(NoMethodError)
42-
#end
43-
44-
# Will be removed in 1.0 once deprecated methods are removed
45-
it '#new delegates to #create' do
39+
it 'delegates to the original constructor' do
4640
args = [:foo, 'bar', 42]
47-
expect(async_class).to receive(:create).once.with(*args)
41+
expect(async_class).to receive(:original_new).once.with(*args).and_call_original
4842
async_class.new(*args)
4943
end
5044

51-
it 'uses #create to instanciate new objects' do
52-
object = async_class.create
53-
expect(object).to be_a async_class
54-
end
55-
56-
specify '#create initializes synchronization' do
57-
mock = async_class.create
58-
allow(async_class).to receive(:original_new).and_return(mock)
59-
expect(mock).to receive(:init_synchronization).once.with(no_args)
60-
async_class.create
61-
end
62-
63-
specify '#create passes all args to the constructor' do
45+
specify 'passes all args to the original constructor' do
6446
clazz = Class.new do
6547
include Concurrent::Async
6648
attr_reader :args
@@ -69,11 +51,11 @@ def initialize(*args)
6951
end
7052
end
7153

72-
object = clazz.create(:foo, :bar)
54+
object = clazz.new(:foo, :bar)
7355
expect(object.args).to eq [:foo, :bar]
7456
end
7557

76-
specify '#create passes all args to the constructor' do
58+
specify 'passes a given block to the original constructor' do
7759
clazz = Class.new do
7860
include Concurrent::Async
7961
attr_reader :block
@@ -82,9 +64,16 @@ def initialize(&block)
8264
end
8365
end
8466

85-
object = clazz.create{ 42 }
67+
object = clazz.new{ 42 }
8668
expect(object.block).to eq 42
8769
end
70+
71+
specify 'initializes synchronization' do
72+
mock = async_class.new
73+
allow(async_class).to receive(:original_new).and_return(mock)
74+
expect(mock).to receive(:init_synchronization).once.with(no_args)
75+
async_class.new
76+
end
8877
end
8978

9079
context '#validate_argc' do
@@ -158,21 +147,21 @@ def many(*args, &block) nil; end
158147
it 'returns the default executor when #executor= has never been called' do
159148
expect(Concurrent).to receive(:global_io_executor).
160149
and_return(ImmediateExecutor.new)
161-
subject = async_class.create
150+
subject = async_class.new
162151
subject.async.echo(:foo)
163152
end
164153

165154
it 'returns the memo after #executor= has been called' do
166155
executor = ImmediateExecutor.new
167156
expect(executor).to receive(:post)
168-
subject = async_class.create
157+
subject = async_class.new
169158
subject.executor = executor
170159
subject.async.echo(:foo)
171160
end
172161

173162
it 'raises an exception if #executor= is called after initialization complete' do
174163
executor = ImmediateExecutor.new
175-
subject = async_class.create
164+
subject = async_class.new
176165
subject.async.echo(:foo)
177166
expect {
178167
subject.executor = executor
@@ -209,7 +198,7 @@ def many(*args, &block) nil; end
209198
it 'runs the future on the memoized executor' do
210199
executor = ImmediateExecutor.new
211200
expect(executor).to receive(:post).with(any_args)
212-
subject = async_class.create
201+
subject = async_class.new
213202
subject.executor = executor
214203
subject.async.echo(:foo)
215204
end
@@ -351,7 +340,7 @@ def gather(seconds, first, *rest)
351340
(@bucket ||= []).concat([first])
352341
@bucket.concat(rest)
353342
end
354-
}.create
343+
}.new
355344

356345
object.async.gather(0.5, :a, :b)
357346
object.await.gather(0, :c, :d)

0 commit comments

Comments
 (0)