5

Here's the official sample of using PipeTo() in Akka.NET:

Receive<BeginProcessFeed>(feed =>
{
    //instance variable for closure
    var senderClosure = Sender; 
    SendMessage(string.Format("Downloading {0} for RSS/ATOM processing...", feed.FeedUri));

    //reply back to the sender
    _feedFactory.CreateFeedAsync(feed.FeedUri).PipeTo(senderClosure);
});

The question is why should we use Sender closure here? Why not to use just:

_feedFactory.CreateFeedAsync(feed.FeedUri).PipeTo(Sender);

In this sample and in the docs it's said it's mandatory to use closure here. But I don't see any reasons to do so.

If we used ContinueWith() it's reasonable to use closure inside the continuation, but not as PipeTo() parameter.

Do I miss something?

alexey
  • 8,360
  • 14
  • 70
  • 102
  • The Official example of PipeTo at the Petabridge site is Wrong about closure. This is not an example of closure at all it is an example of Parameter Passing. Read up on Closure. The variables in question have to be free variables. Not Parameters. You could just have easily sent Sender directly to PipeTo because it is called immediately, and runs in parallel with CreateFeedAsync. Don't believe me. Try it for yourself. – jdawiz Aug 07 '19 at 11:50

1 Answers1

6

Two things to understand here:

  1. Sender is not a static property. Sender is actually a function on the IActorContext that returns the sender of the current message being processed. The value returned changes every time the context gets a new message from the mailbox.

  2. PipeTo is a continuation, and when that continuation executes on the threadpool, calling Sender will access the exact same IActorContext object that started the task. There is no guarantee that the current Sender in the context is the same, because of new message(s) being pushed into the actor for processing since the task started.

So we cache the value of Sender in a local variable to guarantee that we're aiming PipeTo at the correct IActorRef whenever it executes.

AndrewS
  • 1,395
  • 11
  • 23
  • 1
    The problem you describe appears when compiler captures "this" in a context of a closure. For example, it happens when we use "this" members (like Sender) in lambda expressions. But here we just pass the parameter to PipeTo() method and there are no closures created. – alexey Jul 11 '15 at 10:22
  • 3
    Sender is a context-sensitive method - it's value mutates every time a message is received by an actor. If the current value of Sender is not cached in a local variable, which gets closed over when you use PipeTo, there's a strong chance that your Sender call can return a value different than the actor you've expected. We've been able to replicate this error numerous times :p – Aaronontheweb Jul 15 '15 at 00:36
  • @Aaronontheweb: "If the current value of Sender is not cached in a local variable, which gets closed over when you use PipeTo". This does not make sense to me: what PipeTo does is closing not on the `Sender` property but on a local variable (the argument `recipient`) that is a copy of the reference to the current `Sender`. Therefore I don't see this as any different from explicitly copying the `Sender` property to a local variable. – Luca Cremonesi Jan 18 '18 at 17:39
  • @AndrewS. Dig a little deeper. Sender is static because it is a member of a property that is static. Sender is a property which returns protected IActorRef Sender => Context.Sender;. Look At Context. It is protected static IActorContext Context. Keep digging and you will eventually get to [ThreadStatic] private static ActorCell _current; – jdawiz Aug 06 '19 at 20:51