A primer
A primer
A concurrent paradigm where
Processes communicate
by passing messages
over channels
A concurrent paradigm where
Processes Fibers communicate
by passing messages
over Channels
A concurrent paradigm where
Processes Fibers communicate
by passing messages
over channels
Fibers
Lightweight
Cooperative
Unit of execution
A concurrent paradigm where
Processes Fibers communicate
by passing messages
over Channels
Channels
blocking #send and #receive
similar to queues
Regulate data exchange between fibers
Alice writes letters
Bob puts them in envelops
Charles ships them
ch = Channel(Msg).new
msg = Msg.new(uuid)
ch.send msg
puts ch.receive
ch = Channel(Msg).new
msg = Msg.new(uuid)
ch.send msg
puts ch.receive
ch = Channel(Msg).new
msg = Msg.new(uuid)
ch.send msg
puts ch.receive
ch = Channel(Msg).new
msg = Msg.new(uuid)
ch.send msg
puts ch.receive
X
ch = Channel(Msg).new
msg = Msg.new(uuid)
ch.send msg
puts ch.receive
X
ch = Channel(Msg).new
msg = Msg.new(uuid)
ch.send msg
puts ch.receive
Cook
Waiter
Window
Chicken dish
Sending fiber
Channel
Message
Receiving fiber
X
ch = Channel(Msg).new
msg = Msg.new(uuid)
ch.send msg
puts ch.receive
Examples of (potentially) blocking operations:
Fiber.yield
sleep 2
HTTP.get(url)
ch.receive
ch.send obj
fiber A
Thread
Scheduler
Thread
Scheduler
fiber A
fiber B
Scheduler
Thread
fiber A
fiber B
spawn(name: "fiber A") do
spawn(name: "fiber Z") do
# do something
end
msg = ch.receive
end
fiber Z
Scheduler
Thread
fiber A
fiber B
spawn(name: "fiber A") do
spawn(name: "fiber Z") do
# do something
end
msg = ch.receive
end
fiber Z
Scheduler
Thread
fiber A
Thread
fiber B
spawn(name: "fiber A") do
spawn(name: "fiber Z") do
# do something
end
msg = ch.receive
end
fiber Z
Scheduler
Thread
fiber A
Thread
fiber B
fiber Z
Scheduler
Thread
Thread
fiber B
fiber A
fiber Z
Scheduler
Thread
fiber A
Thread
fiber B
fiber C
fiber Z
Scheduler
Thread
Thread
fiber C
fiber A
fiber B
spawn(name: "fiber B") do
Fiber.yield
end
fiber Z
Scheduler
Thread
Thread
fiber C
fiber A
fiber B
fiber Z
Scheduler
Thread
Thread
fiber C
fiber A
fiber B
fiber Z
ch = Channel(Msg).new
msg = Msg.new(uuid)
spawn(name: "processor") do
puts ch.receive
end
ch.send msg
ch = Channel(Msg).new
msg = Msg.new(uuid)
spawn(name: "processor") do
puts ch.receive
end
ch.send msg
ch = Channel(Msg).new
msg = Msg.new(uuid)
spawn(name: "processor") do
puts ch.receive
end
ch.send msg
ch = Channel(Msg).new
msg = Msg.new(uuid)
spawn(name: "processor") do
puts ch.receive
end
ch.send msg
X
ch = Channel(Msg).new
msg = Msg.new(uuid)
spawn(name: "processor") do
puts ch.receive
end
ch.send msg
X
ch = Channel(Msg).new
msg = Msg.new(uuid)
spawn(name: "processor") do
puts ch.receive
end
ch.send msg
X
ch = Channel(Msg).new
msg = Msg.new(uuid)
spawn(name: "processor") do
puts ch.receive
end
ch.send msg
ch = Channel(Msg).new
msg = Msg.new(uuid)
spawn(name: "processor") do
puts ch.receive
end
ch.send msg
Termination
ch = Channel(Msg).new
msg = Msg.new(uuid)
spawn(name: "processor") do
puts ch.receive
end
ch.send msg
Fibers don't block
ch = Channel(Msg).new(capacity: 1)
msg = Msg.new(uuid)
ch.send msg
puts ch.receive
ch = Channel(Msg).new(capacity: 1)
msg = Msg.new(uuid)
ch.send msg
puts ch.receive
Terminates after printing
ch = Channel(Msg).new(capacity: 1024)
msg = Msg.new(uuid)
spawn(name: "processor") do
puts ch.receive
end
ch.send msg
ch = Channel(Msg).new(capacity: 1024)
msg = Msg.new(uuid)
spawn(name: "processor") do
puts ch.receive
end
ch.send msg
No output
When analysing correctness, do not base your reasoning on the order in which fibers run
Depends on runtime implementation
Do not increase channels' capacity as a way out of bugs
For a start, initialise all your channels to zero capacity
Rely on causality
spawn
Rely on causality
"The order in which fibers run should not affect the correctness of your software"
should
correctness
order
"The order in which fibers run should not affect the correctness of your software"
not affect
Fibers
Channel
Message
spawn do
# your concurrent task!
end
To start a Fiber we
ch = Channel(Job).new
Creating a Channel
Sending & receiving
ch.send(Msg.new(uuid))
msg = ch.receive
ch = Channel(Msg).new
2.times.each { |i|
spawn(name: "processor_#{i}") do
loop do
puts "#{Fiber.current.name}: #{ch.receive}"
end
end
}
loop do
ch.send Msg.new(UUID.random)
sleep 0.5
end
Consumers
ch = Channel(Msg).new
2.times.each { |i|
spawn(name: "processor_#{i}") do
loop do
puts "#{Fiber.current.name}: #{ch.receive}"
end
end
}
loop do
ch.send Msg.new(UUID.random)
sleep 0.5
end
Producer
ch = Channel(Msg).new
2.times.each { |i|
spawn(name: "processor_#{i}") do
loop do
puts "#{Fiber.current.name}: #{ch.receive}"
end
end
}
loop do
ch.send Msg.new(UUID.random)
sleep 0.5
end
ch = Channel(Msg).new
2.times.each { |i|
spawn(name: "processor_#{i}") do
loop do
puts "#{Fiber.current.name}: #{ch.receive}"
end
end
}
loop do
ch.send Msg.new(UUID.random)
sleep 0.5
end
X
ch = Channel(Msg).new
2.times.each { |i|
spawn(name: "processor_#{i}") do
loop do
puts "#{Fiber.current.name}: #{ch.receive}"
end
end
}
loop do
ch.send Msg.new(UUID.random)
sleep 0.5
end
X
ch = Channel(Msg).new
2.times.each { |i|
spawn(name: "processor_#{i}") do
loop do
puts "#{Fiber.current.name}: #{ch.receive}"
end
end
}
loop do
ch.send Msg.new(UUID.random)
sleep 0.5
end
X
X
ch = Channel(Msg).new
2.times.each { |i|
spawn(name: "processor_#{i}") do
loop do
puts "#{Fiber.current.name}: #{ch.receive}"
end
end
}
loop do
ch.send Msg.new(UUID.random)
sleep 0.5
end
X
X
ch = Channel(Msg).new
2.times.each { |i|
spawn(name: "processor_#{i}") do
loop do
puts "#{Fiber.current.name}: #{ch.receive}"
end
end
}
loop do
ch.send Msg.new(UUID.random)
sleep 0.5
end
X
X
ch = Channel(Msg).new
2.times.each { |i|
spawn(name: "processor_#{i}") do
loop do
puts "#{Fiber.current.name}: #{ch.receive}"
end
end
}
loop do
ch.send Msg.new(UUID.random)
sleep 0.5
end
X
X
ch = Channel(Msg).new
2.times.each { |i|
spawn(name: "processor_#{i}") do
loop do
puts "#{Fiber.current.name}: #{ch.receive}"
end
end
}
loop do
ch.send Msg.new(UUID.random)
sleep 0.5
end
X
X
X
ch = Channel(Msg).new
2.times.each { |i|
spawn(name: "processor_#{i}") do
loop do
puts "#{Fiber.current.name}: #{ch.receive}"
end
end
}
loop do
ch.send Msg.new(UUID.random)
sleep 0.5
end
X
X
ch = Channel(Msg).new
2.times.each { |i|
spawn(name: "processor_#{i}") do
loop do
puts "#{Fiber.current.name}: #{ch.receive}"
end
end
}
loop do
ch.send Msg.new(UUID.random)
sleep 0.5
end
X
X
X
ch = Channel(Msg).new
2.times.each { |i|
spawn(name: "processor_#{i}") do
loop do
puts "#{Fiber.current.name}: #{ch.receive}"
end
end
}
loop do
ch.send Msg.new(UUID.random)
sleep 0.5
end
X
X
ch = Channel(Msg).new
2.times.each { |i|
spawn(name: "processor_#{i}") do
loop do
puts "#{Fiber.current.name}: #{ch.receive}"
end
end
}
loop do
ch.send Msg.new(UUID.random)
sleep 0.5
end
X
X
X
ch = Channel(Msg).new
2.times.each { |i|
spawn(name: "processor_#{i}") do
loop do
puts "#{Fiber.current.name}: #{ch.receive}"
end
end
}
loop do
ch.send Msg.new(UUID.random)
sleep 0.5
end
X
X
ch = Channel(Msg).new
2.times.each { |i|
spawn(name: "processor_#{i}") do
loop do
puts "#{Fiber.current.name}: #{ch.receive}"
end
end
}
loop do
ch.send Msg.new(UUID.random)
sleep 0.5
end
X
X