What happens if Actor a calls a method on Actor b and Actor c?
Task<int> CallBAndC()
{
IActorB b = ActorProxy.Create<IActorB>(ActorId.CreateRandom(), new Uri("fabric://MyApp/MyService"));
IActorC c = ActorProxy.Create<IActorC>(ActorId.CreateRandom(), new Uri("fabric://MyApp/MyService"));
Task<int> futureBResult = b.Method();
Task<int> futureCResult = c.Method();
// do some lengthy computation
return (await futureBResult) + (await futureCResult);
}
Could 'b.Method()' and 'c.Method()' run simultaneously? Or are they run sequentially (perhaps due to being in the call context of Actor a)?
What happens when 'b.Method()' and 'c.Method()' call a method of a?
It should be okay with this approach, there just a few caveats to keep in mind:
You should always await the tasks returned by an actor call, if you fire and forget you might lose the call, because there is no guarantee that the message got delivered.
When an actor send a call to another actor, it will wait in a queue util the actor is free to process the call. When the call come from same context, if Reentrancy is enabled, and the actor is waiting for an operation of same context to complete, the call will be allowed to continue.
Example: When an actor A call actor B, Then B tries to call actor A while A wait for the initial call to B to complete, The reentrancy will allow B to continue, then the result from A to B is returned, then B will return the result to A. This behaviour can be changed, when reentrant mode is disabled it will dead-lock when B tries to call A. after a timeout the operation will fail.
Related
In Objective-c we can use NS_SWIFT_UI_ACTOR to indicate that a method or a whole class should be executed on the main actor (as we would do with #MainActor in Swift). This works correctly since, when invoking a method which returns a value from outside the main actor (using a Task.detached with background priority), the compiler forces us to call it using "await" (to allow for "actor switch") and, at runtime, I can see that the code is executed on the main thread.
However, when calling a callback based method on the same class (which gets an automatically "imported" async variant, as explained here) the NS_SWIFT_UI_ACTOR seems not working properly, and the code is executed on a background thread.
NS_SWIFT_UI_ACTOR
#interface ObjcClass : NSObject
- (NSString *)method1;
- (void)method2WithCompletion: (void (^)(NSString * _Nonnull))completion;
#end
Task.detached(priority: .background) {
let objcInstance = ObjcClass()
print(await objcInstance.method1()) <----- this is called on the main thread
print(await objcInstance.method2()) <----- this is called on a bg thread
}
While I understand what your expectation is I don't think as of now the way actor is implemented there is any way to resolve this. Actors in swift are reentrant, which means whenever you invoke any async method inside actor's method/actor task, actor suspends the current task until the method returns and starts processing other tasks. This reentrancy behavior is important as it doesn't just lock the actor when executing a long-running task.
Let's take an example of a MainActor isolated async method:
func test() async {
callSync() // Runs on MainActor
await callAsync() // Suspends to process other task, waits for return to resume executing on MainActor
callSync() // Runs on MainActor
}
But since swift can't infer from an objective c async method which part should run on MainActor and for which part actor can just suspend the current task to process other tasks, the entire async method isn't isolated to the actor.
However, there are proposals currently to allow customizing an actor's reentrancy so you might be able to execute the entire objc async method on the actor.
I am designing an actor that should schedule sending a message to itself.
I notice that there are at least two ways to do it.
I would like to understand the difference to choose the right one.
The first is one method of akka.actor.Timers:
def startSingleTimer(key: Any, msg: Any, timeout: FiniteDuration): Unit
The second is the pretty common way with scheduler of actor context system:
final def scheduleOnce(
delay: FiniteDuration,
receiver: ActorRef,
message: Any)(implicit executor: ExecutionContext,
sender: ActorRef = Actor.noSender): Cancellable
Question:
Which is the main difference between them in case of scheduling a
message to itself?
Is it a good idea to pass actor context to scheduleOnce method?
akka.actor.Timers.startSingleTimer
Can be used only within an actor
Allows only scheduling a message to self, ie not possible to schedule a message to some other actors
If actor dies, the active timers are cancelled automatically
Keeps track of active timers by user provided key
context.system.scheduler.scheduleOnce
Can be used to schedule messages outside and inside actors
Requires explicit ActorRef for receiver and optional sender
There is no automatic clean-up process. All should be handled explicitly by calling returned akka.actor.Cancellable
So, if you just need to schedule messages to itself, pick akka.actor.Timers
Is it a good idea to pass actor context to scheduleOnce method?
Not sure in what way you want to do it, but in general actor context must be used only within receive method and not passed outside of an actor neither used in callback methods of Futures.
I have an actor that may receive messages from an external system (UDP/TCP). Based on the content of the incoming data, there are cases when I want the actor to call back to a non-aka part of my code.
In other words, I don't want to call and actor with ask and wait for some incoming data but rather be called back asynchronously.
How can I implement this without e.g. closing over the calling object (the trivial would be pass in a callback on creation of ActorRef but this would capture caller)?
Assuming you have a functional interface handleMessage - that is, a method handleMessage that only accepts immutable data - you can simply wrap it in a Future, run inside the actor's context:
import scala.concurrent.Future
// Inside your actor, this is the implicit execution context holding the thread
// pool the actor executes within. You MUST import this in order for it to be in
// the implicit scope for scala.concurrent.Future.
import context.dispatcher
Future {
handleMessage(messageData)
// If you need to know when this completes, send a message to the initiating
// actor here.
}
I have an actor p: ParentActor which I want to do some cleanup jobs when it is stopped by making use of the postStop method. One of the jobs involves sending a message to a child actor c: ChildActor. After that, c and then p should be stopped. However, once context.stop(p) has been called c seems to be immediately stopped and is unable to receive messages.
Below is an example of what I'd like to do:
class ParentActor extends Actor {
...
override def postStop {
logger.info("Shutdown message received. Sending message to child...")
val future = c ? "doThing"
logger.info("Waiting for child to complete task...")
Await.result(future, Duration.Inf)
context.stop(c)
logger.info("Child stopped. Stopping self. Bye!")
context.stop(self)
}
}
Results in error message:
[ERROR] [SetSystem-akka.actor.default-dispatcher-5] [akka://SetSystem/user/ParentActor] Recipient[Actor[akka://SetSystem/user/ParentActor/ChildActor#70868360]] had already been terminated. Sender[Actor[akka://SetSystem/user/ParentActor#662624985]] sent the message of type "java.lang.String".
An alternative would be to send p a message saying to shutdown, and have the above actions take place as a result of that, but using the built in stopping functionality seems better.
PS. This is a new application so design alternatives are welcome.
When an actor A is stopped, its children are indeed stopped before A's postStop hook is called. The sequence of events when an actor is stopped is as follows (from the official documentation):
Termination of an actor proceeds in two steps: first the actor suspends its mailbox processing and sends a stop command to all its children, then it keeps processing the internal termination notifications from its children until the last one is gone, finally terminating itself (invoking postStop, dumping mailbox, publishing Terminated on the DeathWatch, telling its supervisor).
Overriding the parent's postStop won't help you because your desired shutdown procedure includes sending a message to a child and waiting for a reply. When the parent is terminated, the child is stopped before the parent's postStop is run.
As you mentioned, sending a specific message to the ParentActor to initiate the shutdown is another approach. Something like the following would work:
import akka.pattern.pipe
class ParentActor extends Actor {
...
def receive = {
case Shutdown =>
(c ? DoYourThing).mapTo[ThingIsDone].pipeTo(self)
case ThingIsDone =>
logger.info("Child has finished its task.")
context.stop(self)
case ...
}
}
Note that one should avoid using Await in an actor. Instead, pipe the result of a Future to an actor.
I have an actor that has a mutable state. In my receive method, I pattern match the messages and call some service that would return a Future. This Future would modify the state in my Actor instance. Is this state not thread safe? Since the Future would be executing in a different thread, is the state in my Actor this guaranteed to be thread safe?
No that will not be threadsafe as you correctly assumed. Futures run on any thread that the execution context provides.
The way to solve this is to pipe it back to the same actor. All actor's inputs must always be messages. You can find the documentation here.
Some example code:
import akka.pattern.pipe
//... inside the Actor somewhere:
val futureResult: Future[YourType] = something.thatReturnsAFuture()
futureResult.pipeTo(self)
And then amend your receive block so the result will be processed after the future completes and it is sent back to this actor:
case result: YourType => //...