Slide 1

Slide 1 text

Ruby acting up A look at how Celluloid implements the actor
 model for concurrency in Ruby.

Slide 2

Slide 2 text

encapsulated
 state message message

Slide 3

Slide 3 text

Actor /ˈaktə/ noun 1. a participant in an action or process.

Slide 4

Slide 4 text

Actor /ˈaktə/ noun 1. a participant in an action or process. 2. a process with an inbox for receiving messages.

Slide 5

Slide 5 text

Thread basics Thread.new do loop do # do something interesting end end

Slide 6

Slide 6 text

Concurrency & Parallelism

Slide 7

Slide 7 text

Thread basics Thread.new do loop do # do something interesting end end

Slide 8

Slide 8 text

Starting and stopping @running = true Thread.new do while @running do # do something interesting end end

Slide 9

Slide 9 text

Queues require 'thread' @running = true @queue = Queue.new Thread.new do while @running do message = @queue.pop # do something interesting with message end end

Slide 10

Slide 10 text

Actor object class Actor def initialize @running = true @queue = Queue.new @thread = Thread.new do while @running do process_message( @queue.pop ) end end end def stop @running = false end private def process_message(msg) # do stuff here end end

Slide 11

Slide 11 text

Thread safety class Actor def initialize @counter = 0 end def process_message(msg) @counter += 1 end end

Slide 12

Slide 12 text

Thread safety class Actor def initialize @counter = 0 end def process_message(msg) @counter = (@counter + 1) end end

Slide 13

Slide 13 text

Mutex def initialize @mutex = Mutex.new end def process_message(msg) @mutex.synchronize do # ... end end

Slide 14

Slide 14 text

Queueing messages class Greeter def greet(name) puts "Hello, #{name}!" end end greeter = Greeter.new @queue << [:greet, "John"] greeter.send *@queue.pop

Slide 15

Slide 15 text

Actor delegates to object class Actor def initialize(target) @target = target @thread = Thread.new do process_inbox end end def process_inbox while @running process_message *@queue.pop end end def process_message(func, *args) @mutex.synchronize do target.public_send(
 func, *args ) end end end

Slide 16

Slide 16 text

Actor send_async class Actor def send_async(*args) @queue << args end end class Greeter def greet(name) puts "Hello, #{name}!" end end greeter = Greeter.new actor = Actor.new(greeter) actor.send_async( :greet, 'John' )

Slide 17

Slide 17 text

Celluloid actor mixin module Celluloid def self.included(base) base.extend ClassMethods end module ClassMethods def new(*args) Actor.new(super) end end class Actor def method_missing(*args) target .public_send(*args) end end end

Slide 18

Slide 18 text

Celluloid async proxy module Celluloid class Actor def initialize(target) @proxy = AsyncProxy.new(self) end def async @proxy end end class AsyncProxy def initialize(actor) @actor = actor end def method_missing(*args) @actor.send_async(*args) end end end

Slide 19

Slide 19 text

Celluloid example class Pinger URL = "http://www.google.com/webmasters/tools/ping?sitemap=%s" include Celluloid def ping(sitemap_url) Net::HTTP.get(URL % sitemap_url) end end pinger = Pinger.new url = 'http://arjanvandergaag.nl/sitemap.xml' pinger.ping(url) # => wait... 200 OK" pinger.async.ping(url) # => nil

Slide 20

Slide 20 text

Celluloid futures pinger.ping(url) # => "200 OK" future = pinger.future.ping(url) # => # future.value # => "200 OK"

Slide 21

Slide 21 text

Ruby promises with Whenner pinger.ping.then do |on| on.done do |value| puts "Done with status #{value}" end on.fail do |value| puts "Something bad happened!" end end Whenner.when(pinger.ping, ponger.pong).done do |results| results.each do |status| puts status end end

Slide 22

Slide 22 text

Actors are objects handling messages in a separate thread.

Slide 23

Slide 23 text

Find out more • Celluloid • Celluloid::IO • Concurrent Programming with Celluloid (Tony Arcieri, MountainWest RubyConf 2012) • Sidekiq • RubyTapas • Whenner