#1426 Actor.locals problem.

paul Sun 27 Feb 2011

Actor.locals initialized inside make constructor of Actor, but inside receive method they are empty.

using concurrent

const class SimpleActor : Actor {

    const Str handle := Uuid().toStr

    new make(Str msg) : super(ActorPool()) {
        Actor.locals[handle] = msg
        echo("Make: ${Actor.locals}")
    }

    override Obj? receive(Obj? message) {
        echo("Inside: ${Actor.locals}")
        return "${Actor.locals}"
    }

}

class RunMe {

    Void main() {
        Actor a := SimpleActor("Cake")
        Str? input := null
        Bool quitFlag := false
        while (!quitFlag) {
            input = Env.cur.in.readLine
            if (input != null) {
                echo("Outside: ${a.send(null).get}")
            } else {
                quitFlag = true
            }
        }
    }

}

Output:

Make: [04e2da30-9e18-5480-9b9b-005056c00008:Cake]

Inside: [:]
Outside: [:]

Inside: [:]
Outside: [:]

Using fantom 1.0.57 (java runtime, version: 1.6.0_24).

peter Sun 27 Feb 2011

My understanding is that Actor.locals are not local to an actor, like an instance variable is. They come into existence only for an actor within a given thread. See #1158 for an earlier discussion.

The only objects I manage to store in an actor's constructor are those created and used in a GUI thread (like the example in examples/fwt/clock.fan), because that's the only thread you can be sure of.

For non-GUI objects, I use an init message to set up Actor.locals in receive, and then I am able to retrieve and use the values on sending an action message to the actor.

paul Sun 27 Feb 2011

Thanks peter. But there is one more thing I'd like to ask. How one can pass InStream to an Actor?

vkuzkokov Sun 27 Feb 2011

The perfect solution is to open InStream in the actor's thread (e.g. by passing File or Uri). That will guarantee that no two threads will ever read from it.

If you still feel need for passing mutable objects between actors then sys::Unsafe is the way to do it.

brian Mon 28 Feb 2011

Just to recap:

  • what happens in your Actor's constructor is happening inside the actor/thread who is doing the constructing
  • only what happens inside your receive method is actually inside your actor's thread
  • actor locals are essentially just like Java/C# thread local with the difference that actors move around to multiple threads as their scheduled and the actor locals travel with them

In general the best design techniques are:

  1. save state from construction to const fields in your actor (must be immutable)
  2. pass construction state using an init message (must be immutable/serializable)
  3. lazily init state on first message received

Login or Signup to reply.