Last active
August 29, 2015 14:10
-
-
Save mbj/3f6db32b5ba7349401ac to your computer and use it in GitHub Desktop.
My minimal 135loc (stripped) actor implementation powering mutant
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'concord' | |
module Actor | |
class ProtocolError < RuntimeError | |
end # ProtocolError | |
Undefined = Class.new do | |
INSPECT = 'Actor::Undefined'.freeze | |
def inspect | |
INSPECT | |
end | |
end.new | |
class Message | |
include Concord::Public.new(:type, :payload) | |
def self.new(_type, _payload = Undefined) | |
super | |
end | |
end # Message | |
class Binding | |
include Concord.new(:actor, :other) | |
def call(type) | |
other.call(Message.new(type, actor.sender)) | |
message = actor.receiver.call | |
fail ProtocolError, "Expected #{type} but got #{message.type}" unless type.equal?(message.type) | |
message.payload | |
end | |
end # Binding | |
class Actor | |
include Concord.new(:thread, :mailbox) | |
def initialize(*) | |
super | |
@sender = mailbox.sender(thread) | |
end | |
attr_reader :sender | |
def receiver | |
mailbox.receiver | |
end | |
def bind(other) | |
Binding.new(self, other) | |
end | |
end # Actor | |
class Env | |
include Concord.new(:thread_root) | |
def spawn | |
mailbox = Mailbox.new | |
thread = thread_root.new do | |
yield mailbox.actor(thread_root.current) | |
end | |
mailbox.sender(thread) | |
end | |
def current | |
Mailbox.new.actor(thread_root.current) | |
end | |
end # Env | |
class Mailbox | |
def initialize | |
@mutex = Mutex.new | |
@messages = [] | |
@receiver = Receiver.new(@mutex, @messages) | |
freeze | |
end | |
attr_reader :receiver | |
def actor(thread) | |
Actor.new(thread, self) | |
end | |
def sender(thread) | |
Sender.new(thread, @mutex, @messages) | |
end | |
end # Mailbox | |
class Receiver | |
include Concord.new(:mutex, :mailbox) | |
def call | |
2.times do | |
message = try_blocking_receive | |
return message unless message.equal?(Undefined) | |
end | |
fail ProtocolError | |
end | |
private | |
def try_blocking_receive | |
mutex.lock | |
if mailbox.empty? | |
mutex.unlock | |
Thread.stop | |
Undefined | |
else | |
mailbox.shift.tap do | |
mutex.unlock | |
end | |
end | |
end | |
end # Receiver | |
class Sender | |
include Concord.new(:thread, :mutex, :mailbox) | |
def call(message) | |
mutex.synchronize do | |
mailbox << message | |
thread.run | |
end | |
self | |
end | |
end # Sender | |
end # Actor |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment