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. Actor /ˈaktə/ noun 1. a participant in an action or

    process. 2. a process with an inbox for receiving messages.
  3. Queues require 'thread' @running = true @queue = Queue.new Thread.new

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

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

    def process_message(msg) @counter = (@counter + 1) end end
  7. Queueing messages class Greeter def greet(name) puts "Hello, #{name}!" end

    end greeter = Greeter.new @queue << [:greet, "John"] greeter.send *@queue.pop
  8. 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
  9. 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' )
  10. 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
  11. 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
  12. 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
  13. Celluloid futures pinger.ping(url) # => "200 OK" future = pinger.future.ping(url)

    # => #<Celluloid::Future:0x000001009759b8> future.value # => "200 OK"
  14. 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
  15. Find out more • Celluloid • Celluloid::IO • Concurrent Programming

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