"The scheduler will only switch to the next actor only after processing a message in it's entirety. It will not interrupt an actor that is currently processing a message."
Have they changed it? Last I knew, processes get a "reduction count", which is basically "the number of opcodes in the VM this process gets to run before it gets suspended". Technically, this is exactly like what Go does, except instead of sticking pre-emption points at function calls, it places them at every opcode, which is to say, quite often multiple times per line of code. Technically, you can still freeze the interpreter (or a thread of it) by calling into some C code that counts as one "reduction" but never returns. In practice, this causes about as much trouble for Erlang programmers as it does Go programmers, namely, "it's something you should know but can literally go your entire career without hitting". (Depends on what kind of code you're writing. Someone trying to use Erlang or Go for heavy numerical processing has a good chance of hitting it, but if you're writing network code, you'd have to reeeaallly work at it in either environment.)
Erlang's pre-emption points are specifically inside the implementations of the CALL and RET instructions, not on every opcode. It's just that every stack frame effectively takes O(1) runtime before hitting one or the other of those instructions, because of the lack of primitive loop instructions. (Mind you, there are O(N) instructions—there's cryptographic-hashing BIFs, for example—but they're internally reduction-budgeted, so the scheduler can yield from inside them.)
> Technically, you can still freeze the interpreter (or a thread of it) by calling into some C code that counts as one "reduction" but never returns.
Actually, you can't! (Or, well, at least as long as you've given a moment of thought while coding your NIF you can't.)
Avoiding this used to require explicit calls to the reduction budgeting functions in your native code. But as of recent ERTS releases, you just have to mark your NIF as "dirty" and it'll get offloaded to a separate scheduler-thread-pool that exists just to run dirty (i.e. long CPU-time) NIFs. There's no reason, today, to not just mark your NIF as dirty when you start writing it, and then take the dirty flag off only when you're ready to add the calls to the reduction-budgeting logic.
(If you flip it around in your head, having a dirty flag is essentially the same as having a "clean" flag—or, in other words, an "I know what I'm doing, I've guaranteed this has reduction-budgeting" flag. Which is no more and no less than something like Rust's unsafe{} blocks. To say you can freeze a runtime with unsafe{} code shouldn't be surprising ;) What's more surprising is that you can run native code within ERTS that's not unsafe{} in this sense! ...though, as every Erlang maintainer will tell you, "it's a last resort and you would probably have been fine with a port program, which provides much better isolation.")
Have they changed it? Last I knew, processes get a "reduction count", which is basically "the number of opcodes in the VM this process gets to run before it gets suspended". Technically, this is exactly like what Go does, except instead of sticking pre-emption points at function calls, it places them at every opcode, which is to say, quite often multiple times per line of code. Technically, you can still freeze the interpreter (or a thread of it) by calling into some C code that counts as one "reduction" but never returns. In practice, this causes about as much trouble for Erlang programmers as it does Go programmers, namely, "it's something you should know but can literally go your entire career without hitting". (Depends on what kind of code you're writing. Someone trying to use Erlang or Go for heavy numerical processing has a good chance of hitting it, but if you're writing network code, you'd have to reeeaallly work at it in either environment.)