Skip to content

Commit beba284

Browse files
committed
Moved documentation from wiki to Yardoc.
1 parent 97e5e3c commit beba284

File tree

13 files changed

+709
-141
lines changed

13 files changed

+709
-141
lines changed

doc/agent.md

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
`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.
2+
3+
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.
4+
5+
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.
6+
7+
`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.
8+
9+
## Copy Options
10+
11+
Object references in Ruby are mutable. This can lead to serious problems when the value of an `Agent` is a mutable reference. Which is always the case unless the value is a `Fixnum`, `Symbol`, or similar "primative" data type. Each `Agent` instance can be configured with a few options that can help protect the program from potentially dangerous operations. Each of these options can be optionally set when the `Agent` is created:
12+
13+
* `:dup_on_deref` when true the `Agent` will call the `#dup` method on the `value` object every time the `#value` methid is called (default: false)
14+
* `:freeze_on_deref` when true the `Agent` will call the `#freeze` method on the `value` object every time the `#value` method is called (default: false)
15+
* `:copy_on_deref` when given a `Proc` object the `Proc` will be run every time the `#value` method is called. The `Proc` will be given the current `value` as its only parameter and the result returned by the block will be the return value of the `#value` call. When `nil` this option will be ignored (default: nil)
16+
17+
## Examples
18+
19+
A simple example:
20+
21+
```ruby
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+
40+
With validation and error handling:
41+
42+
```ruby
43+
score = Concurrent::Agent.new(0).validate{|value| value <= 1024 }.
44+
rescue(NoMethodError){|ex| puts "Bam!" }.
45+
rescue(ArgumentError){|ex| puts "Pow!" }.
46+
rescue{|ex| puts "Boom!" }
47+
score.value #=> 0
48+
49+
score << proc{|current| current + 2048 }
50+
sleep(0.1)
51+
score.value #=> 0
52+
53+
score << proc{|current| raise ArgumentError }
54+
sleep(0.1)
55+
#=> puts "Pow!"
56+
score.value #=> 0
57+
58+
score << proc{|current| current + 100 }
59+
sleep(0.1)
60+
score.value #=> 100
61+
```
62+
63+
With observation:
64+
65+
```ruby
66+
bingo = Class.new{
67+
def update(time, score)
68+
puts "Bingo! [score: #{score}, time: #{time}]" if score >= 100
69+
end
70+
}.new
71+
72+
score = Concurrent::Agent.new(0)
73+
score.add_observer(bingo)
74+
75+
score << proc{|current| sleep(0.1); current += 30 }
76+
score << proc{|current| sleep(0.1); current += 30 }
77+
score << proc{|current| sleep(0.1); current += 30 }
78+
score << proc{|current| sleep(0.1); current += 30 }
79+
80+
sleep(1)
81+
#=> Bingo! [score: 120, time: 2013-07-22 21:26:08 -0400]
82+
```

doc/async.md

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
A mixin module that provides simple asynchronous behavior to any standard
2+
class/object or object.
3+
4+
```cucumber
5+
Feature:
6+
As a stateful, plain old Ruby class/object
7+
I want safe, asynchronous behavior
8+
So my long-running methods don't block the main thread
9+
```
10+
11+
Stateful, mutable objects must be managed carefully when used asynchronously.
12+
But Ruby is an object-oriented language so designing with objects and classes
13+
plays to Ruby's strengths and is often more natural to many Ruby programmers.
14+
The `Async` module is a way to mix simple yet powerful asynchronous capabilities
15+
into any plain old Ruby object or class. These capabilities provide a reasonable
16+
level of thread safe guarantees when used correctly.
17+
18+
When this module is mixed into a class or object it provides to new methods:
19+
`async` and `await`. These methods are thread safe with respect to the enclosing
20+
object. The former method allows methods to be called asynchronously by posting
21+
to the global thread pool. The latter allows a method to be called synchronously
22+
on the current thread but does so safely with respect to any pending asynchronous
23+
method calls. Both methods return an `Obligation` which can be inspected for
24+
the result of the method call. Calling a method with `async` will return a
25+
`:pending` `Obligation` whereas `await` will return a `:complete` `Obligation`.
26+
27+
Very loosely based on the `async` and `await` keywords in C#.
28+
29+
#### An Important Note About Initialization
30+
31+
> This module depends on several internal synchronization objects that
32+
> must be initialized prior to calling any of the async/await/executor methods.
33+
> The best practice is to call `init_mutex` from within the constructor
34+
> of the including class. A less ideal but acceptable practice is for the
35+
> thread creating the asynchronous object to explicitly call the `init_mutex`
36+
> method prior to calling any of the async/await/executor methods. If
37+
> `init_mutex` is *not* called explicitly the async/await/executor methods
38+
> will raise a `Concurrent::InitializationError`. This is the only way
39+
> thread-safe initialization can be guaranteed.
40+
41+
#### An Important Note About Thread Safe Guarantees
42+
43+
> Thread safe guarantees can only be made when asynchronous method calls
44+
> are not mixed with synchronous method calls. Use only synchronous calls
45+
> when the object is used exclusively on a single thread. Use only
46+
> `async` and `await` when the object is shared between threads. Once you
47+
> call a method using `async`, you should no longer call any methods
48+
> directly on the object. Use `async` and `await` exclusively from then on.
49+
> With careful programming it is possible to switch back and forth but it's
50+
> also very easy to create race conditions and break your application.
51+
> Basically, it's "async all the way down."
52+
53+
### Examples
54+
55+
#### Defining an asynchronous class
56+
57+
```cucumber
58+
Scenario: Defining an asynchronous class
59+
Given a class definition
60+
When I include the Concurrent::Async module
61+
Then an `async` method is defined for all objects of the class
62+
And an `await` method is defined for all objects of the class
63+
64+
Scenario: Calling the `async` method
65+
Given a class which includes Concurrent::Async module
66+
When I call a normal method through the `async` delegate method
67+
Then the method returns a Concurrent::Future object
68+
And the method is executed on a background thread using the global thread pool
69+
And the current thread does not block
70+
And thread safety is provided with respect to other `async` and `await` calls
71+
And the returned future can be interrogated for the status of the method call
72+
And the returned future will eventually contain the `value` of the method call
73+
Or the returned future will eventually contain the `reason` the method call failed
74+
75+
Scenario: Calling the `await` method
76+
Given a class which includes Concurrent::Async module
77+
When I call a normal method through the `await` delegate method
78+
Then the method returns a Concurrent::IVar object
79+
And the method is executed on the current thread
80+
And thread safety is provided with respect to other `async` and `await` calls
81+
And the returned ivar will be in the :fulfilled state
82+
Or the returned ivar will be in the :rejected state
83+
And the returned ivar will immediately contain the `value` of the method call
84+
Or the returned ivar will immediately contain the `reason` the method call failed
85+
```
86+
87+
```ruby
88+
class Echo
89+
include Concurrent::Async
90+
91+
def initialize
92+
init_mutex # initialize the internal synchronization objects
93+
end
94+
95+
def echo(msg)
96+
sleep(rand)
97+
print "#{msg}\n"
98+
nil
99+
end
100+
end
101+
102+
horn = Echo.new
103+
horn.echo('zero') # synchronous, not thread-safe
104+
105+
horn.async.echo('one') # asynchronous, non-blocking, thread-safe
106+
horn.await.echo('two') # synchronous, blocking, thread-safe
107+
```
108+
109+
#### Monkey-patching an existing object
110+
111+
```cucumber
112+
Scenario: Monkey-patching an existing object
113+
Given an object of a class that does not include the Concurrent::Async module
114+
When I extend the object with the Concurrent::Async module
115+
Then an `async` method is monkey-patched onto the object
116+
And an `await` method is monkey-patched onto the object
117+
And the object behaved as though Concurrent::Async were included in the class
118+
And the `async` and `await` methods perform as expected
119+
And no other objects of that class are affected
120+
```
121+
122+
```ruby
123+
numbers = 1_000_000.times.collect{ rand }
124+
numbers.extend(Concurrent::Async)
125+
numbers.init_mutex # initialize the internal synchronization objects
126+
127+
future = numbers.async.max
128+
future.state #=> :pending
129+
130+
sleep(2)
131+
132+
future.state #=> :fulfilled
133+
future.value #=> 0.999999138918843
134+
```

0 commit comments

Comments
 (0)