Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
146e688
Consistent at_exit behavior for Java and Ruby thread pools.
jdantonio Feb 26, 2015
ab62410
Added a :stop_on_exit option to the enable_at_exit_handler function.
jdantonio Feb 26, 2015
f955955
Updated thread pool docs to better explain shutting down thread pools.
jdantonio Feb 26, 2015
49158fe
Fixed calls to enable_at_exit_handler! that were missing opts.
jdantonio Feb 27, 2015
7575f0a
Added missing 'it_should_behave_like :thread_pool' specs.
jdantonio Feb 27, 2015
360bb22
Simpler :executor option syntax.
jdantonio Feb 27, 2015
7218017
Updated method calls to use new 'executor: :immediate' syntax.
jdantonio Feb 27, 2015
316b379
Updated default executors and associated documentation.
jdantonio Feb 27, 2015
1011fcc
Added Executor#auto_terminate? predicate method.
jdantonio Feb 27, 2015
3441a91
Added at_exit handler to TimerSet.
jdantonio Feb 27, 2015
60d78a5
Simplified auto-termination of the global thread pools.
jdantonio Feb 27, 2015
5fac5a7
Updated CHANGELOG.
jdantonio Feb 27, 2015
67ce971
Simpler :executor option syntax.
jdantonio Feb 27, 2015
2f1f1e7
Updated default executors and associated documentation.
jdantonio Feb 27, 2015
8075149
Major refactor of global thread pools.
jdantonio Feb 27, 2015
4d0bfe7
Updated specs from 'task pool' to 'fast executor'
jdantonio Feb 28, 2015
5956014
Updated specs from 'operation pool' to 'io executor'
jdantonio Feb 28, 2015
6b5ad7d
Fixed io/task vs. fast/operation executor mixup.
jdantonio Feb 28, 2015
a872a69
All high-level abstractions default to the global io executor.
jdantonio Feb 28, 2015
7505bdb
Refactor, cleanup, document.
jdantonio Feb 28, 2015
80faafa
Added shutdown/kill/wait_for_termination variants for global executors
jdantonio Feb 28, 2015
d43f093
Fixed bug in Actor causing it to prematurely warm global thread pools…
jdantonio Feb 28, 2015
09475b4
Renamed private internal OptionsParser to ExecutorOptions.
jdantonio Feb 28, 2015
550592d
Added Lazy, a simpler and faster variation of Delay.
jdantonio Feb 28, 2015
664234b
Updated top-level requires
jdantonio Feb 28, 2015
390ac66
Renamed Lazy to LazyReference.
jdantonio Feb 28, 2015
1447a38
Updated Actor to use the global io executor.
jdantonio Mar 1, 2015
1238a28
Moved actor stress test from spec to script in examples.
jdantonio Mar 1, 2015
79de972
Replaced remaining references to global task and operation thread pools.
jdantonio Mar 1, 2015
6d2f821
Updated travis config.
jdantonio Mar 1, 2015
42d7646
Added 'nuclear option' Concurrent.disable_auto_termination_of_all_exe…
jdantonio Mar 1, 2015
7763f11
Improved global config cleanup during testing.
jdantonio Mar 1, 2015
b254942
Moved global logger out of Configuration object.
jdantonio Mar 1, 2015
09df063
Thread pool at_exit stores id and gets from ObjectSpace when not
jdantonio Mar 2, 2015
c1944a1
Updated CHANGELOG
jdantonio Mar 2, 2015
d07ff0b
Updated to latest version of all dev/test gems.
jdantonio Mar 2, 2015
350dffe
Updated docs for LazyRegister.
jdantonio Mar 2, 2015
8c77ef8
Rebased master after merging PR #258.
jdantonio Mar 4, 2015
e8c6f8a
Removed unnecessary and unsafe queue clearing in TimerSet.kill
jdantonio Mar 5, 2015
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,4 @@ matrix:
- rvm: jruby-head
- rvm: 1.9.3

script: "rake compile && bundle exec rspec --color --backtrace --tag ~unfinished --seed 1 --format documentation ./spec"
script: "rake compile && bundle exec rspec --color --backtrace --tag ~unfinished --seed 1 ./spec"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the --format documentation was there to see the last test where it was hanging. Is there another way?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if there's another way, but --format documentation has been in .travis.yml since June of last year. I only removed it a couple of weeks ago--then immediately added it back when we had a hanging test. I think we should keep it for now. Once we stabilize our tests we can remove it, but the full output is pretty valuable right now.

27 changes: 27 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,33 @@
* Deprecated all clock-time based timer scheduling
- Only support scheduling by delay
- Effects `Concurrent.timer`, `TimerSet`, and `ScheduledTask`
* Consistent `at_exit` behavior for Java and Ruby thread pools.
* Added `at_exit` handler to Ruby thread pools (already in Java thread pools)
- Ruby handler stores the object id and retrieves from `ObjectSpace`
- JRuby disables `ObjectSpace` by default so that handler stores the object reference
* Added a `:stop_on_exit` option to thread pools to enable/disable `at_exit` handler
* Updated thread pool docs to better explain shutting down thread pools
* Simpler `:executor` option syntax for all abstractions which support this option
* Added `Executor#auto_terminate?` predicate method (for thread pools)
* Added `at_exit` handler to `TimerSet`
* Simplified auto-termination of the global executors
- Can now disable auto-termination of global executors
- Added shutdown/kill/wait_for_termination variants for global executors
* Can now disable auto-termination for *all* executors (the nuclear option)
* Simplified auto-termination of the global executors
* Deprecated terms "task pool" and "operation pool"
- New terms are "io executor" and "fast executor"
- New functions added with new names
- Deprecation warnings added to functions referencing old names
* Moved all thread pool related functions from `Concurrent::Configuration` to `Concurrent`
- Old functions still exist with deprecation warnings
- New functions have updated names as appropriate
* All high-level abstractions default to the "io executor"
* Fixed bug in `Actor` causing it to prematurely warm global thread pools on gem load
- This also fixed a `RejectedExecutionError` bug when running with minitest/autorun via JRuby
* Added `LazyReference`, a simpler and faster varition of `Delay`
- Updated most internal uses of `Delay` with `LazyReference`
* Moved global logger up to the `Concurrent` namespace and refactored the code

## Current Release v0.8.0 (25 January 2015)

Expand Down
17 changes: 8 additions & 9 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,20 @@ gemspec name: 'concurrent-ruby'

group :development do
gem 'rake', '~> 10.3.2'
gem 'rake-compiler', '~> 0.9.2'
gem 'rake-compiler', '~> 0.9.5'
gem 'gem-compiler', '~> 0.3.0'
end

group :testing do
gem 'rspec', '~> 3.0.0'
gem 'simplecov', '~> 0.8.2', :require => false
gem 'coveralls', '~> 0.7.0', :require => false
gem 'timecop', '~> 0.7.1'
gem 'rspec', '~> 3.2.0'
gem 'simplecov', '~> 0.9.2', :require => false
gem 'coveralls', '~> 0.7.11', :require => false
gem 'timecop', '~> 0.7.3'
end

group :documentation do
gem 'countloc', '~> 0.4.0', :platforms => :mri, :require => false
gem 'rubycritic', '~> 1.0.2', :platforms => :mri, require: false
gem 'yard', '~> 0.8.7.4', :require => false
gem 'inch', '~> 0.4.6', :platforms => :mri, :require => false
gem 'redcarpet', '~> 3.1.2', platforms: :mri # understands github markdown
gem 'yard', '~> 0.8.7.6', :require => false
gem 'inch', '~> 0.5.10', :platforms => :mri, :require => false
gem 'redcarpet', '~> 3.2.2', platforms: :mri # understands github markdown
end
37 changes: 16 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,36 +66,29 @@ This library contains a variety of concurrency abstractions at high and low leve

* See [ThreadPool](http://ruby-concurrency.github.io/concurrent-ruby/file.thread_pools.html) overview, which also contains a list of other Executors available.

### Thread-safe Observers

* [Concurrent::Observable](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/Observable.html) mixin module
* [CopyOnNotifyObserverSet](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/CopyOnNotifyObserverSet.html)
* [CopyOnWriteObserverSet](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/CopyOnWriteObserverSet.html)

### Thread synchronization classes and algorithms

Lower-level abstractions mainly used as building blocks.

* [condition](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/Condition.html)
* [countdown latch](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/CountDownLatch.html)
* [cyclic barrier](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/CyclicBarrier.html)
* [event](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/Event.html)
* [exchanger](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/Exchanger.html)
* [semaphore](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/Semaphore.html)
* [timeout](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent.html#timeout-class_method)
* [timer](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent.html#timer-class_method)
* [Condition](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/Condition.html)
* [CountdownLatch](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/CountDownLatch.html)
* [CyclicBarrier](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/CyclicBarrier.html)
* [Event](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/Event.html)
* [Exchanger](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/Exchanger.html)
* [Semaphore](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/Semaphore.html)
* [Timeout](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent.html#timeout-class_method)
* [Timer](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent.html#timer-class_method)

### Thread-safe variables

Lower-level abstractions mainly used as building blocks.

* [AtomicBoolean](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/AtomicBoolean.html)
* [AtomicFixnum](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/AtomicFixnum.html)
* AtomicReference (no docs currently available, check source)
* [AtomicReference](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/MutexAtomic.html)
* [Delay](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/Delay.html)
* [LazyReference](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/LazyReference.html)
* [LazyRegister](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/LazyRegister.html)
* [I-Structures](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/IVar.html) (IVar)
* [M-Structures](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/MVar.html) (MVar)
* [thread-local variables](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/ThreadLocalVar.html)
* [software transactional memory](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/TVar.html) (TVar)
* [Thread-local variables](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/ThreadLocalVar.html)
* [Software transactional memory](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/TVar.html) (TVar)

## Usage

Expand Down Expand Up @@ -128,6 +121,8 @@ require 'concurrent/delay' # Concurrent::Delay
require 'concurrent/exchanger' # Concurrent::Exchanger
require 'concurrent/future' # Concurrent::Future
require 'concurrent/ivar' # Concurrent::IVar
require 'concurrent/lazy_register' # Concurrent::LazyRegister
require 'concurrent/lazy_reference' # Concurrent::LazyReference
require 'concurrent/mvar' # Concurrent::MVar
require 'concurrent/promise' # Concurrent::Promise
require 'concurrent/scheduled_task' # Concurrent::ScheduledTask
Expand Down
139 changes: 139 additions & 0 deletions examples/actor_stress_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
#!/usr/bin/env ruby

$: << File.expand_path('../../lib', __FILE__)

require 'benchmark'
require 'optparse'
require 'thread'
require 'rspec/expectations'

require 'concurrent/actor'

class ActorStressTester
include ::RSpec::Matchers

TESTS_PER_RUN = 5
THREADS_PER_TEST = 10
LOOPS_PER_THREAD = 25

class Ping < Concurrent::Actor::Context
def initialize(queue)
@queue = queue
end

def on_message(message)
case message
when :child
Concurrent::Actor::Utils::AdHoc.spawn(:pong, @queue) do |queue|
-> m { queue << m }
end
else
@queue << message
message
end
end
end

def initialize(opts = {})
@tests = opts.fetch(:tests, TESTS_PER_RUN)
@threads = opts.fetch(:threads, THREADS_PER_TEST)
@loops = opts.fetch(:loops, LOOPS_PER_THREAD)
end

def run
plural = ->(number){ number == 1 ? '' : 's' }

puts "Running #{@tests} test#{plural.call(@tests)} " +
"with #{@threads} thread#{plural.call(@threads)} each " +
"and #{@loops} loop#{plural.call(@loops)} per thread..."

Benchmark.bm do |bm|
@tests.times do
bm.report do
test(@threads, @loops)
end
end
end
end

def test(threads, loops)
(1..threads).collect do
Thread.new do
loops.times do

queue = Queue.new
actor = Ping.spawn(:ping, queue)

core = Concurrent::Actor.root.send(:core)
children = core.instance_variable_get(:@children)
expect(children).to include(actor)

actor << 'a' << 1
expect(queue.pop).to eq 'a'
expect(actor.ask(2).value).to eq 2

expect(actor.parent).to eq Concurrent::Actor.root
expect(Concurrent::Actor.root.path).to eq '/'
expect(actor.path).to eq '/ping'

child = actor.ask(:child).value
expect(child.path).to eq '/ping/pong'

queue.clear
child.ask(3)
expect(queue.pop).to eq 3

actor << :terminate!
expect(actor.ask(:blow_up).wait).to be_rejected
terminate_actors(actor, child)
end
end
end.each(&:join)
end

def terminate_actors(*actors)
actors.each do |actor|
unless actor.ask!(:terminated?)
actor.ask!(:terminate!)
end
end
end
end

# def trace!
# set_trace_func proc { |event, file, line, id, binding, classname|
# # thread = eval('Thread.current', binding).object_id.to_s(16)
# printf "%8s %20s %20s %s %s:%-2d\n", event, id, classname, nil, file, line
# }
# yield
# ensure
# set_trace_func nil
# end

if $0 == __FILE__

options = {}

OptionParser.new do |opts|
opts.banner = "Usage: #{File.basename(__FILE__)} [options]"

opts.on("--tests=TESTS", "Number of tests per run") do |value|
options[:tests] = value.to_i
end

opts.on("--threads=THREADS", "Number of threads per test") do |value|
options[:threads] = value.to_i
end

opts.on("--loops=LOOPS", "Number of loops per thread") do |value|
options[:loops] = value.to_i
end

opts.on("-h", "--help", "Prints this help") do
puts opts
exit
end
end.parse!

ActorStressTester.new(options).run
end
23 changes: 23 additions & 0 deletions examples/lazy_and_delay.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#!/usr/bin/env ruby

$: << File.expand_path('../../lib', __FILE__)

require 'benchmark'

require 'concurrent/delay'
require 'concurrent/lazy_reference'

n = 500_000

delay = Concurrent::Delay.new{ nil }
lazy = Concurrent::LazyReference.new{ nil }

delay.value
lazy.value

Benchmark.bm do |x|
puts 'Benchmarking Delay...'
x.report { n.times{ delay.value } }
puts 'Benchmarking Lazy...'
x.report { n.times{ lazy.value } }
end
12 changes: 5 additions & 7 deletions lib/concurrent.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,26 @@

require 'concurrent/configuration'

require 'concurrent/actor'
require 'concurrent/atomics'
require 'concurrent/channels'
require 'concurrent/collections'
require 'concurrent/errors'
require 'concurrent/executors'
require 'concurrent/utilities'

require 'concurrent/actor'
require 'concurrent/atomic'
require 'concurrent/lazy_register'
require 'concurrent/agent'
require 'concurrent/async'
require 'concurrent/atomic'
require 'concurrent/dataflow'
require 'concurrent/delay'
require 'concurrent/dereferenceable'
require 'concurrent/errors'
require 'concurrent/exchanger'
require 'concurrent/future'
require 'concurrent/ivar'
require 'concurrent/lazy_reference'
require 'concurrent/lazy_register'
require 'concurrent/mvar'
require 'concurrent/obligation'
require 'concurrent/observable'
require 'concurrent/options_parser'
require 'concurrent/promise'
require 'concurrent/scheduled_task'
require 'concurrent/timer_task'
Expand Down
5 changes: 3 additions & 2 deletions lib/concurrent/actor.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
require 'concurrent/configuration'
require 'concurrent/delay'
require 'concurrent/executor/serialized_execution'
require 'concurrent/ivar'
require 'concurrent/logging'
Expand Down Expand Up @@ -39,7 +40,7 @@ def self.current
Thread.current[:__current_actor__]
end

@root = Delay.new do
@root = Delay.new(executor: :immediate) do
Core.new(parent: nil, name: '/', class: Root, initialized: ivar = IVar.new).reference.tap do
ivar.no_error!
end
Expand All @@ -59,7 +60,7 @@ def self.root
# Actor.spawn name: :ping3,
# class: AdHoc,
# args: [1]
# executor: Concurrent.configuration.global_task_pool do |add|
# executor: Concurrent.global_io_executor do |add|
# lambda { |number| number + add }
# end
#
Expand Down
4 changes: 2 additions & 2 deletions lib/concurrent/actor/core.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ class Core
# @option opts [Class] reference a custom descendant of {Reference} to use
# @option opts [Context] actor_class a class to be instantiated defining Actor's behaviour
# @option opts [Array<Object>] args arguments for actor_class instantiation
# @option opts [Executor] executor, default is `Concurrent.configuration.global_task_pool`
# @option opts [Executor] executor, default is `Concurrent.global_io_executor`
# @option opts [true, false] link, atomically link the actor to its parent
# @option opts [true, false] supervise, atomically supervise the actor by its parent
# @option opts [Array<Array(Behavior::Abstract, Array<Object>)>]
Expand All @@ -56,7 +56,7 @@ def initialize(opts = {}, &block)
@context_class = Child! opts.fetch(:class), AbstractContext
allocate_context

@executor = Type! opts.fetch(:executor, Concurrent.configuration.global_task_pool), Executor
@executor = Type! opts.fetch(:executor, Concurrent.global_io_executor), Executor
raise ArgumentError, 'ImmediateExecutor is not supported' if @executor.is_a? ImmediateExecutor

@reference = (Child! opts[:reference_class] || @context.default_reference_class, Reference).new self
Expand Down
8 changes: 4 additions & 4 deletions lib/concurrent/actor/reference.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ def tell(message)

# @note it's a good practice to use tell whenever possible. Ask should be used only for
# testing and when it returns very shortly. It can lead to deadlock if all threads in
# global_task_pool will block on while asking. It's fine to use it form outside of actors and
# global_task_pool.
# global_io_executor will block on while asking. It's fine to use it form outside of actors and
# global_io_executor.
#
# sends message to the actor and asks for the result of its processing, returns immediately
# @param [Object] message
Expand All @@ -40,8 +40,8 @@ def ask(message, ivar = IVar.new)

# @note it's a good practice to use tell whenever possible. Ask should be used only for
# testing and when it returns very shortly. It can lead to deadlock if all threads in
# global_task_pool will block on while asking. It's fine to use it form outside of actors and
# global_task_pool.
# global_io_executor will block on while asking. It's fine to use it form outside of actors and
# global_io_executor.
#
# sends message to the actor and asks for the result of its processing, blocks
# @param [Object] message
Expand Down
Loading