Hacker News new | past | comments | ask | show | jobs | submit login

> Message passing definitely doesn't eliminate race conditions or deadlocks.

You mention two very different problems.

1. Data race. It's a problem of mutation of some shared state. This problem is illuminated in the actor's approach by removing the shared state completely. Every actor has its own state and the only actor can change during handling incoming messages.

Even if we don't speak about actors and use message-passing within some other model (like CSP or Pub/Sub) then it hard to imagine how data race can happen in 1-to-1 interaction between entities in your application.

2. Deadlocks. There is no such thing as deadlock if actors use async message-passing. But there can be another problem: livelocks (https://en.wikibooks.org/wiki/Operating_System_Design/Concur...)

Or such thing as miss of a message. For example, actor A starts in state S1(A) and waits for message M1 to switch to state S2(A) where it will wait for M2. But actor B sends messages to A in reverse order: M2, then M1. If message M2 is not deferred by A, then M2 will be ignored by A in S1(A), then A switches to S2(A) and will wait for M2, but that message won't go to it.




exactly right exposition on both data-races and deadlocks in general for message-passing systems.

> Or such thing as miss of a message. For example, actor A starts in state S1(A) and waits for message M1 to switch to state S2(A) where it will wait for M2. But actor B sends messages to A in reverse order: M2, then M1. If message M2 is not deferred by A, then M2 will be ignored by A in S1(A), then A switches to S2(A) and will wait for M2, but that message won't go to it.

once message delivery is guaranteed to be in-order and lossless, then for the above scenario the issue is 'obviously' on the sender side. it can be easily solved with timers where 'A' expects to move from state-a to state-b in say 'n' seconds after startup etc.


> once message delivery is guaranteed to be in-order and lossless, then for the above scenario the issue is 'obviously' on the sender side.

It depends on how the receiver handles incoming messages:

* there could be a scheme where a message is lost if it isn't handled in the current actor's state (or if it isn't deferred explicitly);

* there could be a scheme where a message that is not handled in the current state is deferred automatically (for example, Erlang's selective receive).

The problem I described exists for the first case but isn't actual for the second. However, schemes with selective receive can have their own drawbacks.

> it can be easily solved with timers where 'A' expects to move from state-a to state-b in say 'n' seconds after startup etc.

The main problem is: a developer should understand that problem and should implement that in code. But people make mistakes...


> * there could be a scheme where a message is lost if it isn't handled in the current actor's state (or if it isn't deferred explicitly);

that's weird :) how can a message be 'lost' from a mailbox without any explicit 'read-message-from-mailbox' call from the actor itself. now, an actor can choose to ignore messages in some states, but then again a suitable protocol needs to exist between the interacting parties on how to proceed.

> * there could be a scheme where a message that is not handled in the current state is deferred automatically (for example, Erlang's selective receive).

sure, but that was conscious decision on part of the actor. message was not 'lost' per-se

> The main problem is: a developer should understand that problem and should implement that in code. But people make mistakes...

oh most definitely yes. which is why having callflow diagrams is so ever useful. moreso when dealing with actor like environments...


> how can a message be 'lost' from a mailbox without any explicit 'read-message-from-mailbox' call from the actor itself.

It depends on the implementation of actors. If an actor is represented as a thread/fiber then an actor is responsible to call `receive` method from time to time. The only example of such an approach I know in the C++ world is Just::Thread Pro library. But even in that case, a message can be ignored if a user writes the wrong if-then chain (or `switch` statement).

But actors often implemented as an object with callbacks those are called by actor framework at the appropriate time. List of callbacks can differ from state to state.


ah, i see where you are coming from.

my notion of the whole thing was one where an actor is actually a pid (canonical or simulated), running a infinite loop with deque-process-wait on the msgq.


I understand, but it isn't enough. You have to have a selective receive primitive to write something like:

    for(;;) { // The main state.
      receive(
        when([](M1 msg1){ // Dive into another state.
          while(some_condition) {
            receive(when([](M2 msg2){...},
              when([](M7 msg7){...},
              ...);
          }
        }),
        when([](M3 msg3){...}),
        when([](M5 msg5){...}),
        ...);
    }
In that case, if you receive M2 before M1 the instance of M2 will be kept in the queue.

But if you have to write something like that:

    for(;;) { // The main state.
      auto * m = receive();
      if(m->type == M1::msg_typeid) { // Dive into another state.
        while(some_condition) {
          auto * m2 = receive();
          if(m2->type == M2::msg_typeid) {...}
          else if(m2->type == M7::msg_typeid){...}
          else if...
        }
      }
      else if(m->type == M3::msg_typeid){...}
      else if(m->type == M5::msg_typeid){...}
      else if...
    }
then you can easily lose a message by a mistake.


if you are tangling msgq-receive with its processing then sure.

however, imho, if you decouple the whole thing i.e. msgq-receive, and its processing via callbacks, then things are harder to get wrong. it might lead to either more elaborate state machine on the receiver side though, but then again imho that is not bad either.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: