diff --git a/Gemfile b/Gemfile index ee64ec1c2..c00c2e700 100644 --- a/Gemfile +++ b/Gemfile @@ -6,13 +6,14 @@ gem 'concurrent-ruby-ext', path: '.', platform: :mri group :development do gem 'rake', '~> 11.0' - gem 'rake-compiler', '~> 0.9.5' - gem 'rake-compiler-dock', '~> 0.4.3' + gem 'rake-compiler', '~> 1.0.0' + gem 'rake-compiler-dock', '~> 0.6.0' gem 'gem-compiler', '~> 0.3.0' gem 'benchmark-ips', '~> 2.7' # documentation gem 'countloc', '~> 0.4.0', :platforms => :mri, :require => false + # TODO (pitr-ch 04-May-2018): update to remove: [DEPRECATION] `last_comment` is deprecated. Please use `last_description` instead. gem 'yard', '~> 0.8.0', :require => false gem 'redcarpet', '~> 3.3', platforms: :mri # understands github markdown gem 'md-ruby-eval' diff --git a/Rakefile b/Rakefile index b8dcd4e6e..387f64cb4 100644 --- a/Rakefile +++ b/Rakefile @@ -181,9 +181,7 @@ begin --backtrace --seed 1 --format documentation - --tag ~unfinished - --tag ~notravis - --tag ~buggy ] + --tag ~notravis ] RSpec::Core::RakeTask.new(:travis) do |t| t.rspec_opts = ['--color', *options].join(' ') diff --git a/ext/ConcurrentRubyExtService.java b/ext/ConcurrentRubyExtService.java index 2a9d65f07..42d87c14e 100644 --- a/ext/ConcurrentRubyExtService.java +++ b/ext/ConcurrentRubyExtService.java @@ -2,7 +2,6 @@ import org.jruby.Ruby; import org.jruby.runtime.load.BasicLibraryService; -import com.concurrent_ruby.ext.JRubyMapBackendLibrary; public class ConcurrentRubyExtService implements BasicLibraryService { public boolean basicLoad(final Ruby runtime) throws IOException { diff --git a/lib/concurrent/actor.rb b/lib/concurrent/actor.rb index 9d0d40694..656e7a327 100644 --- a/lib/concurrent/actor.rb +++ b/lib/concurrent/actor.rb @@ -35,9 +35,13 @@ def self.current end @root = Concurrent::Promises.delay do - Core.new(parent: nil, name: '/', class: Root, initialized: future = Concurrent::Promises.resolvable_future).reference.tap do - future.wait! - end + Core. + new(parent: nil, + name: '/', + class: Root, + initialized: future = Concurrent::Promises.resolvable_future). + reference. + tap { future.wait! } end # A root actor, a default parent of all actors spawned outside an actor diff --git a/lib/concurrent/actor/behaviour.rb b/lib/concurrent/actor/behaviour.rb index d96d4a7b3..825574c84 100644 --- a/lib/concurrent/actor/behaviour.rb +++ b/lib/concurrent/actor/behaviour.rb @@ -49,7 +49,7 @@ module Actor # - {RestartingContext} uses # {include:Actor::Behaviour.restarting_behaviour_definition} module Behaviour - MESSAGE_PROCESSED = Object.new + MESSAGE_PROCESSED = ::Object.new require 'concurrent/actor/behaviour/abstract' require 'concurrent/actor/behaviour/awaits' diff --git a/lib/concurrent/actor/core.rb b/lib/concurrent/actor/core.rb index 1546dfe0c..db0dc5717 100644 --- a/lib/concurrent/actor/core.rb +++ b/lib/concurrent/actor/core.rb @@ -90,8 +90,9 @@ def remove_child(child) # can be called from other alternative Reference implementations # @param [Envelope] envelope def on_envelope(envelope) + log(DEBUG) { "is #{envelope.future ? 'asked' : 'told'} #{envelope.message.inspect} by #{envelope.sender}" } schedule_execution do - log(DEBUG) { "was #{envelope.future ? 'asked' : 'told'} #{envelope.message.inspect} by #{envelope.sender}" } + log(DEBUG) { "was #{envelope.future ? 'asked' : 'told'} #{envelope.message.inspect} by #{envelope.sender} - processing" } process_envelope envelope end nil diff --git a/lib/concurrent/agent.rb b/lib/concurrent/agent.rb index dfb9a9288..464da67dc 100644 --- a/lib/concurrent/agent.rb +++ b/lib/concurrent/agent.rb @@ -147,7 +147,7 @@ class Agent < Synchronization::LockableObject ERROR_MODES = [:continue, :fail].freeze private_constant :ERROR_MODES - AWAIT_FLAG = Object.new + AWAIT_FLAG = ::Object.new private_constant :AWAIT_FLAG AWAIT_ACTION = ->(value, latch) { latch.count_down; AWAIT_FLAG } diff --git a/lib/concurrent/async.rb b/lib/concurrent/async.rb index 735ec4f0e..d11bf2c48 100644 --- a/lib/concurrent/async.rb +++ b/lib/concurrent/async.rb @@ -435,7 +435,7 @@ def await # # @!visibility private def init_synchronization - return self if @__async_initialized__ + return self if defined?(@__async_initialized__) && @__async_initialized__ @__async_initialized__ = true @__async_delegator__ = AsyncDelegator.new(self) @__await_delegator__ = AwaitDelegator.new(@__async_delegator__) diff --git a/lib/concurrent/atomic/count_down_latch.rb b/lib/concurrent/atomic/count_down_latch.rb index 6cad016db..450d47a29 100644 --- a/lib/concurrent/atomic/count_down_latch.rb +++ b/lib/concurrent/atomic/count_down_latch.rb @@ -55,7 +55,7 @@ module Concurrent # @!macro internal_implementation_note CountDownLatchImplementation = case when Concurrent.on_jruby? - JavaCountDownLatch + MutexCountDownLatch else MutexCountDownLatch end diff --git a/lib/concurrent/atomic/read_write_lock.rb b/lib/concurrent/atomic/read_write_lock.rb index 435d8f0d6..246f21aac 100644 --- a/lib/concurrent/atomic/read_write_lock.rb +++ b/lib/concurrent/atomic/read_write_lock.rb @@ -193,7 +193,8 @@ def acquire_write_lock # # @return [Boolean] true if the lock is successfully released def release_write_lock - c = @Counter.update { |counter| counter-RUNNING_WRITER } + return true unless running_writer? + c = @Counter.update { |counter| counter - RUNNING_WRITER } @ReadLock.broadcast @WriteLock.signal if waiting_writers(c) > 0 true diff --git a/lib/concurrent/concern/logging.rb b/lib/concurrent/concern/logging.rb index a11a2a819..2c749996f 100644 --- a/lib/concurrent/concern/logging.rb +++ b/lib/concurrent/concern/logging.rb @@ -17,7 +17,12 @@ module Logging def log(level, progname, message = nil, &block) #NOTE: Cannot require 'concurrent/configuration' above due to circular references. # Assume that the gem has been initialized if we've gotten this far. - (@logger || Concurrent.global_logger).call level, progname, message, &block + logger = if defined?(@logger) && @logger + @logger + else + Concurrent.global_logger + end + logger.call level, progname, message, &block rescue => error $stderr.puts "`Concurrent.configuration.logger` failed to log #{[level, progname, message, block]}\n" + "#{error.message} (#{error.class})\n#{error.backtrace.join "\n"}" diff --git a/lib/concurrent/constants.rb b/lib/concurrent/constants.rb index 4f9bedf24..676c2afb9 100644 --- a/lib/concurrent/constants.rb +++ b/lib/concurrent/constants.rb @@ -3,6 +3,6 @@ module Concurrent # Various classes within allows for +nil+ values to be stored, # so a special +NULL+ token is required to indicate the "nil-ness". # @!visibility private - NULL = Object.new + NULL = ::Object.new end diff --git a/lib/concurrent/edge/promises.rb b/lib/concurrent/edge/promises.rb index bf3f73ca7..9011dee74 100644 --- a/lib/concurrent/edge/promises.rb +++ b/lib/concurrent/edge/promises.rb @@ -1987,7 +1987,7 @@ class Channel < Concurrent::Synchronization::Object safe_initialization! # Default size of the Channel, makes it accept unlimited number of messages. - UNLIMITED = Object.new + UNLIMITED = ::Object.new UNLIMITED.singleton_class.class_eval do include Comparable diff --git a/lib/concurrent/exchanger.rb b/lib/concurrent/exchanger.rb index 80e1cd613..a555a0b9b 100644 --- a/lib/concurrent/exchanger.rb +++ b/lib/concurrent/exchanger.rb @@ -41,7 +41,7 @@ module Concurrent class AbstractExchanger < Synchronization::Object # @!visibility private - CANCEL = Object.new + CANCEL = ::Object.new private_constant :CANCEL # @!macro [attach] exchanger_method_initialize @@ -329,7 +329,7 @@ def do_exchange(value, timeout) # @!macro internal_implementation_note ExchangerImplementation = case when Concurrent.on_jruby? - JavaExchanger + RubyExchanger else RubyExchanger end diff --git a/lib/concurrent/executor/cached_thread_pool.rb b/lib/concurrent/executor/cached_thread_pool.rb index 1f103656c..8cb6f44b7 100644 --- a/lib/concurrent/executor/cached_thread_pool.rb +++ b/lib/concurrent/executor/cached_thread_pool.rb @@ -46,17 +46,24 @@ def initialize(opts = {}) private - # @!macro cached_thread_pool_method_initialize - # @!visibility private - def ns_initialize(opts) - super(opts) - if Concurrent.on_jruby? + if defined?(JavaThreadPoolExecutor) && self < JavaThreadPoolExecutor + # @!macro cached_thread_pool_method_initialize + # @!visibility private + def ns_initialize(opts) + super(opts) @max_queue = 0 - @executor = java.util.concurrent.Executors.newCachedThreadPool + @executor = java.util.concurrent.Executors.newCachedThreadPool @executor.setRejectedExecutionHandler(FALLBACK_POLICY_CLASSES[@fallback_policy].new) @executor.setKeepAliveTime(opts.fetch(:idletime, DEFAULT_THREAD_IDLETIMEOUT), java.util.concurrent.TimeUnit::SECONDS) self.auto_terminate = opts.fetch(:auto_terminate, true) end + else + # @!macro cached_thread_pool_method_initialize + # @!visibility private + def ns_initialize(opts) + super(opts) + end end + end end diff --git a/lib/concurrent/executor/single_thread_executor.rb b/lib/concurrent/executor/single_thread_executor.rb index 125c63eb0..803c246c0 100644 --- a/lib/concurrent/executor/single_thread_executor.rb +++ b/lib/concurrent/executor/single_thread_executor.rb @@ -8,7 +8,7 @@ module Concurrent SingleThreadExecutorImplementation = case when Concurrent.on_jruby? - JavaSingleThreadExecutor + RubySingleThreadExecutor else RubySingleThreadExecutor end diff --git a/lib/concurrent/executor/thread_pool_executor.rb b/lib/concurrent/executor/thread_pool_executor.rb index c0b88eefc..402533097 100644 --- a/lib/concurrent/executor/thread_pool_executor.rb +++ b/lib/concurrent/executor/thread_pool_executor.rb @@ -9,7 +9,7 @@ module Concurrent ThreadPoolExecutorImplementation = case when Concurrent.on_jruby? - JavaThreadPoolExecutor + RubyThreadPoolExecutor else RubyThreadPoolExecutor end @@ -58,9 +58,9 @@ class ThreadPoolExecutor < ThreadPoolExecutorImplementation # @!macro [new] thread_pool_executor_method_initialize # # Create a new thread pool. - # + # # @param [Hash] opts the options which configure the thread pool. - # + # # @option opts [Integer] :max_threads (DEFAULT_MAX_POOL_SIZE) the maximum # number of threads to be created # @option opts [Integer] :min_threads (DEFAULT_MIN_POOL_SIZE) When a new task is submitted @@ -73,12 +73,12 @@ class ThreadPoolExecutor < ThreadPoolExecutorImplementation # @option opts [Symbol] :fallback_policy (:abort) the policy for handling new # tasks that are received when the queue size has reached # `max_queue` or the executor has shut down - # + # # @raise [ArgumentError] if `:max_threads` is less than one # @raise [ArgumentError] if `:min_threads` is less than zero # @raise [ArgumentError] if `:fallback_policy` is not one of the values specified # in `FALLBACK_POLICIES` - # + # # @see http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ThreadPoolExecutor.html # @!method initialize(opts = {}) diff --git a/lib/concurrent/ivar.rb b/lib/concurrent/ivar.rb index 994a620b2..099ed0ba2 100644 --- a/lib/concurrent/ivar.rb +++ b/lib/concurrent/ivar.rb @@ -157,9 +157,8 @@ def ns_initialize(value, opts) self.observers = Collection::CopyOnWriteObserverSet.new set_deref_options(opts) - if value == NULL - @state = :pending - else + @state = :pending + if value != NULL ns_complete_without_notification(true, value, nil) end end diff --git a/lib/concurrent/maybe.rb b/lib/concurrent/maybe.rb index 6014594c4..7ba3d3ebb 100644 --- a/lib/concurrent/maybe.rb +++ b/lib/concurrent/maybe.rb @@ -108,7 +108,7 @@ class Maybe < Synchronization::Object # Indicates that the given attribute has not been set. # When `Just` the {#nothing} getter will return `NONE`. # When `Nothing` the {#just} getter will return `NONE`. - NONE = Object.new.freeze + NONE = ::Object.new.freeze # The value of a `Maybe` when `Just`. Will be `NONE` when `Nothing`. attr_reader :just diff --git a/lib/concurrent/mutable_struct.rb b/lib/concurrent/mutable_struct.rb index 778ed1497..2a92d726a 100644 --- a/lib/concurrent/mutable_struct.rb +++ b/lib/concurrent/mutable_struct.rb @@ -212,6 +212,7 @@ def define_struct(name, members, &block) synchronize do clazz = Synchronization::AbstractStruct.define_struct_class(MutableStruct, Synchronization::LockableObject, name, members, &block) members.each_with_index do |member, index| + clazz.send :remove_method, member clazz.send(:define_method, member) do synchronize { @values[index] } end diff --git a/lib/concurrent/mvar.rb b/lib/concurrent/mvar.rb index 0d18dfac2..9034711bf 100644 --- a/lib/concurrent/mvar.rb +++ b/lib/concurrent/mvar.rb @@ -40,11 +40,11 @@ class MVar < Synchronization::Object safe_initialization! # Unique value that represents that an `MVar` was empty - EMPTY = Object.new + EMPTY = ::Object.new # Unique value that represents that an `MVar` timed out before it was able # to produce a value. - TIMEOUT = Object.new + TIMEOUT = ::Object.new # Create a new `MVar`, either empty or with an initial value. # diff --git a/lib/concurrent/settable_struct.rb b/lib/concurrent/settable_struct.rb index f6feb4559..9706cff2d 100644 --- a/lib/concurrent/settable_struct.rb +++ b/lib/concurrent/settable_struct.rb @@ -107,6 +107,7 @@ def define_struct(name, members, &block) synchronize do clazz = Synchronization::AbstractStruct.define_struct_class(SettableStruct, Synchronization::LockableObject, name, members, &block) members.each_with_index do |member, index| + clazz.send :remove_method, member if clazz.instance_methods.include? member clazz.send(:define_method, member) do synchronize { @values[index] } end diff --git a/lib/concurrent/synchronization/abstract_struct.rb b/lib/concurrent/synchronization/abstract_struct.rb index 1fd66ef9e..868ffbf08 100644 --- a/lib/concurrent/synchronization/abstract_struct.rb +++ b/lib/concurrent/synchronization/abstract_struct.rb @@ -138,13 +138,15 @@ def ns_initialize(*values) end unless name.nil? begin + parent.send :remove_const, name if parent.const_defined? name parent.const_set(name, clazz) - parent.const_get(name) + clazz rescue NameError raise NameError.new("identifier #{name} needs to be constant") end end members.each_with_index do |member, index| + clazz.send :remove_method, member if clazz.instance_methods.include? member clazz.send(:define_method, member) do @values[index] end diff --git a/lib/concurrent/timer_task.rb b/lib/concurrent/timer_task.rb index 81f282127..00956db96 100644 --- a/lib/concurrent/timer_task.rb +++ b/lib/concurrent/timer_task.rb @@ -126,6 +126,7 @@ module Concurrent # task = Concurrent::TimerTask.new(execution_interval: 1, timeout_interval: 1){ 42 } # task.add_observer(TaskObserver.new) # task.execute + # sleep 4 # # #=> (2013-10-13 19:08:58 -0400) Execution successfully returned 42 # #=> (2013-10-13 19:08:59 -0400) Execution successfully returned 42 @@ -189,6 +190,7 @@ class TimerTask < RubyExecutorService def initialize(opts = {}, &task) raise ArgumentError.new('no block given') unless block_given? super + set_deref_options opts end # Is the executor running? @@ -280,6 +282,7 @@ def ns_initialize(opts, &task) @run_now = opts[:now] || opts[:run_now] @executor = Concurrent::SafeTaskExecutor.new(task) @running = Concurrent::AtomicBoolean.new(false) + @value = nil self.observers = Collection::CopyOnNotifyObserverSet.new end diff --git a/lib/concurrent/tvar.rb b/lib/concurrent/tvar.rb index 5e3b8c14d..d5337c98a 100644 --- a/lib/concurrent/tvar.rb +++ b/lib/concurrent/tvar.rb @@ -162,7 +162,7 @@ def leave_transaction class Transaction - ABORTED = Object.new + ABORTED = ::Object.new ReadLogEntry = Struct.new(:tvar, :version) diff --git a/spec/concurrent/actor_spec.rb b/spec/concurrent/actor_spec.rb index 361d37b6d..a60bad825 100644 --- a/spec/concurrent/actor_spec.rb +++ b/spec/concurrent/actor_spec.rb @@ -6,7 +6,7 @@ module Actor # FIXME better tests! - RSpec.describe 'Concurrent::Actor', edge: true, if: !defined?(JRUBY_VERSION) do + RSpec.describe 'Concurrent::Actor', edge: true do def terminate_actors(*actors) actors.each do |actor| @@ -148,7 +148,7 @@ def on_message(message) end end - it 'terminates with all its children', buggy: true do + it 'terminates with all its children', notravis: true do child = subject.ask! :child expect(subject.ask!(:terminated?)).to be_falsey subject.ask(:terminate!).wait @@ -172,7 +172,7 @@ def on_message(message) describe 'message redirecting' do let(:parent) do AdHoc.spawn!(:parent) do - child = AdHoc.spawn!(:child) { -> m { m+1 } } + child = AdHoc.spawn!(:child) { -> m { m + 1 } } -> message do if message == :child child @@ -287,7 +287,7 @@ def on_message(message) test = AdHoc.spawn! name: :tester, behaviour_definition: resuming_behaviour do actor = AdHoc.spawn! name: :pausing, - behaviour_definition: Behaviour.restarting_behaviour_definition do + behaviour_definition: Behaviour.restarting_behaviour_definition do queue << :init -> m { m == :add ? 1 : pass } end @@ -313,7 +313,7 @@ def on_message(message) end describe 'pool' do - it 'supports asks', buggy: true do + it 'supports asks', notravis: true do children = Queue.new pool = Concurrent::Actor::Utils::Pool.spawn! 'pool', 5 do |index| worker = Concurrent::Actor::Utils::AdHoc.spawn! name: "worker-#{index}", supervised: true do diff --git a/spec/concurrent/agent_spec.rb b/spec/concurrent/agent_spec.rb index ac5fe393a..3c6f28422 100644 --- a/spec/concurrent/agent_spec.rb +++ b/spec/concurrent/agent_spec.rb @@ -25,7 +25,7 @@ module Concurrent end it 'defaults the error mode to :continue when an error handler is given' do - subject = Agent.new(42, error_handler: ->(value){ true }) + subject = Agent.new(42, error_handler: ->(value) { true }) expect(subject.error_mode).to eq :continue end @@ -50,36 +50,36 @@ module Concurrent context 'action processing' do specify 'the given block will be passed the current value' do - actual = nil + actual = nil expected = 0 - subject = Agent.new(expected) - subject.send_via(immediate){|value| actual = value } + subject = Agent.new(expected) + subject.send_via(immediate) { |value| actual = value } expect(actual).to eq expected end specify 'the given block will be passed any provided arguments' do - actual = nil + actual = nil expected = [1, 2, 3, 4] - subject = Agent.new(0) - subject.send_via(immediate, *expected){|_, *args| actual = args } + subject = Agent.new(0) + subject.send_via(immediate, *expected) { |_, *args| actual = args } expect(actual).to eq expected end specify 'the return value will be passed to the validator function' do - actual = nil - expected = 42 - validator = ->(new_value){ actual = new_value; true } - subject = Agent.new(0, validator: validator) - subject.send_via(immediate){ expected } + actual = nil + expected = 42 + validator = ->(new_value) { actual = new_value; true } + subject = Agent.new(0, validator: validator) + subject.send_via(immediate) { expected } expect(actual).to eq expected end specify 'upon validation the new value will be set to the block return value' do - actual = nil - expected = 42 - validator = ->(new_value){ true } - subject = Agent.new(0, validator: validator) - subject.send_via(immediate){ expected } + actual = nil + expected = 42 + validator = ->(new_value) { true } + subject = Agent.new(0, validator: validator) + subject.send_via(immediate) { expected } expect(subject.value).to eq expected end @@ -88,15 +88,16 @@ module Concurrent def initialize(bucket) @bucket = bucket end + def update(time, old_value, new_value) @bucket.concat([time, old_value, new_value]) end end - bucket = [] + bucket = [] subject = Agent.new(0) subject.add_observer(observer_class.new(bucket)) - subject.send_via(immediate){ 42 } + subject.send_via(immediate) { 42 } expect(bucket[0]).to be_a Time expect(bucket[1]).to eq 0 @@ -122,58 +123,58 @@ def update(time, old_value, new_value) specify 'when the action raises an error the value will not change' do expected = 0 - subject = Agent.new(expected) - subject.send_via(immediate){ raise StandardError } + subject = Agent.new(expected) + subject.send_via(immediate) { raise StandardError } expect(subject.value).to eq expected end specify 'when the action raises an error the validator will not be called' do validator_called = false - validator = ->(new_value){ validator_called = true } - subject = Agent.new(0, validator: validator) - subject.send_via(immediate){ raise StandareError } + validator = ->(new_value) { validator_called = true } + subject = Agent.new(0, validator: validator) + subject.send_via(immediate) { raise StandareError } expect(validator_called).to be false end specify 'when validation returns false the value will not change' do - expected = 0 - validator = ->(new_value){ false } - subject = Agent.new(0, validator: validator) - subject.send_via(immediate){ 42 } + expected = 0 + validator = ->(new_value) { false } + subject = Agent.new(0, validator: validator) + subject.send_via(immediate) { 42 } expect(subject.value).to eq expected end specify 'when validation raises an error the value will not change' do - expected = 0 - validator = ->(new_value){ raise StandareError } - subject = Agent.new(0, validator: validator) - subject.send_via(immediate){ 42 } + expected = 0 + validator = ->(new_value) { raise StandareError } + subject = Agent.new(0, validator: validator) + subject.send_via(immediate) { 42 } expect(subject.value).to eq expected end specify 'when the action raises an error the handler will be called' do error_handler_called = false - error_handler = ->(agent, exception){ error_handler_called = true } - subject = Agent.new(0, error_handler: error_handler) - subject.send_via(immediate){ raise StandardError } + error_handler = ->(agent, exception) { error_handler_called = true } + subject = Agent.new(0, error_handler: error_handler) + subject.send_via(immediate) { raise StandardError } expect(error_handler_called).to be true end specify 'when validation fails the handler will be called' do error_handler_called = false - error_handler = ->(agent, exception){ error_handler_called = true } - validator = ->(new_value){ false } - subject = Agent.new(0, error_handler: error_handler, validator: validator) - subject.send_via(immediate){ 42 } + error_handler = ->(agent, exception) { error_handler_called = true } + validator = ->(new_value) { false } + subject = Agent.new(0, error_handler: error_handler, validator: validator) + subject.send_via(immediate) { 42 } expect(error_handler_called).to be true end specify 'when validation raises an error the handler will be called' do error_handler_called = false - error_handler = ->(agent, exception){ error_handler_called = true } - validator = ->(new_value){ raise StandardError } - subject = Agent.new(0, error_handler: error_handler, validator: validator) - subject.send_via(immediate){ 42 } + error_handler = ->(agent, exception) { error_handler_called = true } + validator = ->(new_value) { raise StandardError } + subject = Agent.new(0, error_handler: error_handler, validator: validator) + subject.send_via(immediate) { 42 } expect(error_handler_called).to be true end end @@ -181,72 +182,74 @@ def update(time, old_value, new_value) context 'validation' do it 'sets the new value when the validator returns true' do - expected = 42 - validator = ->(new_value){ true } - subject = Agent.new(0, validator: validator) - subject.send_via(immediate){ expected } + expected = 42 + validator = ->(new_value) { true } + subject = Agent.new(0, validator: validator) + subject.send_via(immediate) { expected } expect(subject.value).to eq expected end it 'rejects the new value when the validator returns false' do - expected = 0 - validator = ->(new_value){ false } - subject = Agent.new(expected, validator: validator) - subject.send_via(immediate){ 42 } + expected = 0 + validator = ->(new_value) { false } + subject = Agent.new(expected, validator: validator) + subject.send_via(immediate) { 42 } expect(subject.value).to eq expected end it 'rejects the new value when the validator raises an error' do - expected = 0 - validator = ->(new_value){ raise StandardError } - subject = Agent.new(expected, validator: validator) - subject.send_via(immediate){ 42 } + expected = 0 + validator = ->(new_value) { raise StandardError } + subject = Agent.new(expected, validator: validator) + subject.send_via(immediate) { 42 } expect(subject.value).to eq expected end it 'sets the error when the error mode is :fail and the validator returns false' do - validator = ->(new_value){ false } - subject = Agent.new(0, error_mode: :fail, validator: validator) - subject.send_via(immediate){ 42 } + validator = ->(new_value) { false } + subject = Agent.new(0, error_mode: :fail, validator: validator) + subject.send_via(immediate) { 42 } expect(subject.error).to be_a Agent::ValidationError end it 'sets the error when the error mode is :fail and the validator raises an error' do - validator = ->(new_value){ raise expected } - subject = Agent.new(0, error_mode: :fail, validator: validator) - subject.send_via(immediate){ 42 } + validator = ->(new_value) { raise expected } + subject = Agent.new(0, error_mode: :fail, validator: validator) + subject.send_via(immediate) { 42 } expect(subject.error).to be_a Agent::ValidationError end it 'does not set an error when the error mode is :continue and the validator returns false' do - validator = ->(new_value){ false } - subject = Agent.new(0, error_mode: :continue, validator: validator) - subject.send_via(immediate){ 42 } + validator = ->(new_value) { false } + subject = Agent.new(0, error_mode: :continue, validator: validator) + subject.send_via(immediate) { 42 } expect(subject.error).to be nil end it 'does not set an error when the error mode is :continue and the validator raises an error' do - validator = ->(new_value){ raise StandardError } - subject = Agent.new(0, error_mode: :continue, validator: validator) - subject.send_via(immediate){ 42 } + validator = ->(new_value) { raise StandardError } + subject = Agent.new(0, error_mode: :continue, validator: validator) + subject.send_via(immediate) { 42 } expect(subject.error).to be nil end it 'does not trigger observation when validation fails' do observer_class = Class.new do attr_reader :count + def initialize @count = 0 end + def update(time, old_value, new_value) @count += 1 end end observer = observer_class.new - subject = Agent.new(0, validator: ->(new_value){ false }) + subject = Agent.new(0, validator: ->(new_value) { false }) subject.add_observer(observer) - subject.send_via(immediate){ 42 } + subject.send_via(immediate) { 42 } expect(observer.count).to eq 0 end @@ -255,37 +258,39 @@ def update(time, old_value, new_value) context 'error handling' do specify 'the agent will be passed to the handler' do - actual = nil - error_handler = ->(agent, error){ actual = agent } - subject = Agent.new(0, error_handler: error_handler) - subject.send_via(immediate){ raise StandardError} + actual = nil + error_handler = ->(agent, error) { actual = agent } + subject = Agent.new(0, error_handler: error_handler) + subject.send_via(immediate) { raise StandardError } expect(actual).to eq subject end specify 'the exception will be passed to the handler' do - expected = StandardError.new - actual = nil - error_handler = ->(agent, error){ actual = error } - subject = Agent.new(0, error_handler: error_handler) - subject.send_via(immediate){ raise expected} + expected = StandardError.new + actual = nil + error_handler = ->(agent, error) { actual = error } + subject = Agent.new(0, error_handler: error_handler) + subject.send_via(immediate) { raise expected } expect(actual).to eq expected end specify 'does not trigger observation' do observer_class = Class.new do attr_reader :count + def initialize @count = 0 end + def update(time, old_value, new_value) @count += 1 end end observer = observer_class.new - subject = Agent.new(0) + subject = Agent.new(0) subject.add_observer(observer) - subject.send_via(immediate){ raise StandardError } + subject.send_via(immediate) { raise StandardError } expect(observer.count).to eq 0 end @@ -296,37 +301,37 @@ def update(time, old_value, new_value) context ':continue' do it 'does not set an error when the validator returns false' do - validator = ->(new_value){ false } - subject = Agent.new(0, error_mode: :continue, validator: validator) - subject.send_via(immediate){ 42 } + validator = ->(new_value) { false } + subject = Agent.new(0, error_mode: :continue, validator: validator) + subject.send_via(immediate) { 42 } expect(subject.error).to be nil end it 'does not set an error when the validator raises an error' do - validator = ->(new_value){ raise StandardError } - subject = Agent.new(0, error_mode: :continue, validator: validator) - subject.send_via(immediate){ 42 } + validator = ->(new_value) { raise StandardError } + subject = Agent.new(0, error_mode: :continue, validator: validator) + subject.send_via(immediate) { 42 } expect(subject.error).to be nil end it 'does not set an error when the action raises an error' do subject = Agent.new(0, error_mode: :continue) - subject.send_via(immediate){ raise StandardError } + subject.send_via(immediate) { raise StandardError } expect(subject.error).to be nil end it 'does not block further action processing' do expected = 42 - actual = nil - subject = Agent.new(0, error_mode: :continue) - subject.send_via(immediate){ raise StandardError } - subject.send_via(immediate){ 42 } + actual = nil + subject = Agent.new(0, error_mode: :continue) + subject.send_via(immediate) { raise StandardError } + subject.send_via(immediate) { 42 } expect(subject.value).to eq 42 end it 'sets #failed? to false' do subject = Agent.new(0, error_mode: :continue) - subject.send_via(immediate){ raise StandardError } + subject.send_via(immediate) { raise StandardError } expect(subject).to_not be_failed end end @@ -334,33 +339,33 @@ def update(time, old_value, new_value) context ':fail' do it 'sets the error when the validator returns false' do - validator = ->(new_value){ false } - subject = Agent.new(0, error_mode: :fail, validator: validator) - subject.send_via(immediate){ 42 } + validator = ->(new_value) { false } + subject = Agent.new(0, error_mode: :fail, validator: validator) + subject.send_via(immediate) { 42 } expect(subject.error).to be_a Agent::ValidationError end it 'sets the error when the validator raises an error' do - validator = ->(new_value){ raise expected } - subject = Agent.new(0, error_mode: :fail, validator: validator) - subject.send_via(immediate){ 42 } + validator = ->(new_value) { raise expected } + subject = Agent.new(0, error_mode: :fail, validator: validator) + subject.send_via(immediate) { 42 } expect(subject.error).to be_a Agent::ValidationError end it 'sets the error when the action raises an error' do expected = StandardError.new - subject = Agent.new(0, error_mode: :fail) - subject.send_via(immediate){ raise expected } + subject = Agent.new(0, error_mode: :fail) + subject.send_via(immediate) { raise expected } expect(subject.error).to eq expected end it 'blocks all further action processing until a restart' do - latch = Concurrent::CountDownLatch.new + latch = Concurrent::CountDownLatch.new expected = 42 subject = Agent.new(0, error_mode: :fail) - subject.send_via(immediate){ raise StandardError } - subject.send_via(executor){ latch.count_down; expected } + subject.send_via(immediate) { raise StandardError } + subject.send_via(executor) { latch.count_down; expected } latch.wait(0.1) expect(subject.value).to eq 0 @@ -373,7 +378,7 @@ def update(time, old_value, new_value) it 'sets #failed? to true' do subject = Agent.new(0, error_mode: :fail) - subject.send_via(immediate){ raise StandardError } + subject.send_via(immediate) { raise StandardError } expect(subject).to be_failed end end @@ -382,10 +387,10 @@ def update(time, old_value, new_value) context 'nested actions' do specify 'occur in the order they ar post' do - actual = [] + actual = [] expected = [0, 1, 2, 3, 4] - latch = Concurrent::CountDownLatch.new - subject = Agent.new(0) + latch = Concurrent::CountDownLatch.new + subject = Agent.new(0) subject.send_via(executor, subject) do |v1, a1| a1.send_via(executor, a1) do |v2, a2| @@ -408,9 +413,9 @@ def update(time, old_value, new_value) end specify 'work with immediate execution' do - actual = [] + actual = [] expected = [0, 1, 2] - subject = Agent.new(0) + subject = Agent.new(0) subject.send_via(immediate) do |v1| subject.send_via(immediate) do |v2| @@ -432,25 +437,25 @@ def update(time, old_value, new_value) it 'returns true when the job is post' do subject = Agent.new(0) - expect(subject.send{ nil }).to be true + expect(subject.send { nil }).to be true end it 'returns false when #failed?' do subject = Agent.new(0) allow(subject).to receive(:failed?).and_return(true) - expect(subject.send{ nil }).to be false + expect(subject.send { nil }).to be false end it 'posts to the global fast executor' do - expect(Concurrent.global_fast_executor).to receive(:post).with(any_args).and_call_original subject = Agent.new(0) - subject.send{ nil } + expect(subject).to receive(:enqueue_action_job).with(anything, anything, Concurrent.global_fast_executor).and_call_original + subject.send { nil } end it 'does not wait for the action to process' do job_done = false - subject = Agent.new(0) - subject.send{ sleep(5); job_done = true } + subject = Agent.new(0) + subject.send { sleep(5); job_done = true } expect(job_done).to be false end end @@ -459,27 +464,27 @@ def update(time, old_value, new_value) it 'returns true when the job is post' do subject = Agent.new(0) - expect(subject.send!{ nil }).to be true + expect(subject.send! { nil }).to be true end it 'raises an error when #failed?' do subject = Agent.new(0) allow(subject).to receive(:failed?).and_return(true) expect { - subject.send!{ nil } + subject.send! { nil } }.to raise_error(Agent::Error) end it 'posts to the global fast executor' do - expect(Concurrent.global_fast_executor).to receive(:post).with(any_args).and_call_original subject = Agent.new(0) - subject.send!{ nil } + expect(subject).to receive(:enqueue_action_job).with(anything, anything, Concurrent.global_fast_executor).and_call_original + subject.send! { nil } end it 'does not wait for the action to process' do job_done = false - subject = Agent.new(0) - subject.send!{ sleep(5); job_done = true } + subject = Agent.new(0) + subject.send! { sleep(5); job_done = true } expect(job_done).to be false end end @@ -488,25 +493,25 @@ def update(time, old_value, new_value) it 'returns true when the job is post' do subject = Agent.new(0) - expect(subject.send_off{ nil }).to be true + expect(subject.send_off { nil }).to be true end it 'returns false when #failed?' do subject = Agent.new(0) allow(subject).to receive(:failed?).and_return(true) - expect(subject.send_off{ nil }).to be false + expect(subject.send_off { nil }).to be false end it 'posts to the global io executor' do - expect(Concurrent.global_io_executor).to receive(:post).with(any_args).and_call_original subject = Agent.new(0) - subject.send_off{ nil } + expect(subject).to receive(:enqueue_action_job).with(anything, anything, Concurrent.global_io_executor).and_call_original + subject.send_off { nil } end it 'does not wait for the action to process' do job_done = false - subject = Agent.new(0) - subject.send_off{ sleep(5); job_done = true } + subject = Agent.new(0) + subject.send_off { sleep(5); job_done = true } expect(job_done).to be false end end @@ -515,27 +520,27 @@ def update(time, old_value, new_value) it 'returns true when the job is post' do subject = Agent.new(0) - expect(subject.send_off!{ nil }).to be true + expect(subject.send_off! { nil }).to be true end it 'raises an error when #failed?' do subject = Agent.new(0) allow(subject).to receive(:failed?).and_return(true) expect { - subject.send_off!{ nil } + subject.send_off! { nil } }.to raise_error(Agent::Error) end it 'posts to the global io executor' do - expect(Concurrent.global_io_executor).to receive(:post).with(any_args).and_call_original subject = Agent.new(0) - subject.send_off!{ nil } + expect(subject).to receive(:enqueue_action_job).with(anything, anything, Concurrent.global_io_executor).and_call_original + subject.send_off! { nil } end it 'does not wait for the action to process' do job_done = false - subject = Agent.new(0) - subject.send_off!{ sleep(5); job_done = true } + subject = Agent.new(0) + subject.send_off! { sleep(5); job_done = true } expect(job_done).to be false end end @@ -544,25 +549,25 @@ def update(time, old_value, new_value) it 'returns true when the job is post' do subject = Agent.new(0) - expect(subject.send_via(immediate){ nil }).to be true + expect(subject.send_via(immediate) { nil }).to be true end it 'returns false when #failed?' do subject = Agent.new(0) allow(subject).to receive(:failed?).and_return(true) - expect(subject.send_via(immediate){ nil }).to be false + expect(subject.send_via(immediate) { nil }).to be false end it 'posts to the given executor' do expect(immediate).to receive(:post).with(any_args).and_call_original subject = Agent.new(0) - subject.send_via(immediate){ nil } + subject.send_via(immediate) { nil } end it 'does not wait for the action to process' do job_done = false - subject = Agent.new(0) - subject.send_via(executor){ sleep(5); job_done = true } + subject = Agent.new(0) + subject.send_via(executor) { sleep(5); job_done = true } expect(job_done).to be false end end @@ -571,27 +576,27 @@ def update(time, old_value, new_value) it 'returns true when the job is post' do subject = Agent.new(0) - expect(subject.send_via!(immediate){ nil }).to be true + expect(subject.send_via!(immediate) { nil }).to be true end it 'raises an error when #failed?' do subject = Agent.new(0) allow(subject).to receive(:failed?).and_return(true) expect { - subject.send_via!(immediate){ nil } + subject.send_via!(immediate) { nil } }.to raise_error(Agent::Error) end it 'posts to the given executor' do expect(immediate).to receive(:post).with(any_args).and_call_original subject = Agent.new(0) - subject.send_via!(immediate){ nil } + subject.send_via!(immediate) { nil } end it 'does not wait for the action to process' do job_done = false - subject = Agent.new(0) - subject.send_via!(executor){ sleep(5); job_done = true } + subject = Agent.new(0) + subject.send_via!(executor) { sleep(5); job_done = true } expect(job_done).to be false end end @@ -600,25 +605,25 @@ def update(time, old_value, new_value) it 'returns true when the job is post' do subject = Agent.new(0) - expect(subject.post{ nil }).to be true + expect(subject.post { nil }).to be true end it 'returns false when #failed?' do subject = Agent.new(0) allow(subject).to receive(:failed?).and_return(true) - expect(subject.post{ nil }).to be false + expect(subject.post { nil }).to be false end it 'posts to the global io executor' do - expect(Concurrent.global_io_executor).to receive(:post).with(any_args).and_call_original subject = Agent.new(0) - subject.post{ nil } + expect(subject).to receive(:enqueue_action_job).with(anything, anything, Concurrent.global_io_executor).and_call_original + subject.post { nil } end it 'does not wait for the action to process' do job_done = false - subject = Agent.new(0) - subject.post{ sleep(5); job_done = true } + subject = Agent.new(0) + subject.post { sleep(5); job_done = true } expect(job_done).to be false end end @@ -644,7 +649,7 @@ def update(time, old_value, new_value) it 'does not wait for the action to process' do job_done = false - subject = Agent.new(0) + subject = Agent.new(0) subject << proc { sleep(5); job_done = true } expect(job_done).to be false end @@ -656,8 +661,8 @@ def update(time, old_value, new_value) context 'when #failed?' do it 'raises an error if the new value is not valid' do - subject = Agent.new(0, error_mode: :fail, validator: ->(new_value){ false }) - subject.send_via(immediate){ raise StandardError } + subject = Agent.new(0, error_mode: :fail, validator: ->(new_value) { false }) + subject.send_via(immediate) { raise StandardError } expect { subject.restart(0) @@ -666,7 +671,7 @@ def update(time, old_value, new_value) it 'sets the new value' do subject = Agent.new(0, error_mode: :fail) - subject.send_via(immediate){ raise StandardError } + subject.send_via(immediate) { raise StandardError } subject.restart(42) expect(subject.value).to eq 42 @@ -674,7 +679,7 @@ def update(time, old_value, new_value) it 'clears the error' do subject = Agent.new(0, error_mode: :fail) - subject.send_via(immediate){ raise StandardError } + subject.send_via(immediate) { raise StandardError } subject.restart(42) expect(subject.error).to be nil @@ -682,22 +687,22 @@ def update(time, old_value, new_value) it 'sets #failed? to true' do subject = Agent.new(0, error_mode: :fail) - subject.send_via(immediate){ raise StandardError } + subject.send_via(immediate) { raise StandardError } subject.restart(42) expect(subject).to_not be_failed end it 'removes all actions from the queue when :clear_actions is true' do - latch = Concurrent::CountDownLatch.new + latch = Concurrent::CountDownLatch.new end_latch = Concurrent::CountDownLatch.new - subject = Agent.new(0, error_mode: :fail) + subject = Agent.new(0, error_mode: :fail) - subject.send_via(executor){ latch.wait; raise StandardError } - subject.send_via(executor){ end_latch.count_down } + subject.send_via(executor) { latch.wait; raise StandardError } + subject.send_via(executor) { end_latch.count_down } latch.count_down - 10.times{ break if subject.failed?; sleep(0.1) } + 10.times { break if subject.failed?; sleep(0.1) } subject.restart(42, clear_actions: true) result = end_latch.wait(0.1) @@ -705,15 +710,15 @@ def update(time, old_value, new_value) end it 'does not clear the action queue when :clear_actions is false' do - latch = Concurrent::CountDownLatch.new + latch = Concurrent::CountDownLatch.new end_latch = Concurrent::CountDownLatch.new - subject = Agent.new(0, error_mode: :fail) + subject = Agent.new(0, error_mode: :fail) - subject.send_via(executor){ latch.wait; raise StandardError } - subject.send_via(executor){ end_latch.count_down } + subject.send_via(executor) { latch.wait; raise StandardError } + subject.send_via(executor) { end_latch.count_down } latch.count_down - 10.times{ break if subject.failed?; sleep(0.1) } + 10.times { break if subject.failed?; sleep(0.1) } subject.restart(42, clear_actions: false) result = end_latch.wait(3) @@ -721,15 +726,15 @@ def update(time, old_value, new_value) end it 'does not clear the action queue when :clear_actions is not given' do - latch = Concurrent::CountDownLatch.new + latch = Concurrent::CountDownLatch.new end_latch = Concurrent::CountDownLatch.new - subject = Agent.new(0, error_mode: :fail) + subject = Agent.new(0, error_mode: :fail) - subject.send_via(executor){ latch.wait; raise StandardError } - subject.send_via(executor){ end_latch.count_down } + subject.send_via(executor) { latch.wait; raise StandardError } + subject.send_via(executor) { end_latch.count_down } latch.count_down - 10.times{ break if subject.failed?; sleep(0.1) } + 10.times { break if subject.failed?; sleep(0.1) } subject.restart(42) result = end_latch.wait(3) @@ -737,20 +742,20 @@ def update(time, old_value, new_value) end it 'resumes action processing if actions are enqueued' do - count = 5 - latch = Concurrent::CountDownLatch.new + count = 5 + latch = Concurrent::CountDownLatch.new finish_latch = Concurrent::CountDownLatch.new(5) - subject = Agent.new(0, error_mode: :fail) + subject = Agent.new(0, error_mode: :fail) - subject.send_via(executor){ latch.wait; raise StandardError } - count.times{ subject.send_via(executor){ finish_latch.count_down } } + subject.send_via(executor) { latch.wait; raise StandardError } + count.times { subject.send_via(executor) { finish_latch.count_down } } queue = subject.instance_variable_get(:@queue) - size = queue.size + size = queue.size expect(size).to be > 0 latch.count_down - 10.times{ break if subject.failed?; sleep(0.1) } + 10.times { break if subject.failed?; sleep(0.1) } subject.restart(42, clear_actions: false) expect(finish_latch.wait(5)).to be true @@ -759,18 +764,20 @@ def update(time, old_value, new_value) it 'does not trigger observation' do observer_class = Class.new do attr_reader :count + def initialize @count = 0 end + def update(time, old_value, new_value) @count += 1 end end observer = observer_class.new - subject = Agent.new(0, error_mode: :fail) + subject = Agent.new(0, error_mode: :fail) subject.add_observer(observer) - subject.send_via(immediate){ raise StandardError } + subject.send_via(immediate) { raise StandardError } subject.restart(42) expect(observer.count).to eq 0 @@ -794,15 +801,15 @@ def update(time, old_value, new_value) it 'does not change the value' do expected = 42 - subject = Agent.new(0) - subject.send_via(executor){ sleep(0.1); expected } + subject = Agent.new(0) + subject.send_via(executor) { sleep(0.1); expected } subject.await_for(1) expect(subject.value).to eq expected end it 'does not trigger the error mode' do subject = Agent.new(10) - subject.send{ |x| sleep(0.1); x + 1 } + subject.send { |x| sleep(0.1); x + 1 } subject.await_for(1) expect(subject.value).to eq 11 @@ -813,28 +820,30 @@ def update(time, old_value, new_value) it 'does not trigger observers' do observer_class = Class.new do attr_reader :count + def initialize @count = 0 end + def update(time, old_value, new_value) @count += 1 end end observer = observer_class.new - subject = Agent.new(0) + subject = Agent.new(0) subject.add_observer(observer) - subject.send_via(executor){ sleep(0.1); 42 } + subject.send_via(executor) { sleep(0.1); 42 } subject.await_for(1) expect(observer.count).to eq 1 end it 'waits for nested actions' do - bucket = [] - latch = Concurrent::CountDownLatch.new + bucket = [] + latch = Concurrent::CountDownLatch.new executor = Concurrent::FixedThreadPool.new(3) - subject = Agent.new(0) + subject = Agent.new(0) subject.send_via(executor) do subject.send_via(executor) do @@ -863,34 +872,31 @@ def update(time, old_value, new_value) end it 'does not block on actions from other threads' do - latch = Concurrent::CountDownLatch.new + latch = Concurrent::CountDownLatch.new subject = Agent.new(0) - t = Thread.new do - subject.send_via(executor){ sleep } + in_thread do + subject.send_via(executor) { sleep } latch.count_down end latch.wait(0.1) - ok = subject.await - t.kill - - expect(ok).to be_truthy + expect(subject.await).to be_truthy end it 'blocks indefinitely' do - start = Concurrent.monotonic_time + start = Concurrent.monotonic_time subject = Agent.new(0) - subject.send_via(executor){ sleep(1) } + subject.send_via(executor) { sleep(1) } expect(subject.await).to be_truthy expect(Concurrent.monotonic_time - start).to be > 0.5 end it 'returns true when all prior actions have processed' do - count = 0 + count = 0 expected = 5 - subject = Agent.new(0) - subject.send_via(executor){ sleep(1) } - expected.times{ subject.send_via(executor){ count += 1 } } + subject = Agent.new(0) + subject.send_via(executor) { sleep(1) } + expected.times { subject.send_via(executor) { count += 1 } } subject.await expect(count).to eq expected end @@ -899,17 +905,15 @@ def update(time, old_value, new_value) pending('the timing is nearly impossible'); fail subject = Agent.new(0, error_mode: :fail) - t = Thread.new do - subject.send_via(executor){ sleep(0.1) } - subject.send_via(executor){ raise StandardError } - subject.send_via(executor){ nil } - Thread.new{ subject.restart(42, clear_actions: true) } + t = in_thread do + subject.send_via(executor) { sleep(0.1) } + subject.send_via(executor) { raise StandardError } + subject.send_via(executor) { nil } + in_thread { subject.restart(42, clear_actions: true) } subject.await end thread_status = t.join(0.3) - t.kill - expect(thread_status).to be nil end end @@ -922,31 +926,28 @@ def update(time, old_value, new_value) end it 'does not block on actions from other threads' do - latch = Concurrent::CountDownLatch.new + latch = Concurrent::CountDownLatch.new subject = Agent.new(0) - t = Thread.new do - subject.send_via(executor){ sleep } + in_thread do + subject.send_via(executor) { sleep } latch.count_down end latch.wait(0.1) - ok = subject.await_for(0.1) - t.kill - - expect(ok).to be true + expect(subject.await_for(0.1)).to be true end - it 'returns true when all prior actions have processed', buggy: true do + it 'returns true when all prior actions have processed', notravis: true do subject = Agent.new(0) - subject.send_via(executor){ sleep(1) } - 5.times{ subject.send_via(executor){ nil } } + subject.send_via(executor) { sleep(1) } + 5.times { subject.send_via(executor) { nil } } expect(subject.await_for(10)).to be true end it 'returns false on timeout' do subject = Agent.new(0) - subject.send_via(executor){ sleep(1) } - 5.times{ subject.send_via(executor){ nil } } + subject.send_via(executor) { sleep(1) } + 5.times { subject.send_via(executor) { nil } } expect(subject.await_for(0.1)).to be false end @@ -954,11 +955,11 @@ def update(time, old_value, new_value) pending('the timing is nearly impossible'); fail subject = Agent.new(0, error_mode: :fail) - subject.send_via(executor){ sleep(0.1) } - subject.send_via(executor){ raise StandardError } - subject.send_via(executor){ nil } + subject.send_via(executor) { sleep(0.1) } + subject.send_via(executor) { raise StandardError } + subject.send_via(executor) { nil } - t = Thread.new{ subject.restart(42, clear_actions: true) } + in_thread { subject.restart(42, clear_actions: true) } ok = subject.await_for(0.2) expect(ok).to be false @@ -973,31 +974,28 @@ def update(time, old_value, new_value) end it 'does not block on actions from other threads' do - latch = Concurrent::CountDownLatch.new + latch = Concurrent::CountDownLatch.new subject = Agent.new(0) - t = Thread.new do - subject.send_via(executor){ sleep } + in_thread do + subject.send_via(executor) { sleep } latch.count_down end latch.wait(0.1) - ok = subject.await_for!(0.1) - t.kill - - expect(ok).to be true + expect(subject.await_for!(0.1)).to be true end it 'returns true when all prior actions have processed' do subject = Agent.new(0) - subject.send_via(executor){ sleep(1) } - 5.times{ subject.send_via(executor){ nil } } + subject.send_via(executor) { sleep(1) } + 5.times { subject.send_via(executor) { nil } } expect(subject.await_for!(10)).to be true end it 'raises an error on timeout' do subject = Agent.new(0) - subject.send_via(executor){ sleep(1) } - 5.times{ subject.send_via(executor){ nil } } + subject.send_via(executor) { sleep(1) } + 5.times { subject.send_via(executor) { nil } } expect { subject.await_for!(0.1) }.to raise_error(Concurrent::TimeoutError) @@ -1007,11 +1005,11 @@ def update(time, old_value, new_value) pending('the timing is nearly impossible'); fail subject = Agent.new(0, error_mode: :fail) - subject.send_via(executor){ sleep(0.1) } - subject.send_via(executor){ raise StandardError } - subject.send_via(executor){ nil } + subject.send_via(executor) { sleep(0.1) } + subject.send_via(executor) { raise StandardError } + subject.send_via(executor) { nil } - t = Thread.new{ subject.restart(42, clear_actions: true) } + in_thread { subject.restart(42, clear_actions: true) } expect { subject.await_for!(0.2) @@ -1032,24 +1030,21 @@ def update(time, old_value, new_value) end it 'does not block on actions from other threads' do - latch = Concurrent::CountDownLatch.new + latch = Concurrent::CountDownLatch.new subject = Agent.new(0) - t = Thread.new do - subject.send_via(executor){ sleep } + in_thread do + subject.send_via(executor) { sleep } latch.count_down end latch.wait(0.1) - ok = subject.wait(0.1) - t.kill - - expect(ok).to be true + expect(subject.wait(0.1)).to be true end it 'blocks indefinitely when timeout is nil' do - start = Concurrent.monotonic_time + start = Concurrent.monotonic_time subject = Agent.new(0) - subject.send_via(executor){ sleep(1) } + subject.send_via(executor) { sleep(1) } expect(subject.wait(nil)).to be true expect(Concurrent.monotonic_time - start).to be > 0.5 end @@ -1058,34 +1053,31 @@ def update(time, old_value, new_value) pending('the timing is nearly impossible'); fail subject = Agent.new(0, error_mode: :fail) - t = Thread.new do - subject.send_via(executor){ sleep(0.1) } - subject.send_via(executor){ raise StandardError } - subject.send_via(executor){ nil } - Thread.new{ subject.restart(42, clear_actions: true) } + t = in_thread do + subject.send_via(executor) { sleep(0.1) } + subject.send_via(executor) { raise StandardError } + subject.send_via(executor) { nil } + in_thread { subject.restart(42, clear_actions: true) } subject.wait(nil) end - thread_status = t.join(0.3) - t.kill - - expect(thread_status).to be nil + expect(t.join(0.3)).to be nil end it 'returns true when all prior actions have processed' do - count = 0 + count = 0 expected = 5 - subject = Agent.new(0) - subject.send_via(executor){ sleep(1) } - expected.times{ subject.send_via(executor){ count += 1 } } + subject = Agent.new(0) + subject.send_via(executor) { sleep(1) } + expected.times { subject.send_via(executor) { count += 1 } } subject.wait(nil) expect(count).to eq expected end it 'returns false on timeout' do subject = Agent.new(0) - subject.send_via(executor){ sleep(1) } - 5.times{ subject.send_via(executor){ nil } } + subject.send_via(executor) { sleep(1) } + 5.times { subject.send_via(executor) { nil } } expect(subject.wait(0.1)).to be false end @@ -1093,11 +1085,11 @@ def update(time, old_value, new_value) pending('the timing is nearly impossible'); fail subject = Agent.new(0, error_mode: :fail) - subject.send_via(executor){ sleep(0.1) } - subject.send_via(executor){ raise StandardError } - subject.send_via(executor){ nil } + subject.send_via(executor) { sleep(0.1) } + subject.send_via(executor) { raise StandardError } + subject.send_via(executor) { nil } - t = Thread.new{ subject.restart(42, clear_actions: true) } + in_thread { subject.restart(42, clear_actions: true) } ok = subject.wait(0.2) expect(ok).to be false @@ -1107,10 +1099,10 @@ def update(time, old_value, new_value) context 'with .await' do it 'returns true when all prior actions on all agents have processed' do - latch = Concurrent::CountDownLatch.new - agents = 3.times.collect{ Agent.new(0) } - agents.each{|agent| agent.send_via(executor, latch){|_, l| l.wait(1) } } - Thread.new{ latch.count_down } + latch = Concurrent::CountDownLatch.new + agents = 3.times.collect { Agent.new(0) } + agents.each { |agent| agent.send_via(executor, latch) { |_, l| l.wait(1) } } + in_thread { latch.count_down } ok = Agent.await(*agents) expect(ok).to be true end @@ -1119,23 +1111,23 @@ def update(time, old_value, new_value) context 'with .await_for' do it 'returns true when there are no pending actions' do - agents = 3.times.collect{ Agent.new(0) } - ok = Agent.await_for(1, *agents) + agents = 3.times.collect { Agent.new(0) } + ok = Agent.await_for(1, *agents) expect(ok).to be true end it 'returns true when all prior actions for all agents have processed' do - latch = Concurrent::CountDownLatch.new - agents = 3.times.collect{ Agent.new(0) } - agents.each{|agent| agent.send_via(executor, latch){|_, l| l.wait(1) } } - Thread.new{ latch.count_down } - ok = Agent.await_for(1, *agents) + latch = Concurrent::CountDownLatch.new + agents = 3.times.collect { Agent.new(0) } + agents.each { |agent| agent.send_via(executor, latch) { |_, l| l.wait(1) } } + in_thread { latch.count_down } + ok = Agent.await_for(5, *agents) expect(ok).to be true end it 'returns false on timeout' do - agents = 3.times.collect{ Agent.new(0) } - agents.each{|agent| agent.send_via(executor){ sleep(0.3) } } + agents = 3.times.collect { Agent.new(0) } + agents.each { |agent| agent.send_via(executor) { sleep(0.3) } } ok = Agent.await_for(0.1, *agents) expect(ok).to be false end @@ -1144,23 +1136,23 @@ def update(time, old_value, new_value) context 'with await_for!' do it 'returns true when there are no pending actions' do - agents = 3.times.collect{ Agent.new(0) } - ok = Agent.await_for!(1, *agents) + agents = 3.times.collect { Agent.new(0) } + ok = Agent.await_for!(1, *agents) expect(ok).to be true end it 'returns true when all prior actions for all agents have processed' do - latch = Concurrent::CountDownLatch.new - agents = 3.times.collect{ Agent.new(0) } - agents.each{|agent| agent.send_via(executor, latch){|_, l| l.wait(1) } } - Thread.new{ latch.count_down } - ok = Agent.await_for!(1, *agents) + latch = Concurrent::CountDownLatch.new + agents = 3.times.collect { Agent.new(0) } + agents.each { |agent| agent.send_via(executor, latch) { |_, l| l.wait(1) } } + in_thread { latch.count_down } + ok = Agent.await_for!(5, *agents) expect(ok).to be true end it 'raises an exception on timeout' do - agents = 3.times.collect{ Agent.new(0) } - agents.each{|agent| agent.send_via(executor){ sleep(0.3) } } + agents = 3.times.collect { Agent.new(0) } + agents.each { |agent| agent.send_via(executor) { sleep(0.3) } } expect { Agent.await_for!(0.1, *agents) }.to raise_error(Concurrent::TimeoutError) @@ -1173,7 +1165,7 @@ def update(time, old_value, new_value) subject { Agent.new(0) } def trigger_observable(observable) - observable.send_via(immediate){ 42 } + observable.send_via(immediate) { 42 } end it_behaves_like :observable diff --git a/spec/concurrent/array_spec.rb b/spec/concurrent/array_spec.rb index 5d6c7f72a..83450af89 100644 --- a/spec/concurrent/array_spec.rb +++ b/spec/concurrent/array_spec.rb @@ -3,8 +3,8 @@ module Concurrent let!(:ary) { described_class.new } it 'concurrency' do - (1..THREADS).map do |i| - Thread.new do + (1..Concurrent::ThreadSafe::Test::THREADS).map do |i| + in_thread do 1000.times do ary << i ary.each { |x| x * 2 } diff --git a/spec/concurrent/atom_spec.rb b/spec/concurrent/atom_spec.rb index 826f136c6..e7bfd373b 100644 --- a/spec/concurrent/atom_spec.rb +++ b/spec/concurrent/atom_spec.rb @@ -135,7 +135,7 @@ module Concurrent counter = Concurrent::AtomicFixnum.new(0) atom = Atom.new(0) - t = Thread.new do + t = in_thread do atom.swap do |value| latch1.count_down latch2.wait(1) diff --git a/spec/concurrent/atomic/count_down_latch_spec.rb b/spec/concurrent/atomic/count_down_latch_spec.rb index e74c4a792..b20202adf 100644 --- a/spec/concurrent/atomic/count_down_latch_spec.rb +++ b/spec/concurrent/atomic/count_down_latch_spec.rb @@ -42,6 +42,12 @@ describe '#wait' do + it 'blocks indefinitely' do + # test the thread is kill-able + in_thread(latch) { |l| l.wait } + sleep 0.1 + end + context 'count set to zero' do it 'should return true immediately' do result = zero_count_latch.wait @@ -58,7 +64,7 @@ it 'should block thread until counter is set to zero' do 3.times do - Thread.new { sleep(0.1); latch.count_down } + in_thread { sleep(0.1); latch.count_down } end result = latch.wait @@ -68,13 +74,12 @@ it 'should block until counter is set to zero with timeout' do 3.times do - Thread.new { sleep(0.1); latch.count_down } + in_thread { sleep(0.1); latch.count_down } end result = latch.wait(1) expect(result).to be_truthy expect(latch.count).to eq 0 - end it 'should block until timeout and return false when counter is not set to zero' do @@ -106,10 +111,10 @@ def subject.simulate_spurious_wake_up end it 'should resist to spurious wake ups without timeout' do - latch = Concurrent::CountDownLatch.new(1) + latch = Concurrent::CountDownLatch.new(1) expected = false - t = Thread.new do + t = in_thread do latch.wait(1) subject.wait expected = true @@ -124,11 +129,11 @@ def subject.simulate_spurious_wake_up end it 'should resist to spurious wake ups with timeout' do - start_latch = Concurrent::CountDownLatch.new(1) + start_latch = Concurrent::CountDownLatch.new(1) finish_latch = Concurrent::CountDownLatch.new(1) - expected = false + expected = false - t = Thread.new do + t = in_thread do start_latch.wait(1) subject.wait(0.5) expected = true @@ -148,23 +153,21 @@ def subject.simulate_spurious_wake_up end end - if Concurrent.on_jruby? - - RSpec.describe JavaCountDownLatch do - - it_should_behave_like :count_down_latch - end - end + # if Concurrent.on_jruby? + # RSpec.describe JavaCountDownLatch do + # it_should_behave_like :count_down_latch + # end + # end RSpec.describe CountDownLatch do - if Concurrent.on_jruby? - it 'inherits from JavaCountDownLatch' do - expect(CountDownLatch.ancestors).to include(JavaCountDownLatch) - end - else - it 'inherits from MutexCountDownLatch' do - expect(CountDownLatch.ancestors).to include(MutexCountDownLatch) - end + # if Concurrent.on_jruby? + # it 'inherits from JavaCountDownLatch' do + # expect(CountDownLatch.ancestors).to include(JavaCountDownLatch) + # end + # else + it 'inherits from MutexCountDownLatch' do + expect(CountDownLatch.ancestors).to include(MutexCountDownLatch) end + # end end end diff --git a/spec/concurrent/atomic/cyclic_barrier_spec.rb b/spec/concurrent/atomic/cyclic_barrier_spec.rb index c37f50616..0a553152f 100644 --- a/spec/concurrent/atomic/cyclic_barrier_spec.rb +++ b/spec/concurrent/atomic/cyclic_barrier_spec.rb @@ -37,7 +37,7 @@ module Concurrent context 'with waiting threads' do it 'should be equal to the waiting threads count' do - threads = [Thread.new { barrier.wait }, Thread.new { barrier.wait }] + threads = [in_thread { barrier.wait }, in_thread { barrier.wait }] Thread.pass until threads.all? { |t| t.status == 'sleep' } expect(barrier.number_waiting).to eq 2 end @@ -56,11 +56,11 @@ module Concurrent end describe 'reset' do - it 'should release all waiting threads', buggy: true do + it 'should release all waiting threads', notravis: true do start_latch = CountDownLatch.new(1) continue_latch = CountDownLatch.new(1) - Thread.new do + in_thread do start_latch.count_down barrier.wait continue_latch.count_down @@ -77,7 +77,7 @@ module Concurrent describe '#wait' do context 'without timeout' do it 'should block the thread' do - t = Thread.new { barrier.wait } + t = in_thread { barrier.wait } t.join(0.1) expect(t.status).to eq 'sleep' @@ -86,7 +86,7 @@ module Concurrent it 'should release all threads when their number matches the desired one' do latch = CountDownLatch.new(parties) - parties.times { Thread.new { barrier.wait; latch.count_down } } + parties.times { in_thread { barrier.wait; latch.count_down } } expect(latch.wait(1)).to be_truthy expect(barrier.number_waiting).to eq 0 expect(barrier).not_to be_broken @@ -95,7 +95,7 @@ module Concurrent it 'returns true when released' do latch = CountDownLatch.new(parties) - parties.times { Thread.new { latch.count_down if barrier.wait == true } } + parties.times { in_thread { latch.count_down if barrier.wait == true } } expect(latch.wait(1)).to be_truthy end @@ -105,7 +105,7 @@ module Concurrent latch = CountDownLatch.new(parties) - parties.times { Thread.new { latch.count_down if barrier.wait == true } } + parties.times { in_thread { latch.count_down if barrier.wait == true } } expect(latch.wait(1)).to be_truthy expect(counter.value).to eq 1 @@ -113,17 +113,17 @@ module Concurrent it 'can be reused' do first_latch = CountDownLatch.new(parties) - parties.times { Thread.new { barrier.wait; first_latch.count_down } } + parties.times { in_thread { barrier.wait; first_latch.count_down } } latch = CountDownLatch.new(parties) - parties.times { Thread.new { barrier.wait; latch.count_down } } + parties.times { in_thread { barrier.wait; latch.count_down } } expect(latch.wait(1)).to be_truthy end - it 'return false if barrier has been reset', buggy: true do + it 'return false if barrier has been reset', notravis: true do latch = CountDownLatch.new(1) - t = Thread.new { latch.count_down if barrier.wait == false } + t = in_thread { latch.count_down if barrier.wait == false } t.join(0.1) barrier.reset expect(latch.wait(1)).to be_truthy @@ -133,7 +133,7 @@ module Concurrent context 'with timeout' do context 'timeout not expiring' do it 'should block the thread' do - t = Thread.new { barrier.wait(1) } + t = in_thread { barrier.wait(1) } t.join(0.1) expect(t.status).to eq 'sleep' @@ -142,7 +142,7 @@ module Concurrent it 'should release all threads when their number matches the desired one' do latch = CountDownLatch.new(parties) - parties.times { Thread.new { barrier.wait(1); latch.count_down } } + parties.times { in_thread { barrier.wait(1); latch.count_down } } expect(latch.wait(0.2)).to be_truthy expect(barrier.number_waiting).to eq 0 end @@ -150,7 +150,7 @@ module Concurrent it 'returns true when released' do latch = CountDownLatch.new(parties) - parties.times { Thread.new { latch.count_down if barrier.wait(1) == true } } + parties.times { in_thread { latch.count_down if barrier.wait(1) == true } } expect(latch.wait(1)).to be_truthy end end @@ -160,23 +160,23 @@ module Concurrent it 'returns false' do latch = CountDownLatch.new(1) - Thread.new { latch.count_down if barrier.wait(0.1) == false } + in_thread { latch.count_down if barrier.wait(0.1) == false } expect(latch.wait(1)).to be_truthy end it 'breaks the barrier and release all other threads' do latch = CountDownLatch.new(2) - Thread.new { barrier.wait(0.1); latch.count_down } - Thread.new { barrier.wait; latch.count_down } + in_thread { barrier.wait(0.1); latch.count_down } + in_thread { barrier.wait; latch.count_down } expect(latch.wait(1)).to be_truthy expect(barrier).to be_broken end it 'breaks the barrier and release all other threads 2' do - t1 = Thread.new { barrier.wait(0.1) } - t2 = Thread.new { barrier.wait(0.1) } + t1 = in_thread { barrier.wait(0.1) } + t2 = in_thread { barrier.wait(0.1) } [t1, t2].each(&:join) @@ -196,17 +196,16 @@ module Concurrent context '#broken barrier' do it 'should not accept new threads' do - t = Thread.new { barrier.wait(0.1) } - t.join(0.2) + t = in_thread { barrier.wait(0.01) } + join_with t expect(barrier).to be_broken - expect(barrier.wait).to be_falsey end it 'can be reset' do - t = Thread.new { barrier.wait(0.1) } - t.join(0.2) + t = in_thread { barrier.wait(0.01) } + join_with t expect(barrier).to be_broken @@ -230,7 +229,7 @@ def barrier.simulate_spurious_wake_up it 'should resist to spurious wake ups without timeout' do @expected = false - t = Thread.new { barrier.wait; @expected = true } + t = in_thread { barrier.wait; @expected = true } t.join(0.1) barrier.simulate_spurious_wake_up @@ -241,7 +240,7 @@ def barrier.simulate_spurious_wake_up it 'should resist to spurious wake ups with timeout' do @expected = false - t = Thread.new { barrier.wait(0.5); @expected = true } + t = in_thread { barrier.wait(0.5); @expected = true } t.join(0.1) barrier.simulate_spurious_wake_up diff --git a/spec/concurrent/atomic/event_spec.rb b/spec/concurrent/atomic/event_spec.rb index 94e0870aa..657d7f59f 100644 --- a/spec/concurrent/atomic/event_spec.rb +++ b/spec/concurrent/atomic/event_spec.rb @@ -27,7 +27,7 @@ module Concurrent it 'triggers the event' do latch = CountDownLatch.new(1) - t = Thread.new{ subject.wait.tap{ latch.count_down } } + t = in_thread{ subject.wait.tap{ latch.count_down } } t.join(0.1) subject.set expect(latch.wait(1)).to be true @@ -65,7 +65,7 @@ module Concurrent it 'does not trigger an unset event' do latch = CountDownLatch.new(1) - Thread.new{ subject.wait.tap{ latch.count_down } } + in_thread{ subject.wait.tap{ latch.count_down } } subject.reset expect(latch.wait(0.1)).to be false end @@ -94,7 +94,7 @@ module Concurrent subject.reset latch = CountDownLatch.new(1) subject.set - Thread.new{ subject.wait(1000); latch.count_down } + in_thread{ subject.wait(1000); latch.count_down } expect(latch.wait(0.1)).to be true end @@ -106,16 +106,21 @@ module Concurrent it 'blocks indefinitely when the timer is nil' do subject.reset latch = CountDownLatch.new(1) - Thread.new{ subject.wait.tap{ latch.count_down } } + in_thread{ subject.wait.tap{ latch.count_down } } expect(latch.wait(0.1)).to be false subject.set expect(latch.wait(0.1)).to be true end + it 'blocks indefinitely' do + in_thread{ subject.wait } + sleep 0.1 + end + it 'stops waiting when the timer expires' do subject.reset latch = CountDownLatch.new(1) - Thread.new{ subject.wait(0.2); latch.count_down } + in_thread{ subject.wait(0.2); latch.count_down } expect(latch.wait(0.1)).to be false expect(latch.wait).to be true end @@ -128,17 +133,18 @@ module Concurrent it 'triggers multiple waiting threads' do latch = CountDownLatch.new(5) subject.reset - 5.times{ Thread.new{ subject.wait; latch.count_down } } + 5.times{ in_thread{ subject.wait; latch.count_down } } subject.set expect(latch.wait(0.2)).to be true end it 'behaves appropriately if wait begins while #set is processing' do + subject = subject() subject.reset latch = CountDownLatch.new(5) - 5.times{ Thread.new{ subject.wait(5) } } + 5.times{ in_thread{ subject.wait(5) } } subject.set - 5.times{ Thread.new{ subject.wait; latch.count_down } } + 5.times{ in_thread{ subject.wait; latch.count_down } } expect(latch.wait(0.2)).to be true end end @@ -156,16 +162,16 @@ def subject.simulate_spurious_wake_up it 'should resist to spurious wake ups without timeout' do latch = CountDownLatch.new(1) - t = Thread.new{ subject.wait.tap{ latch.count_down } } + t = in_thread{ subject.wait.tap{ latch.count_down } } t.join(0.1) subject.simulate_spurious_wake_up expect(latch.wait(0.1)).to be false end - it 'should resist spurious wake ups with timeout', buggy: true do + it 'should resist spurious wake ups with timeout', notravis: true do latch = CountDownLatch.new(1) - t = Thread.new{ subject.wait(0.5); latch.count_down } + t = in_thread{ subject.wait(0.5); latch.count_down } t.join(0.1) subject.simulate_spurious_wake_up diff --git a/spec/concurrent/atomic/read_write_lock_spec.rb b/spec/concurrent/atomic/read_write_lock_spec.rb index f693f8daa..3609536ff 100644 --- a/spec/concurrent/atomic/read_write_lock_spec.rb +++ b/spec/concurrent/atomic/read_write_lock_spec.rb @@ -8,7 +8,7 @@ module Concurrent latch_1 = Concurrent::CountDownLatch.new(1) latch_2 = Concurrent::CountDownLatch.new(1) - thread = Thread.new do + in_thread do subject.with_write_lock do latch_1.count_down latch_2.wait(1) @@ -18,7 +18,6 @@ module Concurrent latch_1.wait(1) expect(subject).to be_write_locked latch_2.count_down - thread.join end it 'returns false when the write lock is not held' do @@ -28,7 +27,7 @@ module Concurrent it 'returns false when the write lock is not held but there are readers' do latch = Concurrent::CountDownLatch.new(1) - thread = Thread.new do + in_thread do subject.with_read_lock do latch.wait(1) end @@ -36,7 +35,6 @@ module Concurrent expect(subject).to_not be_write_locked latch.count_down - thread.join end end @@ -49,7 +47,7 @@ module Concurrent it 'returns false when there are readers but no writers' do latch = Concurrent::CountDownLatch.new(1) - thread = Thread.new do + in_thread do subject.with_read_lock do latch.wait(1) end @@ -57,7 +55,6 @@ module Concurrent expect(subject).to_not have_waiters latch.count_down - thread.join end it 'returns true when the write lock is held and there are waiting readers' do @@ -65,7 +62,7 @@ module Concurrent latch_2 = Concurrent::CountDownLatch.new(1) latch_3 = Concurrent::CountDownLatch.new(1) - thread_1 = Thread.new do + in_thread do latch_1.wait(1) subject.acquire_write_lock latch_2.count_down @@ -73,7 +70,7 @@ module Concurrent subject.release_write_lock end - thread_2 = Thread.new do + in_thread do latch_2.wait(1) subject.acquire_read_lock subject.release_read_lock @@ -85,7 +82,6 @@ module Concurrent expect(subject).to have_waiters latch_3.count_down - [thread_1, thread_2].each(&:join) end it 'returns true when the write lock is held and there are waiting writers' do @@ -93,7 +89,7 @@ module Concurrent latch_2 = Concurrent::CountDownLatch.new(1) latch_3 = Concurrent::CountDownLatch.new(1) - thread_1 = Thread.new do + t1 = in_thread do latch_1.wait(1) subject.acquire_write_lock latch_2.count_down @@ -101,7 +97,7 @@ module Concurrent subject.release_write_lock end - thread_2 = Thread.new do + t2 = in_thread do latch_2.wait(1) subject.acquire_write_lock subject.release_write_lock @@ -113,7 +109,8 @@ module Concurrent expect(subject).to have_waiters latch_3.count_down - [thread_1, thread_2].each(&:join) + + join_with [t1, t2] end end @@ -216,7 +213,7 @@ module Concurrent write_flag = Concurrent::AtomicBoolean.new(false) read_flag = Concurrent::AtomicBoolean.new(false) - thread_1 = Thread.new do + thread_1 = in_thread do latch_1.wait(1) subject.acquire_write_lock latch_2.count_down @@ -225,7 +222,7 @@ module Concurrent subject.release_write_lock end - thread_2 = Thread.new do + thread_2 = in_thread do latch_2.wait(1) expect(write_flag.value).to be false latch_3.count_down @@ -253,7 +250,7 @@ module Concurrent read_flag_1 = Concurrent::AtomicBoolean.new(false) read_flag_2 = Concurrent::AtomicBoolean.new(false) - thread_1 = Thread.new do + thread_1 = in_thread do latch_1.wait(1) subject.acquire_read_lock expect(counter.value).to eq 1 @@ -263,7 +260,7 @@ module Concurrent subject.release_read_lock end - thread_2 = Thread.new do + thread_2 = in_thread do latch_2.wait(1) expect(read_flag_1.value).to be false subject.acquire_read_lock @@ -310,7 +307,7 @@ module Concurrent latch_2 = Concurrent::CountDownLatch.new(1) write_flag = Concurrent::AtomicBoolean.new(false) - thread = Thread.new do + thread = in_thread do latch_1.wait(1) latch_2.count_down subject.acquire_write_lock @@ -354,7 +351,7 @@ module Concurrent write_flag_1 = Concurrent::AtomicBoolean.new(false) write_flag_2 = Concurrent::AtomicBoolean.new(false) - thread_1 = Thread.new do + thread_1 = in_thread do latch_1.wait(1) subject.acquire_write_lock latch_2.count_down @@ -363,7 +360,7 @@ module Concurrent subject.release_write_lock end - thread_2 = Thread.new do + thread_2 = in_thread do latch_2.wait(1) expect(write_flag_1.value).to be false latch_3.count_down @@ -388,7 +385,7 @@ module Concurrent read_flag = Concurrent::AtomicBoolean.new(false) write_flag = Concurrent::AtomicBoolean.new(false) - thread_1 = Thread.new do + thread_1 = in_thread do latch_1.wait(1) subject.acquire_read_lock latch_2.count_down @@ -397,7 +394,7 @@ module Concurrent subject.release_read_lock end - thread_2 = Thread.new do + thread_2 = in_thread do latch_2.wait(1) expect(read_flag.value).to be false latch_3.count_down @@ -443,7 +440,7 @@ module Concurrent latch_2 = Concurrent::CountDownLatch.new(1) read_flag = Concurrent::AtomicBoolean.new(false) - thread = Thread.new do + thread = in_thread do latch_1.wait(1) latch_2.count_down subject.acquire_read_lock @@ -465,7 +462,7 @@ module Concurrent latch_2 = Concurrent::CountDownLatch.new(1) write_flag = Concurrent::AtomicBoolean.new(false) - thread = Thread.new do + thread = in_thread do latch_1.wait(1) latch_2.count_down subject.acquire_write_lock diff --git a/spec/concurrent/atomic/reentrant_read_write_lock_spec.rb b/spec/concurrent/atomic/reentrant_read_write_lock_spec.rb index a26a7fdc3..7b0d0f7be 100644 --- a/spec/concurrent/atomic/reentrant_read_write_lock_spec.rb +++ b/spec/concurrent/atomic/reentrant_read_write_lock_spec.rb @@ -98,7 +98,7 @@ def wait_up_to(secs, &condition) Timeout.timeout(3) do got_lock = 10.times.collect { CountDownLatch.new } threads = 10.times.collect do |n| - Thread.new do + in_thread do # Each thread takes the read lock and then waits for another one # They will only finish if ALL of them get their read lock expect(lock.acquire_read_lock).to be true @@ -165,10 +165,10 @@ def wait_up_to(secs, &condition) latch1,latch2 = CountDownLatch.new(3),CountDownLatch.new good = AtomicBoolean.new(false) threads = [ - Thread.new { lock.acquire_read_lock; latch1.count_down; latch2.wait; lock.release_read_lock }, - Thread.new { lock.acquire_read_lock; latch1.count_down; latch2.wait; lock.release_read_lock }, - Thread.new { lock.acquire_read_lock; latch1.count_down; latch2.wait; lock.release_read_lock }, - Thread.new { latch1.wait; lock.acquire_write_lock; good.value = true } + in_thread { lock.acquire_read_lock; latch1.count_down; latch2.wait; lock.release_read_lock }, + in_thread { lock.acquire_read_lock; latch1.count_down; latch2.wait; lock.release_read_lock }, + in_thread { lock.acquire_read_lock; latch1.count_down; latch2.wait; lock.release_read_lock }, + in_thread { latch1.wait; lock.acquire_write_lock; good.value = true } ] wait_up_to(0.2) { threads[3].status == 'sleep' } # The last thread should be waiting to acquire a write lock now... @@ -187,8 +187,8 @@ def wait_up_to(secs, &condition) it "cannot be acquired when another thread holds a write lock" do latch = CountDownLatch.new threads = [ - Thread.new { lock.acquire_write_lock; latch.count_down }, - Thread.new { latch.wait; lock.acquire_write_lock } + in_thread { lock.acquire_write_lock; latch.count_down }, + in_thread { latch.wait; lock.acquire_write_lock } ] expect { Timeout.timeout(1) { threads[0].join }}.not_to raise_error expect(threads[0]).to hold(lock).for_write @@ -200,8 +200,8 @@ def wait_up_to(secs, &condition) it "cannot be acquired when another thread holds a read lock" do latch = CountDownLatch.new threads = [ - Thread.new { lock.acquire_read_lock; latch.count_down }, - Thread.new { latch.wait; lock.acquire_write_lock } + in_thread { lock.acquire_read_lock; latch.count_down }, + in_thread { latch.wait; lock.acquire_write_lock } ] expect { Timeout.timeout(1) { threads[0].join }}.not_to raise_error expect(threads[0]).to hold(lock).for_read @@ -260,14 +260,14 @@ def wait_up_to(secs, &condition) end end - it "wakes up waiting readers when the write lock is released", buggy: true do + it "wakes up waiting readers when the write lock is released", notravis: true do latch1,latch2 = CountDownLatch.new,CountDownLatch.new good = AtomicFixnum.new(0) threads = [ - Thread.new { lock.acquire_write_lock; latch1.count_down; latch2.wait; lock.release_write_lock }, - Thread.new { latch1.wait; lock.acquire_read_lock; good.update { |n| n+1 }}, - Thread.new { latch1.wait; lock.acquire_read_lock; good.update { |n| n+1 }}, - Thread.new { latch1.wait; lock.acquire_read_lock; good.update { |n| n+1 }} + in_thread { lock.acquire_write_lock; latch1.count_down; latch2.wait; lock.release_write_lock }, + in_thread { latch1.wait; lock.acquire_read_lock; good.update { |n| n+1 }}, + in_thread { latch1.wait; lock.acquire_read_lock; good.update { |n| n+1 }}, + in_thread { latch1.wait; lock.acquire_read_lock; good.update { |n| n+1 }} ] wait_up_to(0.2) { threads[3].status == 'sleep' } # The last 3 threads should be waiting to acquire read locks now... @@ -284,8 +284,8 @@ def wait_up_to(secs, &condition) latch1,latch2 = CountDownLatch.new,CountDownLatch.new good = AtomicBoolean.new(false) threads = [ - Thread.new { lock.acquire_write_lock; latch1.count_down; latch2.wait; lock.release_write_lock }, - Thread.new { latch1.wait; lock.acquire_write_lock; good.value = true }, + in_thread { lock.acquire_write_lock; latch1.count_down; latch2.wait; lock.release_write_lock }, + in_thread { latch1.wait; lock.acquire_write_lock; good.value = true }, ] wait_up_to(0.2) { threads[1].status == 'sleep' } # The last thread should be waiting to acquire a write lock now... @@ -337,7 +337,7 @@ def wait_up_to(secs, &condition) it "returns false immediately if read lock cannot be obtained" do Timeout.timeout(3) do latch = CountDownLatch.new - thread = Thread.new { lock.acquire_write_lock; latch.count_down } + in_thread { lock.acquire_write_lock; latch.count_down } latch.wait expect { @@ -350,7 +350,7 @@ def wait_up_to(secs, &condition) it "acquires read lock and returns true if it can do so without blocking" do Timeout.timeout(3) do latch = CountDownLatch.new - thread = Thread.new { lock.acquire_read_lock; latch.count_down } + in_thread { lock.acquire_read_lock; latch.count_down } latch.wait expect { @@ -391,7 +391,7 @@ def wait_up_to(secs, &condition) it "returns false immediately if write lock cannot be obtained" do Timeout.timeout(3) do latch = CountDownLatch.new - thread = Thread.new { lock.acquire_write_lock; latch.count_down } + in_thread { lock.acquire_write_lock; latch.count_down } latch.wait expect { @@ -437,10 +437,9 @@ def wait_up_to(secs, &condition) end it "can survive a torture test" do - latch = CountDownLatch.new count = 0 writers = 5.times.collect do - Thread.new do + in_thread do 500.times do lock.with_write_lock do value = (count += 1) @@ -451,7 +450,7 @@ def wait_up_to(secs, &condition) end end readers = 15.times.collect do - Thread.new do + in_thread do 500.times do lock.with_read_lock { expect(count % 2).to eq 0 } end diff --git a/spec/concurrent/atomic/semaphore_spec.rb b/spec/concurrent/atomic/semaphore_spec.rb index 030aeae29..bb4cf25cd 100644 --- a/spec/concurrent/atomic/semaphore_spec.rb +++ b/spec/concurrent/atomic/semaphore_spec.rb @@ -37,7 +37,7 @@ context 'not enough permits available' do it 'should block thread until permits are available' do semaphore.drain_permits - Thread.new { sleep(0.2); semaphore.release } + in_thread { sleep(0.2); semaphore.release } result = semaphore.acquire expect(result).to be_nil @@ -97,7 +97,7 @@ it 'acquires when permits are available within timeout' do semaphore.drain_permits - Thread.new { sleep 0.1; semaphore.release } + in_thread { sleep 0.1; semaphore.release } result = semaphore.try_acquire(1, 1) expect(result).to be_truthy end diff --git a/spec/concurrent/atomic/thread_local_var_spec.rb b/spec/concurrent/atomic/thread_local_var_spec.rb index 927a7cd22..8778c79dc 100644 --- a/spec/concurrent/atomic/thread_local_var_spec.rb +++ b/spec/concurrent/atomic/thread_local_var_spec.rb @@ -20,8 +20,8 @@ module Concurrent it 'sets the same initial value for all threads' do v = described_class.new(14) - t1 = Thread.new { v.value } - t2 = Thread.new { v.value } + t1 = in_thread { v.value } + t2 = in_thread { v.value } expect(t1.value).to eq 14 expect(t2.value).to eq 14 end @@ -85,8 +85,8 @@ module Concurrent expect(block).to receive(:call).twice v = described_class.new(&block) - Thread.new { v.value }.join - Thread.new { v.value }.join + in_thread { v.value }.join + in_thread { v.value }.join end end end @@ -105,7 +105,7 @@ module Concurrent it 'does not modify the initial value for other threads' do v.value = 2 - t = Thread.new { v.value } + t = in_thread { v.value } expect(t.value).to eq 14 end @@ -115,7 +115,7 @@ module Concurrent b1 = CountDownLatch.new(2) b2 = CountDownLatch.new(2) - t1 = Thread.new do + t1 = in_thread do b1.count_down b1.wait v.value = 1 @@ -124,7 +124,7 @@ module Concurrent v.value end - t2 = Thread.new do + t2 = in_thread do b1.count_down b1.wait v.value = 2 diff --git a/spec/concurrent/channel/buffer/base_shared.rb b/spec/concurrent/channel/buffer/base_shared.rb index c21699629..2a0dde38d 100644 --- a/spec/concurrent/channel/buffer/base_shared.rb +++ b/spec/concurrent/channel/buffer/base_shared.rb @@ -44,7 +44,7 @@ it 'returns true on success' do subject # initialize on this thread - t = Thread.new do + t = in_thread do subject.take end t.join(0.1) @@ -84,7 +84,7 @@ it 'returns the next item immediately if available' do subject # initialize on this thread - t = Thread.new do + t = in_thread do subject.put(42) end t.join(0.1) diff --git a/spec/concurrent/channel/buffer/buffered_shared.rb b/spec/concurrent/channel/buffer/buffered_shared.rb index ba0480eee..065d7bdcc 100644 --- a/spec/concurrent/channel/buffer/buffered_shared.rb +++ b/spec/concurrent/channel/buffer/buffered_shared.rb @@ -32,7 +32,7 @@ end it 'is 0 when there are taking threads but no putting threads' do - t = Thread.new { subject.take } + t = in_thread { subject.take } t.join(0.1) expect(subject.size).to eq 0 t.kill # cleanup @@ -75,7 +75,7 @@ it 'blocks until not empty' do subject # initialize on this thread bucket = Concurrent::AtomicReference.new(nil) - t = Thread.new do + t = in_thread do bucket.value = subject.take end t.join(0.1) @@ -101,7 +101,7 @@ it 'blocks until not empty' do subject # initialize on this thread bucket = Concurrent::AtomicReference.new([]) - t = Thread.new do + t = in_thread do bucket.value = subject.next end t.join(0.1) diff --git a/spec/concurrent/channel/buffer/buffered_spec.rb b/spec/concurrent/channel/buffer/buffered_spec.rb index 90765446c..5eff41aad 100644 --- a/spec/concurrent/channel/buffer/buffered_spec.rb +++ b/spec/concurrent/channel/buffer/buffered_spec.rb @@ -26,7 +26,7 @@ module Concurrent::Channel::Buffer subject = described_class.new(1) subject.put(13) bucket = Concurrent::AtomicReference.new(nil) - t = Thread.new do + t = in_thread do subject.put(42) bucket.value = 42 end diff --git a/spec/concurrent/channel/buffer/timing_buffer_shared.rb b/spec/concurrent/channel/buffer/timing_buffer_shared.rb index 97e0fe11d..efd32361b 100644 --- a/spec/concurrent/channel/buffer/timing_buffer_shared.rb +++ b/spec/concurrent/channel/buffer/timing_buffer_shared.rb @@ -47,7 +47,7 @@ it 'blocks when the timer is not ready' do actual = Concurrent::AtomicBoolean.new(false) subject = described_class.new(10) - t = Thread.new do + t = in_thread do subject.take actual.make_true end @@ -107,7 +107,7 @@ it 'blocks when the timer is not ready' do actual = Concurrent::AtomicBoolean.new(false) subject = described_class.new(10) - t = Thread.new do + t = in_thread do subject.next actual.make_true end diff --git a/spec/concurrent/channel/buffer/unbuffered_spec.rb b/spec/concurrent/channel/buffer/unbuffered_spec.rb index 139beb55c..ba9568158 100644 --- a/spec/concurrent/channel/buffer/unbuffered_spec.rb +++ b/spec/concurrent/channel/buffer/unbuffered_spec.rb @@ -23,14 +23,14 @@ module Concurrent::Channel::Buffer end it 'is 1 when a putting thread is waiting' do - t = Thread.new { subject.put(:foo) } + t = in_thread { subject.put(:foo) } t.join(0.1) expect(subject.size).to eq 1 t.kill # cleanup end it 'is 0 when there are taking threads but no putting threads' do - t = Thread.new { subject.take } + t = in_thread { subject.take } t.join(0.1) expect(subject.size).to eq 0 t.kill # cleanup @@ -44,7 +44,7 @@ module Concurrent::Channel::Buffer end it 'is false when there are waiting putting threads' do - t = Thread.new { subject.put(:foo) } + t = in_thread { subject.put(:foo) } t.join(0.1) expect(subject).to_not be_empty t.kill # cleanup @@ -58,7 +58,7 @@ module Concurrent::Channel::Buffer end it 'is false when there are waiting putting threads' do - t = Thread.new { subject.put(:foo) } + t = in_thread { subject.put(:foo) } t.join(0.1) expect(subject).to be_full t.kill # cleanup @@ -81,7 +81,7 @@ module Concurrent::Channel::Buffer it 'blocks until a thread is ready to take' do subject # initialize on this thread bucket = Concurrent::AtomicReference.new(nil) - t = Thread.new do + t = in_thread do subject.put(42) bucket.value = 42 end @@ -99,7 +99,7 @@ module Concurrent::Channel::Buffer end it 'delivers when closed after put starts' do - t = Thread.new do + t = in_thread do subject.put(:foo) end t.join(0.1) @@ -116,7 +116,7 @@ module Concurrent::Channel::Buffer it 'returns false immediately when a put in in progress' do subject # initialize on this thread - t = Thread.new do + t = in_thread do subject.put(:foo) # block the thread end t.join(0.1) @@ -130,7 +130,7 @@ module Concurrent::Channel::Buffer it 'gives the item to a waiting taker and returns true' do subject # initialize on this thread bucket = Concurrent::AtomicReference.new(nil) - t = Thread.new do + t = in_thread do bucket.value = subject.take end t.join(0.1) @@ -150,7 +150,7 @@ module Concurrent::Channel::Buffer it 'returns false immediately when a put in in progress' do subject # initialize on this thread - t = Thread.new do + t = in_thread do subject.put(:foo) # block the thread end t.join(0.1) @@ -164,7 +164,7 @@ module Concurrent::Channel::Buffer it 'gives the item to a waiting taker and returns true' do subject # initialize on this thread bucket = Concurrent::AtomicReference.new(nil) - t = Thread.new do + t = in_thread do bucket.value = subject.take end t.join(0.1) @@ -185,7 +185,7 @@ module Concurrent::Channel::Buffer it 'blocks when no putting and returns , true when one arrives' do subject # initialize on this thread bucket = Concurrent::AtomicReference.new([]) - t = Thread.new do + t = in_thread do bucket.value = subject.next end t.join(0.1) @@ -204,7 +204,7 @@ module Concurrent::Channel::Buffer it 'returns , true when there are multiple putting' do subject # initialize on this thread threads = 2.times.collect do - Thread.new do + in_thread do subject.put(42) end end @@ -218,7 +218,7 @@ module Concurrent::Channel::Buffer end it 'returns , true when closed and last item' do - t = Thread.new do + t = in_thread do subject.put(:foo) end t.join(0.1) @@ -232,7 +232,7 @@ module Concurrent::Channel::Buffer end it 'returns Concurrent::NULL, false when closed and no items remain' do - t = Thread.new do + t = in_thread do subject.put(:foo) end subject.close diff --git a/spec/concurrent/channel_spec.rb b/spec/concurrent/channel_spec.rb index 339f98588..62bc69efc 100644 --- a/spec/concurrent/channel_spec.rb +++ b/spec/concurrent/channel_spec.rb @@ -1,6 +1,6 @@ module Concurrent - RSpec.describe Channel, edge: true, buggy: true do + RSpec.describe Channel, edge: true, notravis: true do context 'initialization' do @@ -464,7 +464,7 @@ module Concurrent it 'returns the next item immediately if available' do subject # initialize on this thread - t = Thread.new do + t = in_thread do subject.put(42) end t.join(0.1) @@ -486,7 +486,7 @@ module Concurrent it 'returns the next item immediately if available' do subject # initialize on this thread - t = Thread.new do + t = in_thread do subject.put(42) end t.join(0.1) @@ -510,9 +510,9 @@ module Concurrent context '#poll?' do - it 'returns a just Maybe immediately if available', buggy: true do + it 'returns a just Maybe immediately if available', notravis: true do subject # initialize on this thread - t = Thread.new do + t = in_thread do subject.put(42) end t.join(0.1) @@ -611,7 +611,7 @@ module Concurrent latch.wait(10) expect(actual).to eq expected end - end + end context '.go_loop_via' do diff --git a/spec/concurrent/concern/obligation_spec.rb b/spec/concurrent/concern/obligation_spec.rb index 0aca8fa7e..f0444ef20 100644 --- a/spec/concurrent/concern/obligation_spec.rb +++ b/spec/concurrent/concern/obligation_spec.rb @@ -4,11 +4,15 @@ module Concern RSpec.describe Obligation do let (:obligation_class) do - Class.new(Synchronization::LockableObject) do include Obligation public :state=, :compare_and_set_state, :if_state attr_writer :value, :reason + def initialize + super + set_deref_options + init_obligation + end end end @@ -69,8 +73,7 @@ module Concern context 'fulfilled' do before(:each) do - obligation.state = :fulfilled - obligation.send(:value=, 42) + obligation.send :set_state, true, 42, nil allow(obligation).to receive(:event).and_return(event) end @@ -147,7 +150,7 @@ module Concern context 'rejected' do before(:each) do - obligation.state = :rejected + obligation.send :set_state, false, nil, (raise rescue $!) allow(obligation).to receive(:event).and_return(event) end @@ -187,19 +190,19 @@ module Concern it 'should return immediately if timeout is zero' do expect(event).not_to receive(:wait) - expect { obligation.value!(0) }.to raise_error + expect { obligation.value!(0) }.to raise_error StandardError end it 'should return immediately if timeout is not set' do expect(event).not_to receive(:wait) - expect { obligation.value! }.to raise_error + expect { obligation.value! }.to raise_error StandardError end it 'should return immediately if timeout is not zero' do expect(event).not_to receive(:wait) - expect { obligation.value!(5) }.to raise_error + expect { obligation.value!(5) }.to raise_error StandardError end end @@ -209,19 +212,19 @@ module Concern it 'should return immediately if timeout is zero' do expect(event).not_to receive(:wait) - expect { obligation.no_error!(0) }.to raise_error + expect { obligation.no_error!(0) }.to raise_error StandardError end it 'should return immediately if timeout is not set' do expect(event).not_to receive(:wait) - expect { obligation.no_error! }.to raise_error + expect { obligation.no_error! }.to raise_error StandardError end it 'should return immediately if timeout is not zero' do expect(event).not_to receive(:wait) - expect { obligation.no_error!(5) }.to raise_error + expect { obligation.no_error!(5) }.to raise_error StandardError end end diff --git a/spec/concurrent/configuration_spec.rb b/spec/concurrent/configuration_spec.rb index 76086bf5e..f58d16ce8 100644 --- a/spec/concurrent/configuration_spec.rb +++ b/spec/concurrent/configuration_spec.rb @@ -2,14 +2,6 @@ module Concurrent RSpec.describe 'configuration', notravis: true do - before(:all) do - reset_gem_configuration - end - - after(:each) do - reset_gem_configuration - end - context 'global executors' do it 'creates a global timer set' do @@ -29,9 +21,9 @@ module Concurrent specify 'Concurrent::AtExit.run acts on all executors with auto_terminate: true' do # The 'at_least(:once)' clauses account for global config reset - expect(Concurrent.global_fast_executor).to receive(:kill).at_least(:once).with(no_args).and_call_original - expect(Concurrent.global_io_executor).to receive(:kill).at_least(:once).with(no_args).and_call_original - expect(Concurrent.global_timer_set).to receive(:kill).at_least(:once).with(no_args).and_call_original + expect(Concurrent.global_fast_executor).to receive(:kill).at_least(:once).with(no_args) + expect(Concurrent.global_io_executor).to receive(:kill).at_least(:once).with(no_args) + expect(Concurrent.global_timer_set).to receive(:kill).at_least(:once).with(no_args) Concurrent::AtExit.run end end diff --git a/spec/concurrent/edge/lock_free_linked_set_spec.rb b/spec/concurrent/edge/lock_free_linked_set_spec.rb index 7d5bd2484..5e0c40a9f 100644 --- a/spec/concurrent/edge/lock_free_linked_set_spec.rb +++ b/spec/concurrent/edge/lock_free_linked_set_spec.rb @@ -19,12 +19,12 @@ expect(subject.add 'test string1').to be true end - context 'in a multi-threaded environment', buggy: true do + context 'in a multi-threaded environment', notravis: true do it 'adds the items to the set' do to_insert = %w(one two three four five six) threads = ::Array.new(16) do - Thread.new do + in_thread do to_insert.each do |item| subject.add item end @@ -85,14 +85,14 @@ end end - context 'in a multi-threaded environment', buggy: true do + context 'in a multi-threaded environment', notravis: true do it 'correctly check that the set contains the item' do to_insert = %w(one two three four five six) to_insert.each { |item| subject << item } threads = ::Array.new(16) do - Thread.new do - 100.times { subject << SecureRandom.hex } + in_thread do + 100.times { subject << SecureRandom.hex } to_insert.each do |item| expect(subject.contains? item).to be true @@ -127,19 +127,19 @@ end end - context 'in a multi-threaded environment', buggy: true do + context 'in a multi-threaded environment', notravis: true do it 'adds the items to the set' do to_insert = %w(one two three four five six) to_insert.each { |item| subject << item } threads = ::Array.new(8) do - Thread.new { subject.remove 'one' } - Thread.new { subject.remove 'two' } - Thread.new { subject.remove 'three' } + [in_thread { subject.remove 'one' }, + in_thread { subject.remove 'two' }, + in_thread { subject.remove 'three' }] end - threads.each(&:join) + threads.flatten.each(&:join) expect(subject.contains? 'one').to be false expect(subject.contains? 'two').to be false @@ -153,9 +153,9 @@ to_insert = %w(one two three four five six) to_insert.each { |item| subject << item } - threads = ::Array.new(16) do - Thread.new do - 100.times { subject << SecureRandom.hex } + ::Array.new(16) do + in_thread do + 100.times { subject << SecureRandom.hex } to_insert.each do |item| subject.remove item @@ -163,8 +163,6 @@ end end end - - threads.map(&:join) end end diff --git a/spec/concurrent/edge/promises_spec.rb b/spec/concurrent/edge/promises_spec.rb index e575dfb59..7fefe8842 100644 --- a/spec/concurrent/edge/promises_spec.rb +++ b/spec/concurrent/edge/promises_spec.rb @@ -287,7 +287,7 @@ def behaves_as_delay(delay, value) end event_or_future.wait - Array.new(event_or_future.is_a?(Concurrent::Promises::Future) ? 12 : 6) { queue.pop } + ::Array.new(event_or_future.is_a?(Concurrent::Promises::Future) ? 12 : 6) { queue.pop } end callback_results = callbacks_tester.call(fulfilled_future(:v)) @@ -442,7 +442,7 @@ def behaves_as_delay(delay, value) it 'propagates requests for values to delayed futures' do expect(future { delay { 1 } }.flat.value!(0.1)).to eq 1 - expect(Array.new(3) { |i| Concurrent::Promises.delay { i } }. + expect(::Array.new(3) { |i| Concurrent::Promises.delay { i } }. inject { |a, b| a.then { b }.flat }.value!(0.2)).to eq 2 end end @@ -502,7 +502,7 @@ def behaves_as_delay(delay, value) specify do source, token = Concurrent::Cancellation.create - futures = Array.new(2) { future(token) { |t| t.loop_until_canceled { Thread.pass }; :done } } + futures = ::Array.new(2) { future(token) { |t| t.loop_until_canceled { Thread.pass }; :done } } source.cancel futures.each do |future| diff --git a/spec/concurrent/exchanger_spec.rb b/spec/concurrent/exchanger_spec.rb index 575d5dfc2..73b874079 100644 --- a/spec/concurrent/exchanger_spec.rb +++ b/spec/concurrent/exchanger_spec.rb @@ -8,59 +8,49 @@ latch_1 = Concurrent::CountDownLatch.new latch_2 = Concurrent::CountDownLatch.new - t = Thread.new do + in_thread do latch_1.count_down - subject.send(method, 1) + subject.send(method_name, 1) latch_2.count_down end latch_1.wait(1) latch_2.wait(0.1) + expect(latch_1.count).to eq 0 expect(latch_2.count).to eq 1 - t.kill end it 'receives the other value' do - first_value = nil + first_value = nil second_value = nil - latch = Concurrent::CountDownLatch.new(2) + latch = Concurrent::CountDownLatch.new(2) - threads = [ - Thread.new { first_value = subject.send(method, 2); latch.count_down }, - Thread.new { second_value = subject.send(method, 4); latch.count_down } - ] + in_thread { first_value = subject.send(method_name, 2); latch.count_down } + in_thread { second_value = subject.send(method_name, 4); latch.count_down } latch.wait(1) expect(get_value(first_value)).to eq 4 expect(get_value(second_value)).to eq 2 - - threads.each {|t| t.kill } end it 'can be reused' do - first_value = nil + first_value = nil second_value = nil - latch_1 = Concurrent::CountDownLatch.new(2) - latch_2 = Concurrent::CountDownLatch.new(2) + latch_1 = Concurrent::CountDownLatch.new(2) + latch_2 = Concurrent::CountDownLatch.new(2) - threads = [ - Thread.new { first_value = subject.send(method, 1); latch_1.count_down }, - Thread.new { second_value = subject.send(method, 0); latch_1.count_down } - ] + in_thread { first_value = subject.send(method_name, 1); latch_1.count_down } + in_thread { second_value = subject.send(method_name, 0); latch_1.count_down } latch_1.wait(1) - threads.each {|t| t.kill } - threads = [ - Thread.new { first_value = subject.send(method, 10); latch_2.count_down }, - Thread.new { second_value = subject.send(method, 12); latch_2.count_down } - ] + in_thread { first_value = subject.send(method_name, 10); latch_2.count_down } + in_thread { second_value = subject.send(method_name, 12); latch_2.count_down } latch_2.wait(1) expect(get_value(first_value)).to eq 12 expect(get_value(second_value)).to eq 10 - threads.each {|t| t.kill } end end @@ -69,7 +59,7 @@ it 'blocks until timeout' do duration = Concurrent::TestHelpers.monotonic_interval do begin - subject.send(method, 2, 0.1) + subject.send(method_name, 2, 0.1) rescue Concurrent::TimeoutError # do nothing end @@ -78,82 +68,71 @@ end it 'receives the other value' do - first_value = nil + first_value = nil second_value = nil - latch = Concurrent::CountDownLatch.new(2) + latch = Concurrent::CountDownLatch.new(2) - threads = [ - Thread.new { first_value = subject.send(method, 2, 1); latch.count_down }, - Thread.new { second_value = subject.send(method, 4, 1); latch.count_down } - ] + in_thread { first_value = subject.send(method_name, 2, 1); latch.count_down } + in_thread { second_value = subject.send(method_name, 4, 1); latch.count_down } latch.wait(1) expect(get_value(first_value)).to eq 4 expect(get_value(second_value)).to eq 2 - - threads.each {|t| t.kill } end it 'can be reused' do - first_value = nil + first_value = nil second_value = nil - latch_1 = Concurrent::CountDownLatch.new(2) - latch_2 = Concurrent::CountDownLatch.new(2) + latch_1 = Concurrent::CountDownLatch.new(2) + latch_2 = Concurrent::CountDownLatch.new(2) - threads = [ - Thread.new { first_value = subject.send(method, 1, 1); latch_1.count_down }, - Thread.new { second_value = subject.send(method, 0, 1); latch_1.count_down } - ] + in_thread { first_value = subject.send(method_name, 1, 1); latch_1.count_down } + in_thread { second_value = subject.send(method_name, 0, 1); latch_1.count_down } latch_1.wait(1) - threads.each {|t| t.kill } - threads = [ - Thread.new { first_value = subject.send(method, 10, 1); latch_2.count_down }, - Thread.new { second_value = subject.send(method, 12, 1); latch_2.count_down } - ] + in_thread { first_value = subject.send(method_name, 10, 1); latch_2.count_down } + in_thread { second_value = subject.send(method_name, 12, 1); latch_2.count_down } + latch_2.wait(1) expect(get_value(first_value)).to eq 12 expect(get_value(second_value)).to eq 10 - threads.each {|t| t.kill } end end RSpec.shared_examples 'exchanger method cross-thread interactions' do it 'when first, waits for a second' do - first_value = nil + first_value = nil second_value = nil - latch = Concurrent::CountDownLatch.new(1) + latch = Concurrent::CountDownLatch.new(1) - t1 = Thread.new do - first_value = subject.send(method, :foo, 1) + t1 = in_thread do + first_value = subject.send(method_name, :foo, 1) latch.count_down end t1.join(0.1) - second_value = subject.send(method, :bar, 0) + second_value = subject.send(method_name, :bar, 0) latch.wait(1) expect(get_value(first_value)).to eq :bar expect(get_value(second_value)).to eq :foo - - t1.kill end - it 'allows multiple firsts to cancel if necessary', buggy: true do - first_value = nil - second_value = nil - cancels = 3 - cancel_latch = Concurrent::CountDownLatch.new(cancels) + it 'allows multiple firsts to cancel if necessary', notravis: true do + first_value = nil + second_value = nil + cancels = 3 + cancel_latch = Concurrent::CountDownLatch.new(cancels) success_latch = Concurrent::CountDownLatch.new(1) threads = cancels.times.collect do - Thread.new do + in_thread do begin - first_value = subject.send(method, :foo, 0.1) + first_value = subject.send(method_name, :foo, 0.1) rescue Concurrent::TimeoutError # suppress ensure @@ -162,47 +141,56 @@ end end - threads.each {|t| t.join(1) } + threads.each { |t| t.join(1) } cancel_latch.wait(1) - t1 = Thread.new do - first_value = subject.send(method, :bar, 1) + t1 = in_thread do + first_value = subject.send(method_name, :bar, 1) success_latch.count_down end t1.join(0.1) - second_value = subject.send(method, :baz, 0) + second_value = subject.send(method_name, :baz, 0) success_latch.wait(1) expect(get_value(first_value)).to eq :baz expect(get_value(second_value)).to eq :bar - - t1.kill - threads.each {|t| t.kill } end end RSpec.shared_examples :exchanger do context '#exchange' do - let!(:method) { :exchange } - def get_value(result) result end + let!(:method_name) { :exchange } + + def get_value(result) + result + end + it_behaves_like 'exchanger method with indefinite timeout' it_behaves_like 'exchanger method with finite timeout' it_behaves_like 'exchanger method cross-thread interactions' end context '#exchange!' do - let!(:method) { :exchange! } - def get_value(result) result end + let!(:method_name) { :exchange! } + + def get_value(result) + result + end + it_behaves_like 'exchanger method with indefinite timeout' it_behaves_like 'exchanger method with finite timeout' it_behaves_like 'exchanger method cross-thread interactions' end context '#try_exchange' do - let!(:method) { :try_exchange } - def get_value(result) result.value end + let!(:method_name) { :try_exchange } + + def get_value(result) + result.value + end + it_behaves_like 'exchanger method with indefinite timeout' it_behaves_like 'exchanger method with finite timeout' it_behaves_like 'exchanger method cross-thread interactions' @@ -218,16 +206,16 @@ module Concurrent if Concurrent.on_cruby? specify 'stress test', notravis: true do - thread_count = 100 + thread_count = 100 exchange_count = 100 - latch = Concurrent::CountDownLatch.new(thread_count) + latch = Concurrent::CountDownLatch.new(thread_count) good = Concurrent::AtomicFixnum.new(0) - bad = Concurrent::AtomicFixnum.new(0) + bad = Concurrent::AtomicFixnum.new(0) ugly = Concurrent::AtomicFixnum.new(0) - threads = thread_count.times.collect do |i| - Thread.new do + thread_count.times.collect do |i| + in_thread do exchange_count.times do |j| begin result = subject.exchange!(i, 1) @@ -245,32 +233,30 @@ module Concurrent puts "Good: #{good.value}, Bad (timeout): #{bad.value}, Ugly: #{ugly.value}" expect(good.value + bad.value + ugly.value).to eq thread_count * exchange_count expect(ugly.value).to eq 0 - - threads.each {|t| t.kill } end end end - if defined? JavaExchanger - - RSpec.describe JavaExchanger do - it_behaves_like :exchanger - end - end + # if defined? JavaExchanger + # + # RSpec.describe JavaExchanger do + # it_behaves_like :exchanger + # end + # end RSpec.describe Exchanger do - context 'class hierarchy' do + context 'class hierarchy' do - if Concurrent.on_jruby? - it 'inherits from JavaExchanger' do - expect(Exchanger.ancestors).to include(JavaExchanger) - end - else - it 'inherits from RubyExchanger' do - expect(Exchanger.ancestors).to include(RubyExchanger) - end + # if Concurrent.on_jruby? + # it 'inherits from JavaExchanger' do + # expect(Exchanger.ancestors).to include(JavaExchanger) + # end + # else + it 'inherits from RubyExchanger' do + expect(Exchanger.ancestors).to include(RubyExchanger) end + # end end end end diff --git a/spec/concurrent/executor/cached_thread_pool_spec.rb b/spec/concurrent/executor/cached_thread_pool_spec.rb index 712d21d82..609cb08a8 100644 --- a/spec/concurrent/executor/cached_thread_pool_spec.rb +++ b/spec/concurrent/executor/cached_thread_pool_spec.rb @@ -116,122 +116,122 @@ module Concurrent context 'runtime-specific implementation' do - if Concurrent.on_jruby? - - context '#initialize' do - - it 'sets :fallback_policy correctly' do - clazz = java.util.concurrent.ThreadPoolExecutor::DiscardPolicy - policy = clazz.new - expect(clazz).to receive(:new).at_least(:once).with(any_args).and_return(policy) - - subject = CachedThreadPool.new(fallback_policy: :discard) - expect(subject.fallback_policy).to eq :discard - end - - it 'defaults :fallback_policy to :abort' do - subject = CachedThreadPool.new - expect(subject.fallback_policy).to eq :abort - end - - it 'raises an exception if given an invalid :fallback_policy' do - expect { - CachedThreadPool.new(fallback_policy: :bogus) - }.to raise_error(ArgumentError) - end + # if Concurrent.on_jruby? + # + # context '#initialize' do + # + # it 'sets :fallback_policy correctly' do + # clazz = java.util.concurrent.ThreadPoolExecutor::DiscardPolicy + # policy = clazz.new + # expect(clazz).to receive(:new).at_least(:once).with(any_args).and_return(policy) + # + # subject = CachedThreadPool.new(fallback_policy: :discard) + # expect(subject.fallback_policy).to eq :discard + # end + # + # it 'defaults :fallback_policy to :abort' do + # subject = CachedThreadPool.new + # expect(subject.fallback_policy).to eq :abort + # end + # + # it 'raises an exception if given an invalid :fallback_policy' do + # expect { + # CachedThreadPool.new(fallback_policy: :bogus) + # }.to raise_error(ArgumentError) + # end + # end + # + # else + + context 'garbage collection' do + + subject { described_class.new(idletime: 0.1, max_threads: 2, gc_interval: 0) } + + it 'removes from pool any thread that has been idle too long' do + latch = Concurrent::CountDownLatch.new(4) + 4.times { subject.post { sleep 0.1; latch.count_down } } + expect(latch.wait(1)).to be true + sleep 0.2 + subject.post {} + sleep 0.2 + expect(subject.length).to be < 4 end - else - - context 'garbage collection' do - - subject { described_class.new(idletime: 0.1, max_threads: 2, gc_interval: 0) } + it 'deals with dead threads' do + expect(subject).to receive(:ns_worker_died).exactly(5).times.and_call_original - it 'removes from pool any thread that has been idle too long' do - latch = Concurrent::CountDownLatch.new(4) - 4.times { subject.post { sleep 0.1; latch.count_down } } - expect(latch.wait(1)).to be true - sleep 0.2 - subject.post {} - sleep 0.2 - expect(subject.length).to be < 4 - end - - it 'deals with dead threads' do - expect(subject).to receive(:ns_worker_died).exactly(5).times.and_call_original - - dead_threads_queue = Queue.new - 5.times { subject.post { sleep 0.1; dead_threads_queue.push Thread.current; raise Exception } } - sleep(0.2) - latch = Concurrent::CountDownLatch.new(5) - 5.times { subject.post { sleep 0.1; latch.count_down } } - expect(latch.wait(1)).to be true + dead_threads_queue = Queue.new + 5.times { subject.post { sleep 0.1; dead_threads_queue.push Thread.current; raise Exception } } + sleep(0.2) + latch = Concurrent::CountDownLatch.new(5) + 5.times { subject.post { sleep 0.1; latch.count_down } } + expect(latch.wait(1)).to be true - dead_threads = [] - dead_threads << dead_threads_queue.pop until dead_threads_queue.empty? - expect(dead_threads.all? { |t| !t.alive? }).to be true - end + dead_threads = [] + dead_threads << dead_threads_queue.pop until dead_threads_queue.empty? + expect(dead_threads.all? { |t| !t.alive? }).to be true end + end - context 'worker creation and caching' do + context 'worker creation and caching' do - subject { described_class.new(idletime: 1, max_threads: 5) } + subject { described_class.new(idletime: 1, max_threads: 5) } - it 'creates new workers when there are none available' do - expect(subject.length).to eq 0 - 5.times { sleep(0.1); subject << proc { sleep(1) } } - sleep(1) - expect(subject.length).to eq 5 - end + it 'creates new workers when there are none available' do + expect(subject.length).to eq 0 + 5.times { sleep(0.1); subject << proc { sleep(1) } } + sleep(1) + expect(subject.length).to eq 5 + end - it 'uses existing idle threads' do - 5.times { subject << proc { sleep(0.1) } } - sleep(1) - expect(subject.length).to be >= 5 - 3.times { subject << proc { sleep(1) } } - sleep(0.1) - expect(subject.length).to be >= 5 - end + it 'uses existing idle threads' do + 5.times { subject << proc { sleep(0.1) } } + sleep(1) + expect(subject.length).to be >= 5 + 3.times { subject << proc { sleep(1) } } + sleep(0.1) + expect(subject.length).to be >= 5 end end + end - context 'stress', notravis: true do - configurations = [ - { min_threads: 2, - max_threads: ThreadPoolExecutor::DEFAULT_MAX_POOL_SIZE, - auto_terminate: false, - idletime: 0.1, # 1 minute - max_queue: 0, # unlimited - fallback_policy: :caller_runs, # shouldn't matter -- 0 max queue - gc_interval: 0.1 }, - { min_threads: 2, - max_threads: 4, - auto_terminate: false, - idletime: 0.1, # 1 minute - max_queue: 0, # unlimited - fallback_policy: :caller_runs, # shouldn't matter -- 0 max queue - gc_interval: 0.1 } - ] - - configurations.each do |config| - specify do - pool = RubyThreadPoolExecutor.new(config) - - 10.times do - count = Concurrent::CountDownLatch.new(100) - 100.times do - pool.post { count.count_down } - end - count.wait - sleep 0.01 # let the tasks end after count_down - expect(pool.length).to be <= [200, config[:max_threads]].min - if pool.length > [110, config[:max_threads]].min - puts "ERRORSIZE #{pool.length} max #{config[:max_threads]}" - end + context 'stress', notravis: true do + configurations = [ + { min_threads: 2, + max_threads: ThreadPoolExecutor::DEFAULT_MAX_POOL_SIZE, + auto_terminate: false, + idletime: 0.1, # 1 minute + max_queue: 0, # unlimited + fallback_policy: :caller_runs, # shouldn't matter -- 0 max queue + gc_interval: 0.1 }, + { min_threads: 2, + max_threads: 4, + auto_terminate: false, + idletime: 0.1, # 1 minute + max_queue: 0, # unlimited + fallback_policy: :caller_runs, # shouldn't matter -- 0 max queue + gc_interval: 0.1 } + ] + + configurations.each do |config| + specify do + pool = RubyThreadPoolExecutor.new(config) + + 10.times do + count = Concurrent::CountDownLatch.new(100) + 100.times do + pool.post { count.count_down } + end + count.wait + sleep 0.01 # let the tasks end after count_down + expect(pool.length).to be <= [200, config[:max_threads]].min + if pool.length > [110, config[:max_threads]].min + puts "ERRORSIZE #{pool.length} max #{config[:max_threads]}" end end end end + # end end end end diff --git a/spec/concurrent/executor/fixed_thread_pool_spec.rb b/spec/concurrent/executor/fixed_thread_pool_spec.rb index 9ae9c128a..99661688c 100644 --- a/spec/concurrent/executor/fixed_thread_pool_spec.rb +++ b/spec/concurrent/executor/fixed_thread_pool_spec.rb @@ -259,41 +259,41 @@ module Concurrent context 'runtime-specific implementation' do - if Concurrent.on_jruby? - - it 'sets :fallback_policy correctly' do - clazz = java.util.concurrent.ThreadPoolExecutor::DiscardPolicy - policy = clazz.new - expect(clazz).to receive(:new).at_least(:once).with(any_args).and_return(policy) - - subject = FixedThreadPool.new(5, fallback_policy: :discard) - expect(subject.fallback_policy).to eq :discard - end - - else - - context 'exception handling' do - - it 'restarts threads that experience exception' do - count = subject.length - count.times{ subject << proc{ raise StandardError } } - sleep(1) - expect(subject.length).to eq count - end + # if Concurrent.on_jruby? + # + # it 'sets :fallback_policy correctly' do + # clazz = java.util.concurrent.ThreadPoolExecutor::DiscardPolicy + # policy = clazz.new + # expect(clazz).to receive(:new).at_least(:once).with(any_args).and_return(policy) + # + # subject = FixedThreadPool.new(5, fallback_policy: :discard) + # expect(subject.fallback_policy).to eq :discard + # end + # + # else + + context 'exception handling' do + + it 'restarts threads that experience exception' do + count = subject.length + count.times { subject << proc { raise StandardError } } + sleep(1) + expect(subject.length).to eq count end + end - context 'worker creation and caching' do + context 'worker creation and caching' do - it 'creates new workers when there are none available' do - pool = described_class.new(5) - expect(pool.length).to eq 0 - 5.times{ pool << proc{ sleep(1) } } - sleep(0.1) - expect(pool.length).to eq 5 - pool.kill - end + it 'creates new workers when there are none available' do + pool = described_class.new(5) + expect(pool.length).to eq 0 + 5.times { pool << proc { sleep(1) } } + sleep(0.1) + expect(pool.length).to eq 5 + pool.kill end end + # end end end end diff --git a/spec/concurrent/executor/simple_executor_service_spec.rb b/spec/concurrent/executor/simple_executor_service_spec.rb index f763220c1..fceaea805 100644 --- a/spec/concurrent/executor/simple_executor_service_spec.rb +++ b/spec/concurrent/executor/simple_executor_service_spec.rb @@ -13,7 +13,7 @@ module Concurrent subject { SimpleExecutorService.new } it 'creates a new thread for a call without arguments' do - thread = Thread.new{ nil } + thread = in_thread{ nil } expect(Thread).to receive(:new).with(no_args()).and_return(thread) expect(Concurrent.global_fast_executor).not_to receive(:post).with(any_args()) subject.post{ nil } @@ -26,7 +26,7 @@ module Concurrent end it 'creates a new thread for a call with arguments' do - thread = Thread.new{ nil } + thread = in_thread{ nil } expect(Thread).to receive(:new).with(1,2,3).and_return(thread) expect(Concurrent.global_fast_executor).not_to receive(:post).with(any_args()) subject.post(1,2,3){ nil } @@ -45,7 +45,7 @@ module Concurrent end it 'aliases #<<' do - thread = Thread.new{ nil } + thread = in_thread{ nil } expect(Thread).to receive(:new).with(no_args()).and_return(thread) expect(Concurrent.global_fast_executor).not_to receive(:post).with(any_args()) subject << proc{ nil } @@ -57,7 +57,7 @@ module Concurrent subject { SimpleExecutorService } it 'creates a new thread for a call without arguments' do - thread = Thread.new{ nil } + thread = in_thread{ nil } expect(Thread).to receive(:new).with(no_args()).and_return(thread) expect(Concurrent.global_fast_executor).not_to receive(:post).with(any_args()) subject.post{ nil } @@ -70,7 +70,7 @@ module Concurrent end it 'creates a new thread for a call with arguments' do - thread = Thread.new{ nil } + thread = in_thread{ nil } expect(Thread).to receive(:new).with(1,2,3).and_return(thread) expect(Concurrent.global_fast_executor).not_to receive(:post).with(any_args()) subject.post(1,2,3){ nil } @@ -89,7 +89,7 @@ module Concurrent end it 'aliases #<<' do - thread = Thread.new{ nil } + thread = in_thread{ nil } expect(Thread).to receive(:new).with(no_args()).and_return(thread) expect(Concurrent.global_fast_executor).not_to receive(:post).with(any_args()) subject << proc{ nil } diff --git a/spec/concurrent/executor/thread_pool_class_cast_spec.rb b/spec/concurrent/executor/thread_pool_class_cast_spec.rb index 273f7fdde..c34882ec0 100644 --- a/spec/concurrent/executor/thread_pool_class_cast_spec.rb +++ b/spec/concurrent/executor/thread_pool_class_cast_spec.rb @@ -1,26 +1,26 @@ module Concurrent RSpec.describe SingleThreadExecutor do - if Concurrent.on_jruby? - it 'inherits from JavaSingleThreadExecutor' do - expect(SingleThreadExecutor.ancestors).to include(JavaSingleThreadExecutor) - end - else - it 'inherits from RubySingleThreadExecutor' do - expect(SingleThreadExecutor.ancestors).to include(RubySingleThreadExecutor) - end + # if Concurrent.on_jruby? + # it 'inherits from JavaSingleThreadExecutor' do + # expect(SingleThreadExecutor.ancestors).to include(JavaSingleThreadExecutor) + # end + # else + it 'inherits from RubySingleThreadExecutor' do + expect(SingleThreadExecutor.ancestors).to include(RubySingleThreadExecutor) end + # end end RSpec.describe ThreadPoolExecutor do - if Concurrent.on_jruby? - it 'inherits from JavaThreadPoolExecutor' do - expect(ThreadPoolExecutor.ancestors).to include(JavaThreadPoolExecutor) - end - else - it 'inherits from RubyThreadPoolExecutor' do - expect(ThreadPoolExecutor.ancestors).to include(RubyThreadPoolExecutor) - end + # if Concurrent.on_jruby? + # it 'inherits from JavaThreadPoolExecutor' do + # expect(ThreadPoolExecutor.ancestors).to include(JavaThreadPoolExecutor) + # end + # else + it 'inherits from RubyThreadPoolExecutor' do + expect(ThreadPoolExecutor.ancestors).to include(RubyThreadPoolExecutor) end + # end end end diff --git a/spec/concurrent/executor/thread_pool_executor_shared.rb b/spec/concurrent/executor/thread_pool_executor_shared.rb index 61937c3a8..dbe5d4bcc 100644 --- a/spec/concurrent/executor/thread_pool_executor_shared.rb +++ b/spec/concurrent/executor/thread_pool_executor_shared.rb @@ -495,7 +495,7 @@ # Post several tasks to the executor. Has to be a new thread, # because it will start blocking once the queue fills up. - Thread.new do + in_thread do 5.times{ executor.post{ trigger.wait } } end diff --git a/spec/concurrent/executor/timer_set_spec.rb b/spec/concurrent/executor/timer_set_spec.rb index 8e0411597..8d43fcf49 100644 --- a/spec/concurrent/executor/timer_set_spec.rb +++ b/spec/concurrent/executor/timer_set_spec.rb @@ -10,28 +10,14 @@ module Concurrent after(:each){ subject.kill } context 'construction' do - - before(:all) do - reset_gem_configuration - end - - after(:each) do - reset_gem_configuration - end - it 'uses the executor given at construction' do - executor = Concurrent.global_immediate_executor - expect(executor).to receive(:post).with(no_args) - subject = TimerSet.new(executor: executor) - subject.post(0){ nil } + subject = TimerSet.new(executor: Concurrent.global_immediate_executor) + expect(subject.instance_variable_get(:@task_executor)).to eq Concurrent.global_immediate_executor end it 'uses the global io executor be default' do - latch = Concurrent::CountDownLatch.new(1) - expect(Concurrent.global_io_executor).to receive(:post).with(no_args) subject = TimerSet.new - subject.post(0){ latch.count_down } - latch.wait(0.1) + expect(subject.instance_variable_get(:@task_executor)).to eq Concurrent.global_io_executor end end diff --git a/spec/concurrent/future_spec.rb b/spec/concurrent/future_spec.rb index a9a92bb01..b8c3de276 100644 --- a/spec/concurrent/future_spec.rb +++ b/spec/concurrent/future_spec.rb @@ -309,6 +309,7 @@ def get_ivar_from_args(opts) attr_reader :reason attr_reader :count define_method(:update) do |time, value, reason| + @count ||= 0 @count = @count.to_i + 1 @value = value @reason = reason @@ -379,7 +380,7 @@ def get_ivar_from_args(opts) context 'deadlock avoidance' do def reentrant_observer(future) - obs = Object.new + obs = ::Object.new obs.define_singleton_method(:update) do |time, value, reason| @value = future.value end diff --git a/spec/concurrent/hash_spec.rb b/spec/concurrent/hash_spec.rb index 459e0a1ad..37146048e 100644 --- a/spec/concurrent/hash_spec.rb +++ b/spec/concurrent/hash_spec.rb @@ -3,8 +3,8 @@ module Concurrent let!(:hsh) { described_class.new } it 'concurrency' do - (1..THREADS).map do |i| - Thread.new do + (1..Concurrent::ThreadSafe::Test::THREADS).map do |i| + in_thread do 1000.times do |j| hsh[i * 1000 + j] = i expect(hsh[i * 1000 + j]).to eq(i) diff --git a/spec/concurrent/ivar_spec.rb b/spec/concurrent/ivar_spec.rb index afa74600c..760728e24 100644 --- a/spec/concurrent/ivar_spec.rb +++ b/spec/concurrent/ivar_spec.rb @@ -13,7 +13,7 @@ module Concurrent let(:pending_subject) do ivar = IVar.new - Thread.new do + in_thread do sleep(0.1) ivar.set(fulfilled_value) end @@ -87,6 +87,7 @@ def trigger_observable(observable) attr_reader :reason attr_reader :count define_method(:update) do |time, value, reason| + @count ||= 0 @count = @count.to_i + 1 @value = value @reason = reason @@ -109,7 +110,7 @@ def trigger_observable(observable) context 'deadlock avoidance' do def reentrant_observer(i) - obs = Object.new + obs = ::Object.new obs.define_singleton_method(:update) do |time, value, reason| @value = i.value end diff --git a/spec/concurrent/map_spec.rb b/spec/concurrent/map_spec.rb index 6156a0d38..3d7155382 100644 --- a/spec/concurrent/map_spec.rb +++ b/spec/concurrent/map_spec.rb @@ -8,8 +8,8 @@ module Concurrent end it 'concurrency' do - (1..THREADS).map do |i| - Thread.new do + (1..Concurrent::ThreadSafe::Test::THREADS).map do |i| + in_thread do 1000.times do |j| key = i * 1000 + j @cache[key] = i @@ -76,8 +76,8 @@ module Concurrent getter_threads_count = 5 compute_started = Concurrent::CountDownLatch.new(1) compute_proceed = Concurrent::CountDownLatch.new( - late_compute_threads_count + - late_put_if_absent_threads_count + + late_compute_threads_count + + late_put_if_absent_threads_count + getter_threads_count ) block_until_compute_started = lambda do |name| @@ -90,29 +90,29 @@ module Concurrent end expect_size_change 1 do - late_compute_threads = Array.new(late_compute_threads_count) do - Thread.new do + late_compute_threads = ::Array.new(late_compute_threads_count) do + in_thread do block_until_compute_started.call('compute_if_absent') expect(1).to eq @cache.compute_if_absent(:a) { fail } end end - late_put_if_absent_threads = Array.new(late_put_if_absent_threads_count) do - Thread.new do + late_put_if_absent_threads = ::Array.new(late_put_if_absent_threads_count) do + in_thread do block_until_compute_started.call('put_if_absent') expect(1).to eq @cache.put_if_absent(:a, 2) end end - getter_threads = Array.new(getter_threads_count) do - Thread.new do + getter_threads = ::Array.new(getter_threads_count) do + in_thread do block_until_compute_started.call('getter') Thread.pass while @cache[:a].nil? expect(1).to eq @cache[:a] end end - Thread.new do + in_thread do @cache.compute_if_absent(:a) do compute_started.count_down compute_proceed.wait @@ -120,8 +120,8 @@ module Concurrent 1 end end.join - (late_compute_threads + - late_put_if_absent_threads + + (late_compute_threads + + late_put_if_absent_threads + getter_threads).each(&:join) end end @@ -168,7 +168,7 @@ module Concurrent it 'with return' do with_or_without_default_proc do @cache[:a] = 1 - expect_handles_return_lambda(:compute_if_present, :a) + expect_handles_return_lambda(:compute_if_present, :a) end end @@ -261,8 +261,8 @@ module Concurrent getters_count = 20 key_klass = Concurrent::ThreadSafe::Test::HashCollisionKey - keys = [key_klass.new(1, 100), - key_klass.new(2, 100), + keys = [key_klass.new(1, 100), + key_klass.new(2, 100), key_klass.new(3, 100)] # hash colliding keys inserted_keys = [] @@ -272,7 +272,7 @@ module Concurrent getters_started = Concurrent::CountDownLatch.new(getters_count) getters_finished = Concurrent::CountDownLatch.new(getters_count) - computer_thread = Thread.new do + computer_thread = in_thread do getters_started.wait @cache.compute_if_absent(key) do compute_started.count_down @@ -282,31 +282,33 @@ module Concurrent compute_finished.count_down end - getter_threads = (1..getters_count).map do - Thread.new do + getter_threads = getters_count.times.map do + in_thread do getters_started.count_down inserted_keys.each do |inserted_key| - expect(true).to eq @cache.key?(inserted_key) - expect(1).to eq @cache[inserted_key] + expect(@cache.key?(inserted_key)).to eq true + expect(@cache[inserted_key]).to eq 1 end expect(false).to eq @cache.key?(key) + compute_started.wait + inserted_keys.each do |inserted_key| - expect(true).to eq @cache.key?(inserted_key) - expect(1).to eq @cache[inserted_key] + expect(@cache.key?(inserted_key)).to eq true + expect(@cache[inserted_key]).to eq 1 end - expect(false).to eq @cache.key?(key) - expect(nil).to eq @cache[key] + expect(@cache.key?(key)).to eq false + expect(@cache[key]).to eq nil getters_finished.count_down + compute_finished.wait - expect(true).to eq @cache.key?(key) - expect(1).to eq @cache[key] + + expect(@cache.key?(key)).to eq true + expect(@cache[key]).to eq 1 end end - (getter_threads << computer_thread).map do |t| - expect(t.join(2)).to be_truthy - end # asserting no deadlocks + join_with getter_threads + [computer_thread] inserted_keys << key end end @@ -318,7 +320,7 @@ module Concurrent end specify 'collision resistance with arrays' do - special_array_class = Class.new(Array) do + special_array_class = Class.new(::Array) do def key # assert_collision_resistance expects to be able to call .key to get the "real" key first.key end @@ -482,8 +484,8 @@ def key # assert_collision_resistance expects to be able to call .key to get the describe '#fetch' do it 'common' do - with_or_without_default_proc do |default_proc_set| - expect_no_size_change do + with_or_without_default_proc do |default_proc_set| + expect_no_size_change do expect(1).to eq @cache.fetch(:a, 1) expect(1).to eq @cache.fetch(:a) { 1 } expect(false).to eq @cache.key?(:a) @@ -841,7 +843,7 @@ def expect_invalid_options(options) expect { Concurrent::Map.new(options) }.to raise_error(ArgumentError) end - def expect_no_size_change(cache = @cache, &block) + def expect_no_size_change(cache = @cache, &block) expect_size_change(0, cache, &block) end @@ -896,8 +898,8 @@ def expect_collision_resistance(keys) size = keys.size while i < size k = keys[i] - expect(k.key == @cache.delete(k) && - !@cache.key?(k) && + expect(k.key == @cache.delete(k) && + !@cache.key?(k) && (@cache[k] = k.key; @cache[k] == k.key)).to be_truthy i += 10 end diff --git a/spec/concurrent/mvar_spec.rb b/spec/concurrent/mvar_spec.rb index ae85c0e8b..f180f794e 100644 --- a/spec/concurrent/mvar_spec.rb +++ b/spec/concurrent/mvar_spec.rb @@ -55,7 +55,7 @@ def dereferenceable_subject(value, opts = {}) it 'waits for another thread to #put' do m = MVar.new - putter = Thread.new { + putter = in_thread { sleep(0.1) m.put 14 } @@ -86,13 +86,13 @@ def dereferenceable_subject(value, opts = {}) it 'returns the returned value of the block' do m = MVar.new(14) - expect(m.borrow{2}).to eq(2) + expect(m.borrow { 2 }).to eq(2) expect(m.take).to eq(14) end it 'returns TIMEOUT on timeout on an empty MVar' do m = MVar.new - expect(m.borrow(0.1){}).to eq MVar::TIMEOUT + expect(m.borrow(0.1) { }).to eq MVar::TIMEOUT end end @@ -115,7 +115,7 @@ def dereferenceable_subject(value, opts = {}) it 'waits for another thread to #take' do m = MVar.new(14) - putter = Thread.new { + putter = in_thread { sleep(0.1) m.take } @@ -172,24 +172,24 @@ def dereferenceable_subject(value, opts = {}) it 'modifies a full MVar' do m = MVar.new(14) - m.modify{ |v| v + 2 } + m.modify { |v| v + 2 } expect(m.take).to eq 16 end it 'returns the unmodified value' do m = MVar.new(14) - expect(m.modify{ |v| v + 2 }).to eq 14 + expect(m.modify { |v| v + 2 }).to eq 14 end it 'waits for another thread to #put' do m = MVar.new - putter = Thread.new { + putter = in_thread { sleep(0.1) m.put 14 } - expect(m.modify{ |v| v + 2 }).to eq 14 + expect(m.modify { |v| v + 2 }).to eq 14 end it 'is atomic' do @@ -198,7 +198,7 @@ def dereferenceable_subject(value, opts = {}) # #modify conceptually does #take and #put - but it should be atomic. # Check that another #put can't sneak it during the #modify. - modifier = Thread.new { + modifier = in_thread { m.modify do |v| sleep(0.5) 1 @@ -212,7 +212,7 @@ def dereferenceable_subject(value, opts = {}) it 'returns TIMEOUT on timeout on an empty MVar' do m = MVar.new - expect(m.modify(0.1){ |v| v + 2 }).to eq MVar::TIMEOUT + expect(m.modify(0.1) { |v| v + 2 }).to eq MVar::TIMEOUT end end @@ -293,31 +293,31 @@ def dereferenceable_subject(value, opts = {}) it 'modifies a full MVar' do m = MVar.new(14) - m.modify!{ |v| v + 2 } + m.modify! { |v| v + 2 } expect(m.take).to eq 16 end it 'modifies an empty MVar' do m = MVar.new - m.modify!{ |v| 14 } + m.modify! { |v| 14 } expect(m.take).to eq 14 end it 'can be used to set a full MVar to empty' do m = MVar.new(14) - m.modify!{ |v| MVar::EMPTY } + m.modify! { |v| MVar::EMPTY } expect(m).to be_empty end it 'can be used to set an empty MVar to empty' do m = MVar.new - m.modify!{ |v| MVar::EMPTY } + m.modify! { |v| MVar::EMPTY } expect(m).to be_empty end it 'returns the unmodified value' do m = MVar.new(14) - expect(m.modify!{ |v| v + 2 }).to eq 14 + expect(m.modify! { |v| v + 2 }).to eq 14 end end @@ -337,17 +337,17 @@ def m.simulate_spurious_wake_up describe '#take' do it 'waits for another thread to #put' do - Thread.new { sleep(0.5); m.put 14 } - Thread.new { sleep(0.1); m.simulate_spurious_wake_up } + in_thread { sleep(0.5); m.put 14 } + in_thread { sleep(0.1); m.simulate_spurious_wake_up } expect(m.take).to eq 14 end it 'returns TIMEOUT on timeout on an empty MVar' do result = nil - Thread.new { result = m.take(0.3) } + in_thread { result = m.take(0.3) } sleep(0.1) - Thread.new { m.simulate_spurious_wake_up } + in_thread { m.simulate_spurious_wake_up } sleep(0.1) expect(result).to be_nil sleep(0.2) @@ -358,17 +358,17 @@ def m.simulate_spurious_wake_up describe '#modify' do it 'waits for another thread to #put' do - Thread.new { sleep(0.5); m.put 14 } - Thread.new { sleep(0.1); m.simulate_spurious_wake_up } + in_thread { sleep(0.5); m.put 14 } + in_thread { sleep(0.1); m.simulate_spurious_wake_up } - expect(m.modify{ |v| v + 2 }).to eq 14 + expect(m.modify { |v| v + 2 }).to eq 14 end it 'returns TIMEOUT on timeout on an empty MVar' do result = nil - Thread.new { result = m.modify(0.3){ |v| v + 2 } } + in_thread { result = m.modify(0.3) { |v| v + 2 } } sleep(0.1) - Thread.new { m.simulate_spurious_wake_up } + in_thread { m.simulate_spurious_wake_up } sleep(0.1) expect(result).to be_nil sleep(0.2) @@ -381,18 +381,18 @@ def m.simulate_spurious_wake_up before(:each) { m.put(42) } it 'waits for another thread to #take' do - Thread.new { sleep(0.5); m.take } - Thread.new { sleep(0.1); m.simulate_spurious_wake_up } + in_thread { sleep(0.5); m.take } + in_thread { sleep(0.1); m.simulate_spurious_wake_up } expect(m.put(14)).to eq 14 end - it 'returns TIMEOUT on timeout on a full MVar', buggy: true do + it 'returns TIMEOUT on timeout on a full MVar', notravis: true do # TODO (pitr-ch 15-Oct-2016): fails on jruby result = nil - Thread.new { result = m.put(14, 0.3) } + in_thread { result = m.put(14, 0.3) } sleep(0.1) - Thread.new { m.simulate_spurious_wake_up } + in_thread { m.simulate_spurious_wake_up } sleep(0.1) expect(result).to be_nil sleep(0.2) diff --git a/spec/concurrent/scheduled_task_spec.rb b/spec/concurrent/scheduled_task_spec.rb index 7657f19e2..ac9b99ebd 100644 --- a/spec/concurrent/scheduled_task_spec.rb +++ b/spec/concurrent/scheduled_task_spec.rb @@ -232,6 +232,7 @@ def trigger_observable(observable) attr_reader :latch def initialize @latch = Concurrent::CountDownLatch.new(1) + @count = 0 end def update(time, value, reason) @count = @count.to_i + 1 diff --git a/spec/concurrent/set_spec.rb b/spec/concurrent/set_spec.rb index 6fe8766b1..4b2ba36dd 100644 --- a/spec/concurrent/set_spec.rb +++ b/spec/concurrent/set_spec.rb @@ -4,8 +4,8 @@ module Concurrent let!(:set) { described_class.new } it 'concurrency' do - (1..THREADS).map do |i| - Thread.new do + (1..Concurrent::ThreadSafe::Test::THREADS).map do |i| + in_thread do 1000.times do v = i set << v diff --git a/spec/concurrent/synchronization_spec.rb b/spec/concurrent/synchronization_spec.rb index fe0434afd..ef28e4edf 100644 --- a/spec/concurrent/synchronization_spec.rb +++ b/spec/concurrent/synchronization_spec.rb @@ -7,11 +7,11 @@ module Concurrent RSpec.shared_examples :attr_volatile do specify 'older writes are always visible' do - # store = BClass.new + store = store() store.not_volatile = 0 store.volatile = 0 - t1 = Thread.new do + in_thread do Thread.abort_on_exception = true 1000000000.times do |i| store.not_volatile = i @@ -19,21 +19,21 @@ module Concurrent end end - t2 = Thread.new do - 10.times do + t2 = in_thread do + Thread.abort_on_exception = true + 10.times.map do + Thread.pass volatile = store.volatile not_volatile = store.not_volatile - expect(not_volatile).to be >= volatile - Thread.pass + not_volatile >= volatile end end - t2.join - t1.kill + expect(t2.value.all?).to eq true end end - describe Synchronization::Object do + describe Synchronization::Object do class AAClass < Synchronization::Object end @@ -78,9 +78,9 @@ class VolatileFieldClass < Synchronization::Object let(:store) { VolatileFieldClass.new } it_should_behave_like :attr_volatile - end + end - describe Synchronization::LockableObject do + describe Synchronization::LockableObject do class BClass < Synchronization::LockableObject safe_initialization! @@ -120,50 +120,53 @@ def ns_initialize describe '#wait' do it 'puts the current thread to sleep' do - t = Thread.new do + t1 = in_thread do Thread.abort_on_exception = true subject.wait end - sleep 0.1 - expect(t.status).to eq 'sleep' + t2 = in_thread { Thread.pass until t1.status == 'sleep' } + join_with t2 end it 'allows the sleeping thread to be killed' do - t = Thread.new do + t = in_thread do Thread.abort_on_exception = true subject.wait rescue nil end sleep 0.1 t.kill sleep 0.1 - expect(t.status).to eq false + expect(t.join).not_to eq nil expect(t.alive?).to eq false end it 'releases the lock on the current object' do - expect { Timeout.timeout(3) do - t = Thread.new { subject.wait } - sleep 0.1 - # TODO (pitr-ch 15-Oct-2016): https://travis-ci.org/pitr-ch/concurrent-ruby/jobs/167933569 - expect(t.status).to eq 'sleep' - subject.synchronize {} # we will deadlock here if #wait doesn't release lock - end }.not_to raise_error + t1 = in_thread do + # #wait should release lock, even if it was already held on entry + t2 = in_thread { subject.wait } + Thread.pass until t2.status == 'sleep' + subject.synchronize {} # it will deadlock here if #wait doesn't release lock + t2 + end + join_with t1 + expect(t1.value.status).to eq 'sleep' end it 'can be called from within a #synchronize block' do - expect { Timeout.timeout(3) do - # #wait should release lock, even if it was already held on entry - t = Thread.new { subject.synchronize { subject.wait } } - sleep 0.1 - expect(t.status).to eq 'sleep' - subject.synchronize {} # we will deadlock here if lock wasn't released - end }.not_to raise_error + t1 = in_thread do + t2 = in_thread { subject.synchronize { subject.wait } } + Thread.pass until t2.status == 'sleep' + subject.synchronize {} # it will deadlock here if #wait doesn't release lock + t2 + end + join_with t1 + expect(t1.value.status).to eq 'sleep' end end describe '#synchronize' do it 'allows only one thread to execute count' do - threads = 10.times.map { Thread.new(subject) { 100.times { subject.count } } } + threads = 10.times.map { in_thread(subject) { 100.times { subject.count } } } threads.each(&:join) expect(subject.count).to eq 1001 end @@ -175,15 +178,15 @@ def ns_initialize specify 'final field always visible' do store = BClass.new 'asd' - t1 = Thread.new { 1000000000.times { |i| store = BClass.new i.to_s } } - t2 = Thread.new { 10.times { expect(store.final).not_to be_nil; Thread.pass } } + t1 = in_thread { 1000000000.times { |i| store = BClass.new i.to_s } } + t2 = in_thread { 10.times { expect(store.final).not_to be_nil; Thread.pass } } t2.join t1.kill end let(:store) { BClass.new } it_should_behave_like :attr_volatile - end + end describe 'Concurrent::Synchronization::Volatile module' do class BareClass diff --git a/spec/concurrent/thread_safe/map_loops_spec.rb b/spec/concurrent/thread_safe/map_loops_spec.rb index e6bd7fd8a..cbac98ea9 100644 --- a/spec/concurrent/thread_safe/map_loops_spec.rb +++ b/spec/concurrent/thread_safe/map_loops_spec.rb @@ -40,8 +40,8 @@ module Concurrent it '#put_if_absent' do do_thread_loop( - :put_if_absent, - 'acc += 1 unless cache.put_if_absent(key, key)', + :put_if_absent, + 'acc += 1 unless cache.put_if_absent(key, key)', key_count: 100_000 ) do |result, cache, options, keys| expect_standard_accumulator_test_result(result, cache, options, keys) @@ -128,10 +128,10 @@ module Concurrent acc += change if cache.replace_pair(key, v, v + change) RUBY_EVAL do_thread_loop( - :count_race, - code, - loop_count: 5, - prelude: prelude, + :count_race, + code, + loop_count: 5, + prelude: prelude, cache_setup: ZERO_VALUE_CACHE_SETUP ) do |result, cache, options, keys| result_sum = sum(result) @@ -151,9 +151,9 @@ module Concurrent it 'get_and_set_existing' do code = 'acc += 1 if cache.get_and_set(key, key) == -1' do_thread_loop( - :get_and_set_existing, - code, - cache_setup: INITIAL_VALUE_CACHE_SETUP, + :get_and_set_existing, + code, + cache_setup: INITIAL_VALUE_CACHE_SETUP, initial_value: -1 ) do |result, cache, options, keys| expect_standard_accumulator_test_result(result, cache, options, keys) @@ -172,8 +172,8 @@ def compute_if_absent_and_present(opts = {}) end RUBY_EVAL do_thread_loop( - __method__, - code, + __method__, + code, {loop_count: 5, prelude: prelude}.merge(opts) ) do |result, cache, options, keys| stored_sum = 0 @@ -199,8 +199,8 @@ def add_remove(opts = {}) end RUBY_EVAL do_thread_loop( - __method__, - code, + __method__, + code, {loop_count: 5, prelude: prelude}.merge(opts) ) do |result, cache, options, keys| expect_all_key_mappings_exist(cache, keys, false) @@ -222,8 +222,8 @@ def add_remove_via_compute(opts = {}) end RUBY_EVAL do_thread_loop( - __method__, - code, + __method__, + code, {loop_count: 5, prelude: prelude}.merge(opts) ) do |result, cache, options, keys| expect_all_key_mappings_exist(cache, keys, false) @@ -241,8 +241,8 @@ def add_remove_via_compute_if_absent_present(opts = {}) end RUBY_EVAL do_thread_loop( - __method__, - code, + __method__, + code, {loop_count: 5, prelude: prelude}.merge(opts) ) do |result, cache, options, keys| expect_all_key_mappings_exist(cache, keys, false) @@ -260,8 +260,8 @@ def add_remove_indiscriminate(opts = {}) end RUBY_EVAL do_thread_loop( - __method__, - code, + __method__, + code, {loop_count: 5, prelude: prelude}.merge(opts) ) do |result, cache, options, keys| expect_all_key_mappings_exist(cache, keys, false) @@ -275,8 +275,8 @@ def count_up(opts = {}) acc += 1 if cache.replace_pair(key, v, v + 1) RUBY_EVAL do_thread_loop( - __method__, - code, + __method__, + code, {loop_count: 5, cache_setup: ZERO_VALUE_CACHE_SETUP}.merge(opts) ) do |result, cache, options, keys| expect_count_up(result, cache, options, keys) @@ -291,7 +291,7 @@ def count_up_via_compute(opts = {}) end RUBY_EVAL do_thread_loop( - __method__, + __method__, code, {loop_count: 5}.merge(opts) ) do |result, cache, options, keys| expect_count_up(result, cache, options, keys) @@ -307,8 +307,8 @@ def count_up_via_merge_pair(opts = {}) cache.merge_pair(key, 1) { |old_value| old_value + 1 } RUBY_EVAL do_thread_loop( - __method__, - code, + __method__, + code, {loop_count: 5}.merge(opts) ) do |result, cache, options, keys| all_match = true @@ -330,8 +330,8 @@ def add_remove_to_zero(opts = {}) acc -= 1 if cache.delete_pair(key, key) RUBY_EVAL do_thread_loop( - __method__, - code, + __method__, + code, {loop_count: 5}.merge(opts) ) do |result, cache, options, keys| expect_all_key_mappings_exist(cache, keys, false) @@ -344,8 +344,8 @@ def add_remove_to_zero_via_merge_pair(opts = {}) acc += (cache.merge_pair(key, key) {}) ? 1 : -1 RUBY_EVAL do_thread_loop( - __method__, - code, + __method__, + code, {loop_count: 5}.merge(opts) ) do |result, cache, options, keys| expect_all_key_mappings_exist(cache, keys, false) @@ -363,9 +363,9 @@ def do_thread_loop(name, code, options = {}, &block) options[:key_count] = (options[:key_count] / 40).to_i keys = to_hash_collision_keys_array(options[:key_count]) run_thread_loop( - meth, - keys, - options.merge(loop_count: options[:loop_count] * 5), + meth, + keys, + options.merge(loop_count: options[:loop_count] * 5), &block ) end @@ -375,12 +375,12 @@ def run_thread_loop(meth, keys, options, &block) cache = options[:cache_setup].call(options, keys) barrier = Concurrent::ThreadSafe::Test::Barrier.new(options[:thread_count]) result = (1..options[:thread_count]).map do - Thread.new do + in_thread do setup_sync_and_start_loop( - meth, - cache, - keys, - barrier, + meth, + cache, + keys, + barrier, options[:loop_count] ) end diff --git a/spec/concurrent/thread_safe/synchronized_delegator_spec.rb b/spec/concurrent/thread_safe/synchronized_delegator_spec.rb index e80407a10..2b7055479 100644 --- a/spec/concurrent/thread_safe/synchronized_delegator_spec.rb +++ b/spec/concurrent/thread_safe/synchronized_delegator_spec.rb @@ -26,12 +26,12 @@ module Concurrent sync_hash = described_class.new(hash) sync_hash[1] = 'egy' - t1 = Thread.new do + t1 = in_thread do sync_hash[2] = 'dva' sync_hash[3] # triggers t2_continue end - t2 = Thread.new do + t2 = in_thread do Thread.pass until t2_continue sync_hash[4] = '42' end @@ -56,7 +56,7 @@ module Concurrent array = ::Array.new sync_array = described_class.new(array) - t1 = Thread.new do + t1 = in_thread do sync_array << 1 sync_array.each do t2_continue = true @@ -64,7 +64,7 @@ module Concurrent end end - t2 = Thread.new do + t2 = in_thread do # sleep(0.01) Thread.pass until t2_continue sync_array << 2 diff --git a/spec/concurrent/timer_task_spec.rb b/spec/concurrent/timer_task_spec.rb index d0d74285e..6b4e1d676 100644 --- a/spec/concurrent/timer_task_spec.rb +++ b/spec/concurrent/timer_task_spec.rb @@ -8,7 +8,7 @@ module Concurrent context :dereferenceable do def kill_subject - @subject.kill if @subject + @subject.kill if defined?(@subject) && @subject rescue Exception => ex # prevent exceptions with mocks in tests end diff --git a/spec/concurrent/tvar_spec.rb b/spec/concurrent/tvar_spec.rb index 8a87237d6..8effb33f6 100644 --- a/spec/concurrent/tvar_spec.rb +++ b/spec/concurrent/tvar_spec.rb @@ -112,7 +112,7 @@ module Concurrent a = CountDownLatch.new b = CountDownLatch.new - Thread.new do + in_thread do Concurrent::atomically do t.value = 1 a.count_down @@ -135,7 +135,7 @@ module Concurrent a = CountDownLatch.new b = CountDownLatch.new - Thread.new do + in_thread do Concurrent::atomically do t.value = 1 a.count_down diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 659fd652e..9b4727dfb 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -24,8 +24,8 @@ Concurrent.use_simple_logger Logger::FATAL -# import all the support files -Dir[File.join(File.dirname(__FILE__), 'support/**/*.rb')].each { |f| require File.expand_path(f) } +require_relative 'support/example_group_extensions' +require_relative 'support/threadsafe_test' RSpec.configure do |config| #config.raise_errors_for_deprecations! diff --git a/spec/support/example_group_extensions.rb b/spec/support/example_group_extensions.rb index 340727d60..aa43452c3 100644 --- a/spec/support/example_group_extensions.rb +++ b/spec/support/example_group_extensions.rb @@ -19,32 +19,45 @@ def use_c_extensions? Concurrent.allow_c_extensions? end - GLOBAL_EXECUTORS = [ - [:GLOBAL_FAST_EXECUTOR, -> { Delay.new { Concurrent.new_fast_executor(auto_terminate: true) } }], - [:GLOBAL_IO_EXECUTOR, -> { Delay.new { Concurrent.new_io_executor(auto_terminate: true) } }], - [:GLOBAL_TIMER_SET, -> { Delay.new { Concurrent::TimerSet.new(auto_terminate: true) } }], - ] - - def reset_gem_configuration - GLOBAL_EXECUTORS.each do |var, factory| - executor = Concurrent.const_get(var).value! - executor.shutdown - executor.wait_for_termination(0.2) - executor.kill - Concurrent.const_set(var, factory.call) - end - end - def monotonic_interval raise ArgumentError.new('no block given') unless block_given? start_time = GLOBAL_MONOTONIC_CLOCK.get_time yield GLOBAL_MONOTONIC_CLOCK.get_time - start_time end + + def in_thread(*args, &block) + @created_threads ||= Queue.new + new_thread = Thread.new(*args) do |*args, &b| + Thread.abort_on_exception = true + block.call *args, &b + end + @created_threads.push new_thread + new_thread + end + + def join_with(threads, timeout = 5) + threads = Array(threads) + threads.each do |t| + joined_thread = t.join(timeout * threads.size) + expect(joined_thread).not_to eq nil + end + end end end class RSpec::Core::ExampleGroup include Concurrent::TestHelpers extend Concurrent::TestHelpers + + before :each do + expect(@created_threads.nil? || @created_threads.empty?).to be_truthy + end + + after :each do + while (thread = (@created_threads.pop(true) rescue nil)) + thread.kill + expect(thread.join(0.25)).not_to be_nil + end + end end diff --git a/spec/support/less_than_or_equal_to_matcher.rb b/spec/support/less_than_or_equal_to_matcher.rb deleted file mode 100644 index 8fbed099c..000000000 --- a/spec/support/less_than_or_equal_to_matcher.rb +++ /dev/null @@ -1,5 +0,0 @@ -RSpec::Matchers.define :be_less_than_or_equal_to do |expected| - match do |actual| - actual <= expected - end -end \ No newline at end of file diff --git a/spec/support/threads.rb b/spec/support/threads.rb deleted file mode 100644 index 1251fa4fb..000000000 --- a/spec/support/threads.rb +++ /dev/null @@ -1 +0,0 @@ -THREADS = (RUBY_ENGINE == 'ruby' ? 100 : 10) diff --git a/spec/support/threadsafe_test.rb b/spec/support/threadsafe_test.rb index b2006a87e..4184f389f 100644 --- a/spec/support/threadsafe_test.rb +++ b/spec/support/threadsafe_test.rb @@ -1,6 +1,9 @@ module Concurrent module ThreadSafe module Test + + THREADS = (RUBY_ENGINE == 'ruby' ? 100 : 10) + class Barrier def initialize(count = 1) @count = count diff --git a/support/release.sh b/support/release.sh index 74a3ba67b..f2edcddf3 100755 --- a/support/release.sh +++ b/support/release.sh @@ -55,7 +55,7 @@ then cd .. # TODO (pitr-ch 17-Dec-2016): dry: duplicates rake task - rspec_options='--color --backtrace --seed 1 --format documentation --tag ~unfinished --tag ~notravis --tag ~buggy' + rspec_options='--color --backtrace --seed 1 --format documentation --tag ~notravis' # Install and test MRI version export RBENV_VERSION=$mriVersion