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.

241eb3a089132c5a0c65e765558a6735?s=128

Arjan van der Gaag

November 11, 2015
Tweet

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