Skip to content

Initial implementation of ActorContext and the #spawn function #48

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 13 commits into from
Apr 7, 2014

Conversation

jdantonio
Copy link
Member

This PR implements the simplest case of the new actor framework as discussed in issue #36. It deprecates the previous Actor base class and replaces it with the ActorContext mixin module. The only implemented factory method is spawn which creates a single instance of the actor class, wraps it in an ActorRef object, and restarts the actor thread should it crash.

Creating an actor is done simply by including ActorContext and implementing a #receive method that takes with the signature def receive(*msg). It receives a message of zero or more arguments and returns the result of message processing.

Actors which mix in the ActorContext module have a new lifecycle: on_start, on_reset, and on_shutdown. The first and the last are each called once. The on_reset method is called every time the actor is reset. Actor resetting is a larger topic which will eventually be written about in the wiki. The lifecycle that triggers on_start is controlled by several factory options including :abort_on_exception, :reset_on_error, and :rescue_exception.

This implementation reduces the number of message passing methods from four down to two: post and post!. The forward method has been deprecated and will not likely return. The original post method, which returned a boolean value, has been deprecated. It was based on a Scala method which returned void. Ruby does not have void functions so there was no value in having both post and post? (the latter of which returned a future).

The post method now always returns a future object (specifically, an IVar). Additionally the post method can be given a block which will act as a callback on message completion. The block parameters are the same as for observation. The difference is that observers are notified every time a message is processed whereas the post callback only applies to the given message.

The post! method works the way it did in the previous Actor implementation. It blocks for the given number of seconds and bubbles raised errors.

An example:

require 'concurrent'

class EchoActor
  include Concurrent::ActorContext

  def receive(*msg)
    print "Received message #{msg}\n"
    msg
  end
end

ref = EchoActor.spawn #=> #<Concurrent::SimpleActorRef:0x6f198967...

# this method does not block and returns a future
future = ref.post(:foo, :bar) #=> #<Concurrent::IVar:0x4de07d3e...
#> Received message [:foo, :bar]

# this method provides a callback
ref.post(:hello, :world) do |time, msg, value, reason|
  print "Callback called at #{time} with #{value} as the value.\n"
end
#> Received message [:hello, :world]
#> Callback called at 2014-04-06 21:05:30 -0400 with  as the value.

# this method blocks for 1 second
ref.post!(1, :pow, :zap, :boom) #=> [:pow, :zap, :boom]
#> Received message [:pow, :zap, :boom]

@jdantonio
Copy link
Member Author

@lucasallan What do you think of this as the first step in a new actor framework? It provides the same capabilities as the current Actor class, plus some additional features, and it sets the stage for the advanced actor roadmap we've been discussing. I think the next steps could be spawning unregistered actor pools and unregistered remote actors. Perhaps that could be the target for the nest release (0.6.0, release date TBD). That would allow us to completely deprecate the original actor class. A global actor registry would still be part of the 1.0 release. Thoughts?

@schmurfy
Copy link

schmurfy commented Apr 7, 2014

I like it :)

You mentioned auto restart, what is your plan for supervisors, do you plan to have an erlang style supervisor hierarchy ?

@jdantonio
Copy link
Member Author

@schmurfy We already have a functionally complete implementation of Erlang's Supervisor that supports supervision trees and a Runnable mixin so that anyone can create a supervisor-compatible class. (The code could use some refactoring but I'm holding off on that until we work through the new ActorContext framework.) The original Actor implementation mixed-in Runnable and supported supervision.

I plan on keeping supervision a part of the new ActorContext, but I want to change from an explicit supervision model to an implicit model. With the original Actor class you must create a Supervisor separately then add the actor object to the supervisor. Akka uses an implicit supervision model where all actors are automatically supervised. That's what I would like to emulate.

The simple case that I've built in this PR doesn't use Supervisor because it's only a single thread. Rather than run a separate Supervisor to monitor the thread I check the state of the thread every time a message is post. It's a little brute-force but much simpler. I plan to encapsulate a Supervisor inside of the ActorRef when I implement actor pools.

@schmurfy
Copy link

schmurfy commented Apr 7, 2014

I was thinking about something similar to what Erlang does: process A spawn processes B and C, if B and C dies then A dies too and its parent respawn A which will respawn B and C.
You already mentioned it in the other thread but I just wanted to be sure it was still around ^^

The "let it ail" philosophy is one of the things I really like about Erlang.

jdantonio added a commit that referenced this pull request Apr 7, 2014
Initial implementation of ActorContext and the #spawn function
@jdantonio jdantonio merged commit 0592010 into master Apr 7, 2014
jdantonio pushed a commit that referenced this pull request May 30, 2014
Update test suite to Minitest 5
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants