Skip to content

Commit

Permalink
Merge dc3bf17 into e4dd3de
Browse files Browse the repository at this point in the history
  • Loading branch information
Petr Chalupa committed Jun 6, 2014
2 parents e4dd3de + dc3bf17 commit 63ef73f
Show file tree
Hide file tree
Showing 28 changed files with 777 additions and 194 deletions.
1 change: 1 addition & 0 deletions .travis.yml
Expand Up @@ -15,3 +15,4 @@ matrix:
- rvm: ruby-head
- rvm: jruby-head
- rvm: 1.9.3
script: "bundle exec rspec --color --backtrace --seed 1 --format documentation ./spec"
7 changes: 6 additions & 1 deletion .yardopts
@@ -1 +1,6 @@
--protected --no-private --embed-mixins --output-dir ./doc --markup markdown
--protected
--no-private
--embed-mixins
--output-dir ./doc
--markup markdown
--use-cache
1 change: 1 addition & 0 deletions Gemfile
Expand Up @@ -8,6 +8,7 @@ group :development do
gem 'countloc', '~> 0.4.0', platforms: :mri
gem 'yard', '~> 0.8.7.4'
gem 'inch', '~> 0.4.1', platforms: :mri
gem 'redcarpet', platforms: :mri # understands github markdown
end

group :testing do
Expand Down
7 changes: 5 additions & 2 deletions README.md
Expand Up @@ -40,14 +40,16 @@ The design goals of this gem are:
```shell
gem install concurrent-ruby
```

or add the following line to Gemfile:

```ruby
gem 'concurrent-ruby'
```

and run `bundle install` from your shell.

*NOTE: There is an old gem from 2007 called "concurrent" that does not appear to be under active development. That isn't us. Please do not run* `gem install concurrent`*. It is not the droid you are looking for.*
_NOTE: There is an old gem from 2007 called "concurrent" that does not appear to be under active development. That isn't us. Please do not run* `gem install concurrent`*. It is not the droid you are looking for._

## Features & Documentation

Expand All @@ -74,6 +76,7 @@ into several general groups:
* Thread synchronization classes and algorithms including [dataflow](https://github.com/jdantonio/concurrent-ruby/wiki/Dataflow),
timeout, condition, countdown latch, dependency counter, and event
* Java-inspired [thread pools](https://github.com/jdantonio/concurrent-ruby/wiki/Thread%20Pools)
* New fast light-weighted [Actor model](http://rubydoc.info/gems/concurrent-ruby/Concurrent/Actress) implementation.
* And many more...

### Semantic Versioning
Expand All @@ -91,7 +94,7 @@ It should be fully compatible with any interpreter that is compliant with Ruby 1
Many more code examples can be found in the documentation for each class (linked above).
This one simple example shows some of the power of this gem.

```ruby
```ruby
require 'concurrent'
require 'thread' # for Queue
require 'open-uri' # for open(uri)
Expand Down
96 changes: 96 additions & 0 deletions examples/actress/celluloid_benchmark.rb
@@ -0,0 +1,96 @@
require 'benchmark'
require 'concurrent/actress'
Concurrent::Actress.i_know_it_is_experimental!
require 'celluloid'
require 'celluloid/autostart'

logger = Logger.new($stderr)
logger.level = Logger::INFO
Concurrent.configuration.logger = lambda do |level, progname, message = nil, &block|
logger.add level, message, progname, &block
end

scale = 1
ADD_TO = (100 * scale).to_i
counts_size = (500 * scale).to_i
adders_size = (500 * scale).to_i

class Counter
include Celluloid

def initialize(adders, i)
@adders = adders
@i = i
end

def counting(count, ivar)
if count < ADD_TO
@adders[(@i+1) % @adders.size].counting count+1, ivar
else
ivar.set count
end
end
end

threads = []

Benchmark.bmbm(10) do |b|
[2, adders_size, adders_size*2, adders_size*3].each do |adders_size|

b.report(format('%5d %4d %s', ADD_TO*counts_size, adders_size, 'actress')) do
counts = Array.new(counts_size) { [0, Concurrent::IVar.new] }
adders = Array.new(adders_size) do |i|
Concurrent::Actress::AdHoc.spawn("adder#{i}") do
lambda do |(count, ivar)|
if count.nil?
terminate!
else
if count < ADD_TO
adders[(i+1) % adders_size].tell [count+1, ivar]
else
ivar.set count
end
end
end
end
end

counts.each_with_index do |count, i|
adders[i % adders_size].tell count
end

counts.each do |count, ivar|
raise unless ivar.value >= ADD_TO
end

threads << Thread.list.size

adders.each { |a| a << [nil, nil] }
end

b.report(format('%5d %4d %s', ADD_TO*counts_size, adders_size, 'celluloid')) do
counts = []
counts_size.times { counts << [0, Concurrent::IVar.new] }

adders = []
adders_size.times do |i|
adders << Counter.new(adders, i)
end

counts.each_with_index do |count, i|
adders[i % adders_size].counting *count
end

counts.each do |count, ivar|
raise unless ivar.value >= ADD_TO
end

threads << Thread.list.size

adders.each(&:terminate)
end
end
end

p threads

76 changes: 76 additions & 0 deletions examples/actress/format.rb
@@ -0,0 +1,76 @@
require 'rubygems'
require 'bundler/setup'
require 'pry'
require 'pp'

input_paths = if ARGV.empty?
Dir.glob("#{File.dirname(__FILE__)}/*.in.rb")
else
ARGV
end.map { |p| File.expand_path p }

input_paths.each_with_index do |input_path, i|

pid = fork do
require 'concurrent/actress'
Concurrent::Actress.i_know_it_is_experimental!

begin
output_path = input_path.gsub /\.in\.rb$/, '.out.rb'
input = File.readlines(input_path)

chunks = []
line = ''

while !input.empty?
line += input.shift
if Pry::Code.complete_expression? line
chunks << line
line = ''
end
end

raise unless line.empty?

chunks.map! { |chunk| [chunk, [chunk.split($/).size, 1].max] }
environment = Module.new.send :binding
evaluate = ->(code, line) do
eval(code, environment, input_path, line)
end

indent = 50

line_count = 1
output = ''
chunks.each do |chunk, lines|
result = evaluate.(chunk, line_count)
unless chunk.strip.empty? || chunk =~ /\A *#/
pre_lines = chunk.lines.to_a
last_line = pre_lines.pop
output << pre_lines.join

if last_line =~ /\#$/
output << last_line.gsub(/\#$/, '')
else
if last_line.size < indent && result.inspect.size < indent
output << "%-#{indent}s %s" % [last_line.chomp, "# => #{result.inspect}\n"]
else
output << last_line << " # => #{result.inspect}\n"
end
end
else
output << chunk
end
line_count += lines
end

puts "#{input_path}\n -> #{output_path}"
#puts output
File.write(output_path, output)
rescue => ex
puts "#{ex} (#{ex.class})\n#{ex.backtrace * "\n"}"
end
end

Process.wait pid
end
84 changes: 84 additions & 0 deletions examples/actress/quick.in.rb
@@ -0,0 +1,84 @@
class Counter
# Include context of an actor which gives this class access to reference and other information
# about the actor, see CoreDelegations.
include Concurrent::Actress::Context

# use initialize as you wish
def initialize(initial_value)
@count = initial_value
end

# override on_message to define actor's behaviour
def on_message(message)
case message
when Integer
@count += message
when :terminate
terminate!
else
raise 'unknown'
end
end
end

# Create new actor naming the instance 'first'.
# Return value is a reference to the actor, the actual actor is never returned.
counter = Counter.spawn(:first, 5)

# Tell a message and forget returning self.
counter.tell(1)
counter << 1
# (First counter now contains 7.)

# Send a messages asking for a result.
counter.ask(0).class
counter.ask(0).value

# Terminate the actor.
counter.tell(:terminate)
# Not terminated yet, it takes a while until the message is processed.
counter.terminated?
# Waiting for the termination.
counter.terminated.class
counter.terminated.wait
counter.terminated?
# Any subsequent messages are rejected.
counter.ask(5).wait.rejected?

# Failure on message processing terminates the actor.
counter = Counter.spawn(:first, 0)
counter.ask('boom').wait.rejected?
counter.terminated?


# Lets define an actor creating children actors.
class Node
include Concurrent::Actress::Context

def on_message(message)
case message
when :new_child
spawn self.class, :child
when :children
children
when :terminate
terminate!
else
raise 'unknown'
end
end
end

# Actors are tracking parent-child relationships
parent = Node.spawn :parent
child = parent.ask!(:new_child)
child.parent
parent.ask!(:children)

# There is a special root actor which is used for all actors spawned outside any actor.
parent.parent

# Termination of an parent will also terminate all children.
parent.ask('boom').wait #
parent.terminated?
child.terminated?

0 comments on commit 63ef73f

Please sign in to comment.