7 ms·
> Decoupling concurrently running software components via message passing avoids race conditions by design. How am I supposed to read this? It's obviously not
by clarry 7y ago
> Decoupling concurrently running software components via message passing avoids race conditions by design.
How am I supposed to read this? It's obviously not "message passing eliminates race conditions", so perhaps it's intended to say something like "you need to design the components to avoid race conditions."
- dnautics 7y agoI don't think it's true. Message passing definitely doesn't eliminate race conditions or deadlocks. Although, it's much easier to design code that has fewer race conditions. It's possible that they mean "it avoids data races by design".
- jfkebwjsbx 7y agoExactly! Data races are just a subset of issues!
- senderista 7y agoMessage passing without selective receive does eliminate deadlocks. See: Pony.
- dnautics 7y agoSingle threaded service a sends a call request to service b, as a part of handling the request, service b requests back to service a. Deadlock. You may not have control over service b.
- signa11 7y agohuh :) if you have async-messages without any blocking semantics you are just fine.
- dnautics 7y agoMy point is just that it's possible to construct one in any system that static analysis will be unable to detect or prevent. If you can construct it, it can also happen by accident.
- signa11 7y ago> My point is just that it's possible to construct one in any system that static analysis will be unable to detect or prevent. If you can construct it, it can also happen by accident. how ? can you please explain ? thanks !
- dnautics 7y agomost people implement "block on API response", because that's way easier to reason about, sometimes, even without timeout. Reading through the thread, what I was misunderstanding is that this situation technically gets rolled into what called a "livelock". Fine, but from the higher-level API consumer's POV it's a deadlock.
- signa11 7y agowith blocking semantics, you are sure to run into deadlocks in a single-threaded environment pretty easily a -> b -> c -> a. grpc f.e. has support for blocking requests but it's 'solution' is to spawn a new thread for the request.
- senderista 7y agoYou did not construct such a scenario for an async message-passing system.
- eao197 7y ago> 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/Concurrency/Livelock 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.
- signa11 7y agoexactly 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.
- eao197 7y ago> 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...
- yellowapple 7y agoErlang makes similar promises around its implementation of the actor model; however, that's also combined with immutability, a lack of shared state (processes are isolated from one another), and preemptive multitasking, and I don't know how much of that CAF is able to reproduce. Then again, avoid != eliminate. You can avoid hitting a deer on the road, but that doesn't mean you're able to eliminate the possibility of doing so.