Upgrade to Pro — share decks privately, control downloads, hide ads and more …

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

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

Arjan van der Gaag

November 11, 2015
Tweet

More Decks by Arjan van der Gaag

Other Decks in Programming

Transcript

  1. Ruby acting up A look at how Celluloid implements the

    actor
 model for concurrency in Ruby.
  2. encapsulated
 state message message

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

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

    process. 2. a process with an inbox for receiving messages.
  5. Thread basics Thread.new do loop do # do something interesting

    end end
  6. Concurrency & Parallelism

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

    end end
  8. Starting and stopping @running = true Thread.new do while @running

    do # do something interesting end end
  9. Queues require 'thread' @running = true @queue = Queue.new Thread.new

    do while @running do message = @queue.pop # do something interesting with message end end
  10. 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
  11. Thread safety class Actor def initialize @counter = 0 end

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

    def process_message(msg) @counter = (@counter + 1) end end
  13. Mutex def initialize @mutex = Mutex.new end def process_message(msg) @mutex.synchronize

    do # ... end end
  14. Queueing messages class Greeter def greet(name) puts "Hello, #{name}!" end

    end greeter = Greeter.new @queue << [:greet, "John"] greeter.send *@queue.pop
  15. 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
  16. 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' )
  17. 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
  18. 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
  19. 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
  20. Celluloid futures pinger.ping(url) # => "200 OK" future = pinger.future.ping(url)

    # => #<Celluloid::Future:0x000001009759b8> future.value # => "200 OK"
  21. 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
  22. Actors are objects handling messages in a separate thread.

  23. Find out more • Celluloid • Celluloid::IO • Concurrent Programming

    with Celluloid (Tony Arcieri, MountainWest RubyConf 2012) • Sidekiq • RubyTapas • Whenner