OMG HELLO!
Friday, November 5, 2010
THANK YOU
Friday, November 5, 2010
Aaron Patterson
Friday, November 5, 2010
AT&T, AT&T logo and all AT&T related marks are trademarks of AT&T Intellectual Property and/or AT&T affiliated companies.
Friday, November 5, 2010
@tenderlove
Friday, November 5, 2010
nokogiri
Friday, November 5, 2010
ruby core
committer
Friday, November 5, 2010
YAML
Friday, November 5, 2010
Psych
Friday, November 5, 2010
DL
Friday, November 5, 2010
rails core
committer
Friday, November 5, 2010
rails core
committer
Friday, November 5, 2010
ActiveRecord
Friday, November 5, 2010
ARel
Friday, November 5, 2010
anywhere...
Friday, November 5, 2010
aaron.patterson@gmail.com
Friday, November 5, 2010
Friday, November 5, 2010
WWFMD?
Friday, November 5, 2010
Preparations
the @tenderlove way
Friday, November 5, 2010
What should
I talk about?
Friday, November 5, 2010
Yehuda?
Friday, November 5, 2010
Evan?
Friday, November 5, 2010
DHH?
Friday, November 5, 2010
I will talk
about
Concurrency!
Friday, November 5, 2010
Threads!
Friday, November 5, 2010
Iterators!
Friday, November 5, 2010
Generators!
Friday, November 5, 2010
Fibers!
Friday, November 5, 2010
Coroutines!
Friday, November 5, 2010
Friday, November 5, 2010
the @tenderlove
process for
producing slides
Friday, November 5, 2010
BOOKS ARE IN
STORES NOW!
Friday, November 5, 2010
ONLY $24.99!!!
Friday, November 5, 2010
BUY TODAY!
Friday, November 5, 2010
Fun with Fibers!
Friday, November 5, 2010
Concurrency
Friday, November 5, 2010
WARNING!
Friday, November 5, 2010
fork()
Friday, November 5, 2010
fork()
foo = "hello"
fork do
foo << " world"
puts foo
end
fork do
foo << " everyone!"
puts foo
end
puts foo
Process.wait
Friday, November 5, 2010
Copy on Write
Friday, November 5, 2010
Threads
Friday, November 5, 2010
Why Use Threads?
Friday, November 5, 2010
Friday, November 5, 2010
Thread API
Friday, November 5, 2010
thread = Thread.new {
... do some work ...
}
thread.join
new and join
Friday, November 5, 2010
100.times { Thread.new { ... } }
(Thread.list - [Thread.main]).each { |t|
t.join
}
list / main
Friday, November 5, 2010
Return Value
thread = Thread.new do
10
end
thread.join
thread.value # => 10
Friday, November 5, 2010
Shared Data
Friday, November 5, 2010
n = 0
def increment(v)
v + 1
end
threads = 2.times.map {
Thread.new {
1_000_000.times { n = increment(n) }
}
}
threads.each { |x| x.join }
puts n
Friday, November 5, 2010
2,000,000?
Friday, November 5, 2010
Friday, November 5, 2010
Friday, November 5, 2010
Read
Increment
Update
v = n
v += 1
n = v
n = 2
n = 2
n = 3
Friday, November 5, 2010
Thread 1 Thread 2
Friday, November 5, 2010
Read
Read
Thread 1 Thread 2
Increment
Increment
Update
Update
Friday, November 5, 2010
Read
Read
Thread 1 Thread 2
Increment
Increment
Update
Update
n = 2
n = 3
Friday, November 5, 2010
Solutions
Friday, November 5, 2010
Locking
Friday, November 5, 2010
Mutex
n = 0
mutex = Mutex.new
def increment(v)
v + 1
end
2.times do
Thread.new {
1_000_000.times {
mutex.synchronize { n = increment(n) }
}
}
end
Friday, November 5, 2010
Mutex
n = 0
mutex = Mutex.new
def increment(v)
v + 1
end
2.times do
Thread.new {
1_000_000.times {
mutex.synchronize { n = increment(n) }
}
}
end
Friday, November 5, 2010
Friday, November 5, 2010
Mutex_m
require 'mutex_m'
class Tenderlove
include Mutex_m
end
n = 0
mutex = Tenderlove.new
mutex.synchronize { ... }
Friday, November 5, 2010
Conditional Variable
Friday, November 5, 2010
mutex = Mutex.new
resource = ConditionVariable.new
a = Thread.new {
mutex.synchronize {
puts "hello"
resource.wait(mutex)
puts "world"
}
}
b = Thread.new {
mutex.synchronize {
puts "hello1"
sleep 1
resource.signal
}
}
a.join
b.join
Friday, November 5, 2010
mutex = Mutex.new
resource = ConditionVariable.new
a = Thread.new {
mutex.synchronize {
puts "hello"
resource.wait(mutex)
puts "world"
}
}
b = Thread.new {
mutex.synchronize {
puts "hello1"
sleep 1
resource.signal
}
}
a.join
b.join
Friday, November 5, 2010
mutex = Mutex.new
resource = ConditionVariable.new
a = Thread.new {
mutex.synchronize {
puts "hello"
resource.wait(mutex)
puts "world"
}
}
b = Thread.new {
mutex.synchronize {
puts "hello1"
sleep 1
resource.signal
}
}
a.join
b.join
Friday, November 5, 2010
Friday, November 5, 2010
Thread Safe
Objects
Friday, November 5, 2010
Queue
queue = Queue.new
[
Thread.new { 100.times {
queue << "omg!"
} },
Thread.new { 100.times {
queue << "omgagain!"
} },
].each { |t| t.join }
Friday, November 5, 2010
SizedQueue
Friday, November 5, 2010
Iterators
Friday, November 5, 2010
Internal
Friday, November 5, 2010
%w{ a b c d e }.each do
...
...
end
Friday, November 5, 2010
%w{ a b c d e }.each do
...
...
end
Friday, November 5, 2010
External
Friday, November 5, 2010
list = %w{ a b }
iter = Enumerator.new(list)
iter.next # => a
iter.next # => b
iter.next # raises StopIteration
Friday, November 5, 2010
list = %w{ a b }
iter = Enumerator.new(list)
begin
3.times do
iter.next
end
rescue StopIteration => e
p e.result
end
Friday, November 5, 2010
list = %w{ a b }
iter = Enumerator.new(list)
begin
3.times do
iter.next
end
rescue StopIteration => e
p e.result # => ["a", "b"]
end
Friday, November 5, 2010
External => Internal
Friday, November 5, 2010
list = %w{ a b }
iter = Enumerator.new(list)
iter.each do |thing|
p thing
end
Friday, November 5, 2010
list = %w{ a b }
iter = Enumerator.new(list)
iter.each do |thing|
p thing
end
Friday, November 5, 2010
Specify a Method
Friday, November 5, 2010
list = %w{ a b }
iter = Enumerator.new(list, :map)
x = iter.each do |thing|
thing + " hello!"
end
p x
Friday, November 5, 2010
list = %w{ a b }
iter = Enumerator.new(list, :map)
x = iter.each do |thing|
thing + " hello!"
end
p x # => ["a hello!", "b hello!"]
Friday, November 5, 2010
list = %w{ a b }
iter = Enumerator.new(list, :map)
x = iter.each_with_index do |thing, i|
thing + ":#{i}"
end
p x
Friday, November 5, 2010
list = %w{ a b }
iter = Enumerator.new(list, :map)
x = iter.each_with_index do |thing, i|
thing + ":#{i}"
end
p x # => ["a:0", "b:1"]
Friday, November 5, 2010
DRY it up
Friday, November 5, 2010
list = %w{ a b }
iter = list.enum_for(:each)
3.times do
p iter.next
end
Friday, November 5, 2010
list = %w{ a b }
iter = list.each
3.times do
p itr.next
end
Friday, November 5, 2010
list = %w{ a b }
list.map.each_with_index { |obj, i|
"#{obj} => #{i}"
}
Friday, November 5, 2010
list = %w{ a b }
list.map.each_with_index { |obj, i|
"#{obj} => #{i}"
} # => ["a => 0", "b => 1"]
Friday, November 5, 2010
Fun with Iterators!
Friday, November 5, 2010
Parallel Iteration
Friday, November 5, 2010
a = %w{ a b c d }
b = %w{ a b c d }
iter = a.each
b.all? { |v| iter.next == v }
Friday, November 5, 2010
Iteration Strategy
Friday, November 5, 2010
Tree
Node
NodeNode
NodeNode
Friday, November 5, 2010
Node
class Node < Struct.new(:children)
def initialize(children = [])
super
end
end
Friday, November 5, 2010
Bottom Up
class DepthFirst < Struct.new(:tree)
def each(&block)
iterate(tree, block)
end
private
def iterate(node, block)
node.children.each { |n|
iterate(n, block)
}
block.call node
end
end
Friday, November 5, 2010
Node
NodeNode
NodeNode
Bottom Up
Node
NodeNode
NodeNode
Friday, November 5, 2010
Node
NodeNode
NodeNode
Bottom Up
Node
NodeNode
NodeNode
Friday, November 5, 2010
Top Down
class TopDown < Struct.new(:tree)
def each(&block)
iterate(tree, block)
end
private
def iterate(node, block)
block.call node
node.children.each { |n|
iterate(n, block)
}
end
end
Friday, November 5, 2010
Node
NodeNode
NodeNode
Top Down
Node
NodeNode
NodeNode
Friday, November 5, 2010
Node
NodeNode
NodeNode
Top Down
Node
NodeNode
NodeNode
Friday, November 5, 2010
Node
class Node < Struct.new(:children)
def initialize(children = [])
super
end
def each(&block)
return enum_for(:each) unless block_given?
DepthFirst.new(self).each(&block)
end
end
Friday, November 5, 2010
Parallel Iteration
iter = tree1.each
tree2.all? { |node| node == iter.next }
Friday, November 5, 2010
To Infinity, and
Beyond!
Friday, November 5, 2010
Infinite Series
n → ∞
Friday, November 5, 2010
class Infinity
include Enumerable
def each
return enum_for(:each) unless block_given?
i = 0
loop do
yield i
i += 1
end
end
end
Friday, November 5, 2010
Friday, November 5, 2010
infinity = Enumerator.new do |yielder|
i = 0
loop do
yielder << i
i += 1
end
end
Friday, November 5, 2010
infinity = Enumerator.new do |yielder|
i = 0
loop do
yielder << i
i += 1
end
end
Friday, November 5, 2010
Friday, November 5, 2010
π
Pi
Friday, November 5, 2010
Mādhavan of Sangamagrāmam
Friday, November 5, 2010
Friday, November 5, 2010
pi = Enumerator.new do |yielder|
root_12 = Math.sqrt(12)
pi = 0
infinity.each do |k|
pi += (-1 / 3.0) ** k / (2 * k + 1).to_f
yielder << (root_12 * pi)
end
end
Friday, November 5, 2010
pi = Enumerator.new do |yielder|
root_12 = Math.sqrt(12)
pi = 0
infinity.each do |k|
pi += (-1 / 3.0) ** k / (2 * k + 1).to_f
yielder << (root_12 * pi)
end
end
Friday, November 5, 2010
Friday, November 5, 2010
Generators
Friday, November 5, 2010
Iterator.is_a?
Generator
Friday, November 5, 2010
Generator Client
Friday, November 5, 2010
Generator Client
Value
Friday, November 5, 2010
Fibers
Friday, November 5, 2010
Abstract Methods
Friday, November 5, 2010
method = Proc.new { ... }
method.call # => "hello"
method.call # => "world"
method.call # => "how are you?"
Friday, November 5, 2010
method = Proc.new { ... }
method.call('1') # => "1: hello"
method.call('2') # => "2: world"
method.call('3') # => "3: how are you?"
Friday, November 5, 2010
Proc => Fiber
Friday, November 5, 2010
call => resume
Friday, November 5, 2010
method = Proc.new { ... }
method.call('1') # => "1: hello"
method.call('2') # => "2: world"
method.call('3') # => "3: how are you?"
Friday, November 5, 2010
method = Fiber.new { ... }
method.resume('1') # => "1: hello"
method.resume('2') # => "2: world"
method.resume('3') # => "3: how are you?"
Friday, November 5, 2010
Arbitrary Exits
Friday, November 5, 2010
method = Fiber.new do
Fiber.yield "hello"
Fiber.yield "world"
end
method.resume # => "hello"
method.resume # => "world"
method.resume # => nil
method.resume # => (FiberError)
Friday, November 5, 2010
Accept Parameters
Friday, November 5, 2010
method = Fiber.new do |val|
val = Fiber.yield "#{val}: hello"
val = Fiber.yield "#{val}: world"
val
end
method.resume(1) # => "1: hello"
method.resume(2) # => "2: world"
method.resume(3) # => 3
method.resume(4) # => (FiberError)
Friday, November 5, 2010
method = Fiber.new do |val|
val = Fiber.yield "#{val}: hello"
val = Fiber.yield "#{val}: world"
val
end
method.resume(1) # => "1: hello"
method.resume(2) # => "2: world"
method.resume(3) # => 3
method.resume(4) # => (FiberError)
Friday, November 5, 2010
method = Fiber.new do |val|
val = Fiber.yield "#{val}: hello"
val = Fiber.yield "#{val}: world"
val
end
method.resume(1) # => "1: hello"
method.resume(2) # => "2: world"
method.resume(3) # => 3
method.resume(4) # => (FiberError)
Friday, November 5, 2010
First "resume" is
the block argument
Friday, November 5, 2010
method = Fiber.new do |val|
val = Fiber.yield "#{val}: hello"
val = Fiber.yield "#{val}: world"
val
end
method.resume(1) # => "1: hello"
method.resume(2) # => "2: world"
method.resume(3) # => 3
method.resume(4) # => (FiberError)
Friday, November 5, 2010
The rest are
returned by
Fiber.yield
Friday, November 5, 2010
method = Fiber.new do |val|
val = Fiber.yield "#{val}: hello"
val = Fiber.yield "#{val}: world"
val
end
method.resume(1) # => "1: hello"
method.resume(2) # => "2: world"
method.resume(3) # => 3
method.resume(4) # => (FiberError)
Friday, November 5, 2010
Cool story bro
Friday, November 5, 2010
Friday, November 5, 2010
Callbacks -> FSM
Friday, November 5, 2010
YAML
because XML is boring
Friday, November 5, 2010
YAML
yes, I wrote nokogiri. :-P
Friday, November 5, 2010
Psych
Ruby 1.9.2 Only
Friday, November 5, 2010
class Foo < Psych::Handler
def start_document(...)
...
end
def end_document(...)
...
end
end
SAX Style Handler
Friday, November 5, 2010
Event Handler
class EventHandler
def initialize(fiber)
@fiber = fiber
end
def method_missing(name, *args)
@fiber.resume(name, args)
end
end
Friday, November 5, 2010
fiber = Fiber.new do |method, args|
stack = []
loop do
case method
when :start_mapping
stack << []
when :end_mapping
h = Hash[*stack.pop]
if stack.empty?
p h
else
stack.last << h
end
when :scalar
stack.last << args.first
end
method, args = Fiber.yield
end
end
Friday, November 5, 2010
case method
when :start_mapping
stack << []
when :end_mapping
h = Hash[*stack.pop]
if stack.empty?
stdout.resume h
else
stack.last << h
end
when :scalar
stack.last << args.first
end
method, args = Fiber.yield
Friday, November 5, 2010
StdOut Fiber
stdout = Fiber.new do |obj|
loop do
p obj
obj = Fiber.yield
end
end
Friday, November 5, 2010
Tied Together
parser = Psych::Parser.new EventHandler.new
fiber
parser.parse(<<-eoyaml)
---
- foo: { fiz: buzz }
- bar: { fiz: buzz }
- baz: { fiz: buzz }
eoyaml
Friday, November 5, 2010
Friday, November 5, 2010
Filtering Results
Friday, November 5, 2010
Foo Filter
foo_filter = Fiber.new do |hash|
loop do
stdout.resume hash if hash.key? 'foo'
hash = Fiber.yield
end
end
Friday, November 5, 2010
Output
{"foo"=>{"fiz"=>"buzz"}}
Friday, November 5, 2010
Scheduling
Threads Without Threads
Friday, November 5, 2010
P1
P2
Processes
Friday, November 5, 2010
P1
P2
Processes
fopen()
fread()
fseek()
fopen()
fopen()
Friday, November 5, 2010
Simple Example
Friday, November 5, 2010
Producer
producer = Fiber.new do
loop do
5.times do |i|
queue << "hello :#{i}"
end
Fiber.yield
end
end
Friday, November 5, 2010
Consumer
hungry = Fiber.new do
loop do
while !queue.empty?
puts queue.pop
end
Fiber.yield
end
end
Friday, November 5, 2010
Scheduler
fibers = [producer, hungry]
loop do
fibers.each do |fiber|
fiber.resume
end
end
Friday, November 5, 2010
Slow Consumer
slow = Fiber.new do
loop do
while !queue.empty?
sleep 1
puts "########### slow: " + queue.pop
Fiber.yield
end
Fiber.yield
end
end
Friday, November 5, 2010
Random Scheduler
fibers = [producer, hungry, slow]
loop do
fibers.sort_by { rand }.each do |fiber|
fiber.resume
end
end
Friday, November 5, 2010
Friday, November 5, 2010
"Processes"
•Anonymous
•We control when to "give up"
Friday, November 5, 2010
Homework
•Favor fast processes
•Favor queue fillers
•Favor queue drainers
•Deal with multiple queues
Friday, November 5, 2010
Generators
The Fiber Edition
Friday, November 5, 2010
infinity = Fiber.new do
i = 0
loop do
Fiber.yield i
i += 1
end
end
Friday, November 5, 2010
infinity = Enumerator.new do |yielder|
i = 0
loop do
yielder << i
i += 1
end
end
Friday, November 5, 2010
Our Own
Enumerator
Friday, November 5, 2010
class SimpleEnum
def initialize &block
@fiber = Fiber.new do
block.call(self)
end
end
def << value
Fiber.yield value
end
def next
@fiber.resume
end
end
Friday, November 5, 2010
infinity = SimpleEnum.new do |yielder|
i = 0
loop do
yielder << i
i += 1
end
end
Friday, November 5, 2010
•Capture return value
•Add StopIteration Exception
Homework
Friday, November 5, 2010
Coroutines
Friday, November 5, 2010
Fiber implements
Coroutine
Friday, November 5, 2010
Coroutine Client
Value
Friday, November 5, 2010
Coroutine Client
Value
Friday, November 5, 2010
Pipelines
Friday, November 5, 2010
Coroutine CoroutineGenerator
Friday, November 5, 2010
Coroutine CoroutineGenerator
Friday, November 5, 2010
Our Friend, Infinity
infinity = Fiber.new do
i = 0
loop do
Fiber.yield i
i += 1
end
end
Friday, November 5, 2010
Even Numbers
evens = Fiber.new do
loop do
value = infinity.resume
Fiber.yield value if value % 2 == 0
end
end
Friday, November 5, 2010
Friday, November 5, 2010
Three's
threes = Fiber.new do
loop do
value = evens.resume
Fiber.yield value if value % 3 == 0
end
end
Friday, November 5, 2010
Three's
threes = Fiber.new do
loop do
value = evens.resume
Fiber.yield value if value % 3 == 0
end
end
Friday, November 5, 2010
Friday, November 5, 2010
Abstraction
Friday, November 5, 2010
Common Elements
•Source
•Sink
•Runloop
•Work
Friday, November 5, 2010
class Pipe < Struct.new(:source)
def initialize(source = nil)
super
@fiber = Fiber.new { self.loop }
end
def loop
while value = read
process value
end
end
def read; source.resume end
def write(value); Fiber.yield(value) end
def process(value); write(value) end
def resume; @fiber.resume end
end
Friday, November 5, 2010
class Infinity < Pipe
def loop
i = 0
while true
write i
i += 1
end
end
end
Infinity (again)
Friday, November 5, 2010
Evens
class Evens < Pipe
def process(value)
super if value % 2 == 0
end
end
Friday, November 5, 2010
StdOut
class StdOut < Pipe
def write value
puts value
end
end
Friday, November 5, 2010
Connection
infinity = Infinity.new
even = Evens.new infinity
stdout = StdOut.new even
stdout.resume
Friday, November 5, 2010
Pretty Syntax
class Pipe
def |(other)
other.source = self
other
end
end
Friday, November 5, 2010
Connection
(Infinity.new | Evens.new | StdOut.new).resume
Friday, November 5, 2010
Producer / Consumer
Friday, November 5, 2010
ERb
Friday, November 5, 2010
Compiled Template
template = ERB.new('<%= "hello" %>')
puts template.src
Friday, November 5, 2010
Compiled Template
#coding:US-ASCII
_erbout = '';
_erbout.concat(( "hello" ).to_s);
_erbout.force_encoding(__ENCODING__)
Friday, November 5, 2010
Redirect Output
Friday, November 5, 2010
Compiled Template
erb = ERB.new('<%= "hello" %>', nil, nil, 'self')
puts erb.src
Friday, November 5, 2010
Compiled Template
#coding:US-ASCII
self = '';
self.concat(( "hello" ).to_s);
self.force_encoding(__ENCODING__)
Friday, November 5, 2010
Compiled Template
#coding:US-ASCII
self = '';
self.concat(( "hello" ).to_s);
self.force_encoding(__ENCODING__)
Friday, November 5, 2010
Monkey Patch!
class ERB::Compiler
def pre_cmd= x; end
end
erb = ERB.new('<%= "hello" %>', nil, nil,
'self')
puts erb.src
Friday, November 5, 2010
Compiled Source
#coding:US-ASCII
self.concat(( "hello" ).to_s);
self.force_encoding(__ENCODING__)
Friday, November 5, 2010
ERb Pipe
Friday, November 5, 2010
ERb Pipe
class PipERB < Pipe
def initialize data
super()
@erb = ERB.new(data, nil, nil, 'self')
end
alias :concat :process
def read; @erb.result(binding) end
def force_encoding encoding; end
end
Friday, November 5, 2010
Pipe to StdOut
(PipERB.new("<%= 'hello' %>") | StdOut.new).resume
Friday, November 5, 2010
ERb Pipe + Rack
Friday, November 5, 2010
Slow Template
<ul>
<% a = b = 1 %>
<% 10.times do %>
<li><%= a %></li>
<% sleep(1) # simulate work %>
<% a, b = b, a + b %>
<% end %>
</ul>
Friday, November 5, 2010
Normal Handler
class ERBHandler
def initialize
@fib_template = DATA.read
end
def call env
result = [
ERB.new(@fib_template).result(binding)
]
[ 200, {'X-Hello' => 'World'}, result ]
end
end
Friday, November 5, 2010
Friday, November 5, 2010
Rack API
Demands "each"
Friday, November 5, 2010
Each Pipe
class Each < Pipe
def each(&block)
@block = block
resume
end
def process(value)
@block.call(value)
end
end
Friday, November 5, 2010
Each Pipe
(PipERB.new(DATA.read) | Each.new).each do |chunk|
print chunk
end
__END__
<ul>
<% a = b = 1 %>
<% 10.times do %>
<li><%= a %></li>
<% sleep 1; a, b = b, a + b %>
<% end %>
</ul>
Friday, November 5, 2010
Each Pipe
(PipERB.new(DATA.read) | Each.new).each do |chunk|
print chunk
end
__END__
<ul>
<% a = b = 1 %>
<% 10.times do %>
<li><%= a %></li>
<% sleep 1; a, b = b, a + b %>
<% end %>
</ul>
Friday, November 5, 2010
Fiber Handler
class ERBHandler
def initialize
@fib_template = DATA.read
end
def call env
[ 200, {},
(PipERB.new(@fib_template) | Each.new)
]
end
end
Friday, November 5, 2010
Friday, November 5, 2010
Cooperative
Scheduling
Friday, November 5, 2010
ERb
Rack
Friday, November 5, 2010
Caveats?
Friday, November 5, 2010
Conclusion
Friday, November 5, 2010
Two Pipelines
Friday, November 5, 2010
Real Life?
Friday, November 5, 2010
Fibers are
Interesting!
Friday, November 5, 2010
Concurrency is
Interesting
Friday, November 5, 2010
But it requires a
shift in thinking
Friday, November 5, 2010
Install Ruby 1.9.2
Friday, November 5, 2010
Play with this at
home!
Friday, November 5, 2010
Questions?
Friday, November 5, 2010
Friday, November 5, 2010

RubyConf Brazil 2010