Skip to content

Commit 1dc2ff8

Browse files
committed
Merge branch 'master' into actress
* master: (41 commits) Attempt to improve Dereferenceable shared specs. ...
2 parents 9906e23 + d82334d commit 1dc2ff8

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

56 files changed

+2842
-1356
lines changed

.rspec

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
11
--require spec_helper
22
--color
3-
--backtrace
43
--format documentation

.yardopts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
--protected
2-
--private
1+
--no-private
32
--embed-mixins
43
--output-dir ./yardoc
54
--markup markdown

Gemfile

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@ end
1818

1919
group :testing do
2020
gem 'rspec', '~> 3.2.0'
21+
gem 'timecop', '~> 0.7.3'
22+
23+
# Coverage
2124
gem 'simplecov', '~> 0.10.0', :require => false
2225
gem 'coveralls', '~> 0.8.1', :require => false
23-
gem 'timecop', '~> 0.7.3'
2426
end

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ This library contains a variety of concurrency abstractions at high and low leve
5353
### High-level, general-purpose asynchronous concurrency abstractions
5454

5555
* [Async](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/Async.html): A mixin module that provides simple asynchronous behavior to any standard class/object or object.
56+
* [Atom](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/Atom.html): A way to manage shared, synchronous, independent state.
5657
* [Future](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/Future.html): An asynchronous operation that produces a value.
5758
* [Dataflow](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent.html#dataflow-class_method): Built on Futures, Dataflow allows you to create a task that will be scheduled when all of its data dependencies are available.
5859
* [Promise](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/Promise.html): Similar to Futures, with more features.

Rakefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
#!/usr/bin/env rake
22

3+
$:.push File.join(File.dirname(__FILE__), 'lib')
4+
35
require 'concurrent/version'
46
require 'concurrent/native_extensions'
57

doc/agent.md

Lines changed: 0 additions & 82 deletions
This file was deleted.

doc/async.md

Lines changed: 0 additions & 134 deletions
This file was deleted.

examples/benchmark_new_futures.rb

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,37 @@
22
require 'concurrent'
33
require 'concurrent-edge'
44

5+
# require 'ruby-prof'
6+
#
7+
# result = RubyProf.profile do
8+
# 1000.times do
9+
# head = Concurrent.future { 1 }
10+
# branch1 = head.then(&:succ)
11+
# branch2 = head.then(&:succ).then(&:succ)
12+
# branch3 = head.then(&:succ).then(&:succ).then(&:succ)
13+
# Concurrent.join(branch1, branch2, branch3).then { |(a, b, c)| a + b + c }.value!
14+
# end
15+
# end
16+
#
17+
# printer = RubyProf::FlatPrinter.new(result)
18+
# printer.print(STDOUT)
19+
#
20+
# printer = RubyProf::GraphPrinter.new(result)
21+
# printer.print(STDOUT, {})
22+
#
23+
# exit
24+
525
scale = 1
626
time = 10 * scale
727
warmup = 2 * scale
828
warmup *= 10 if Concurrent.on_jruby?
929

30+
Benchmark.ips(time, warmup) do |x|
31+
x.report('flat-old') { Concurrent::Promise.execute { 1 }.flat_map { |v| Concurrent::Promise.execute { v + 2 } }.value! }
32+
x.report('flat-new') { Concurrent.future(:fast) { 1 }.then { |v| Concurrent.future(:fast) { v+ 1 } }.flat.value! }
33+
x.compare!
34+
end
35+
1036
Benchmark.ips(time, warmup) do |x|
1137
x.report('status-old') { f = Concurrent::Promise.execute { nil }; 100.times { f.complete? } }
1238
x.report('status-new') { f = Concurrent.future(:fast) { nil }; 100.times { f.completed? } }

lib/concurrent.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,13 @@
1313
require 'concurrent/struct'
1414

1515
require 'concurrent/atomic/atomic_reference'
16+
require 'concurrent/atom'
1617
require 'concurrent/async'
1718
require 'concurrent/dataflow'
1819
require 'concurrent/delay'
1920
require 'concurrent/future'
2021
require 'concurrent/ivar'
22+
require 'concurrent/maybe'
2123
require 'concurrent/mvar'
2224
require 'concurrent/promise'
2325
require 'concurrent/scheduled_task'

lib/concurrent/agent.rb

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,74 @@
77

88
module Concurrent
99

10-
# {include:file:doc/agent.md}
10+
# `Agent`s are inspired by [Clojure's](http://clojure.org/) [agent](http://clojure.org/agents) function. An `Agent` is a single atomic value that represents an identity. The current value of the `Agent` can be requested at any time (`deref`). Each `Agent` has a work queue and operates on the global thread pool (see below). Consumers can `post` code blocks to the `Agent`. The code block (function) will receive the current value of the `Agent` as its sole parameter. The return value of the block will become the new value of the `Agent`. `Agent`s support two error handling modes: fail and continue. A good example of an `Agent` is a shared incrementing counter, such as the score in a video game.
11+
#
12+
# An `Agent` must be initialize with an initial value. This value is always accessible via the `value` (or `deref`) methods. Code blocks sent to the `Agent` will be processed in the order received. As each block is processed the current value is updated with the result from the block. This update is an atomic operation so a `deref` will never block and will always return the current value.
13+
#
14+
# When an `Agent` is created it may be given an optional `validate` block and zero or more `rescue` blocks. When a new value is calculated the value will be checked against the validator, if present. If the validator returns `true` the new value will be accepted. If it returns `false` it will be rejected. If a block raises an exception during execution the list of `rescue` blocks will be seacrhed in order until one matching the current exception is found. That `rescue` block will then be called an passed the exception object. If no matching `rescue` block is found, or none were configured, then the exception will be suppressed.
15+
#
16+
# `Agent`s also implement Ruby's [Observable](http://ruby-doc.org/stdlib-2.0/libdoc/observer/rdoc/Observable.html). Code that observes an `Agent` will receive a callback with the new value any time the value is changed.
17+
#
18+
# @!macro copy_options
19+
#
20+
# @example Simple Example
21+
#
22+
# require 'concurrent'
23+
#
24+
# score = Concurrent::Agent.new(10)
25+
# score.value #=> 10
26+
#
27+
# score << proc{|current| current + 100 }
28+
# sleep(0.1)
29+
# score.value #=> 110
30+
#
31+
# score << proc{|current| current * 2 }
32+
# sleep(0.1)
33+
# score.value #=> 220
34+
#
35+
# score << proc{|current| current - 50 }
36+
# sleep(0.1)
37+
# score.value #=> 170
38+
#
39+
# @example With Validation and Error Handling
40+
#
41+
# score = Concurrent::Agent.new(0).validate{|value| value <= 1024 }.
42+
# rescue(NoMethodError){|ex| puts "Bam!" }.
43+
# rescue(ArgumentError){|ex| puts "Pow!" }.
44+
# rescue{|ex| puts "Boom!" }
45+
# score.value #=> 0
46+
#
47+
# score << proc{|current| current + 2048 }
48+
# sleep(0.1)
49+
# score.value #=> 0
50+
#
51+
# score << proc{|current| raise ArgumentError }
52+
# sleep(0.1)
53+
# #=> puts "Pow!"
54+
# score.value #=> 0
55+
#
56+
# score << proc{|current| current + 100 }
57+
# sleep(0.1)
58+
# score.value #=> 100
59+
#
60+
# @example With Observation
61+
#
62+
# bingo = Class.new{
63+
# def update(time, score)
64+
# puts "Bingo! [score: #{score}, time: #{time}]" if score >= 100
65+
# end
66+
# }.new
67+
#
68+
# score = Concurrent::Agent.new(0)
69+
# score.add_observer(bingo)
70+
#
71+
# score << proc{|current| sleep(0.1); current += 30 }
72+
# score << proc{|current| sleep(0.1); current += 30 }
73+
# score << proc{|current| sleep(0.1); current += 30 }
74+
# score << proc{|current| sleep(0.1); current += 30 }
75+
#
76+
# sleep(1)
77+
# #=> Bingo! [score: 120, time: 2013-07-22 21:26:08 -0400]
1178
#
1279
# @!attribute [r] timeout
1380
# @return [Fixnum] the maximum number of seconds before an update is cancelled

0 commit comments

Comments
 (0)