Actor Object Protocol

I’ve been meaning to blog this for a while…

Tony Arcieri and I devised an object protocol that any object acting like an actor in Ruby ought to implement in order to interoperate with our actor implementations. It is implemented, more or less, in both Revactor and the Rubinius actor implementation, and I plan on refitting Omnibus actors to satisfy it as well.

(Interoperate, here, means simply to be capable of receiving messages from any code which observes this protocol, and also having the ability to participate in linking and supervision tree relationships with other objects implementing the protocol. Protocol is meant in the Smalltalk sense of the term, which is essentially a “duck type”.)

Anyway, here is some pseudo-Ruby sketching out the protocol in all its glory. It consists of four methods:

protocol Actor
  # Submits the given message to this actor's mailbox.
  #
  # Messages submitted to a dead actor are silently ignored.
  #
  def <<(message) ; end

  # Notifies this actor that other_actor has linked with
  # it.  It represents "half" of the linking process,
  # merely instructing this actor to add other_actor to
  # its set of linked actors if it has not already been
  # added.  In order to link two actors (we'll call them
  # a and b), an actor implementation should perform:
  #
  #  a.notify_link(b)
  #  b.notify_link(a)
  #
  # If an actor receiving a link notification is dead,
  # when its implementation permits it should respond
  # to other_actor's link attempt by calling:
  #
  #   other_actor.notify_exited(self, reason)
  #
  # (See the description of notify_exited for
  # discussion of exit reasons.)
  #
  # Aside from dead actors responding with exit
  # notifications every time, notify_link should be
  # idempotent:  repeated attempts to link the same
  # actor should have no additional effect.  An actor is
  # either linked or it is not; there is no notion of
  # "link count".
  #
  def notify_link(other_actor) ; end

  # Notifies this actor that other_actor has unlinked
  # from it.  It represents "half" of the unlinking
  # process, and merely instructs this actor to remove
  # other_actor from its set of linked actors if the
  # set of linked actors currently contains it.
  #
  # Similarly to linking, an actor implementation
  # should perform unlinking as follows:
  #
  #  a.notify_unlink(b)
  #  b.notify_unlink(a)
  #
  # Unlike link notifications, an unlink notification
  # sent to a dead actor has no extra effect.
  # 
  def notify_unlink(other_actor) ; end

  # Notifies this actor that other_actor has died, either
  # by exiting normally or as the result of an error.
  # If the dead actor exited normally, reason should be
  # nil, otherwise it should be an object which
  # stringifies to a description of the error (often an
  # Exception object, but even a simple string is
  # allowed).
  #
  # When an actor dies, it should call notify_exited on
  # all of the actors linked to it *at its time of death*
  # in order to notify them that it has exited.
  #
  # How an actor responds to the reciept of an exit
  # notification is outside the scope of this protocol,
  # although it is suggested (not required) that the
  # default behavior be for the receiving actor to
  # terminate itself, as that is most useful behavior
  # for implementing supervision trees.  However, it
  # is permissible for implementations to ignore exit
  # notifications.
  #
  # Exit notifications sent to an actor that is itself
  # already dead should always be silently ignored.
  # 
  def notify_exited(other_actor, reason) ; end
end
Note that all four of these methods are asynchronous: their effect need not be immediate, they should not block the calling thread for significant periods of time, and they should always succeed, returning self. They should be made safe to call from any thread, and calls made from the same thread should retain their order with respect to one another.

Beyond that, the protocol places no requirements on the scheduling or concurrency properties of the actor represented by an object implementing this protocol—it need not even be an actor in the usual sense.

hoodwink.d enhanced