> - Cancellation (and the corollary, the ability to externally control, suspend, or time out arbitrary sets of computations at yield points).
i'm not convinced by this one; i'd need to see more details, but my intuition is that it's just an efficiency hack, and i'll explain why
in a shared-nothing system like erlang (or pre-threading unix) you really can externally control, suspend, or time out arbitrary sets of computations. if you have to wait for a yield point, you only sort of can; a task stuck in an infinite loop that therefore never yields will not get a chance to be canceled. moreover, if a task is holding a lock when it yields, that could be either because its own correctness depends on some shared mutable data structure staying consistent, or because it currently has such a structure in a temporarily inconsistent state that it needs to restore to consistency
in the second case, how do you cancel it? you need to somehow restore the consistency, and nobody outside the task has the information required, and if it just releases the lock and exits instead of continuing, the next task that depends on the inconsistent structure will probably crash. so you can't cancel it at the next yield; you have to wait until the task yields without any locks held. and similar remarks apply for suspension, except that the potential problem is not crashing but a deadly embrace, a particularly pernicious kind of deadly embrace because it's hard to tell who was responsible for unsuspending the task
but let's suppose that being able to sometimes cancel, suspend, or time out a computation is a useful feature. we must ask, then, when is it useful? if a task is computing a result that is no longer needed (perhaps the user has already been shown a 'timed out' error, for example) what is the harm of allowing it to continue as long as it wants? it depends on the task, of course, but it had better not be crucial to correctness, for two reasons. one is that, as explained above, the cancelation might have to be delayed for an arbitrarily long time so that the task can suspend while not holding any locks. the other is slightly more subtle: if the task's correctness depends on it getting canceled early enough, then its correctness is dependent on the other task that kills it getting scheduled early enough, which is generally not something that can be guaranteed
consequently, i believe the only case where task cancelation can be useful in a shared-memory system like this is when it's just an efficiency hack and doesn't actually affect the system's semantics
but maybe there's something i don't understand about this (my ignorance of async rust is truly vast) so please let me know. git repository urls containing purported counterexamples would be especially welcome
> - Ergonomic (rather than just performant) heterogenous multiplex-waits. Doing that in non-future-oriented systems isn't just potentially slow, it's fiddly and a breeding ground for bugs in many languages, including Rust.
it's possible i'm not understanding what you mean; can you explain with code? it sounds like the kind of thing you could put into a library function in a system using multithreading; you can solve it without reconsidering your entire programming paradigm
> - The ability to outsource the geometry of concurrency and parallelism to an async runtime rather than spending brain and editor cycles deciding things like e.g. how many handler vs. acceptor threads you're going to manage, and whether executors will launch/stop lazily or eagerly.
this one is purely an efficiency hack; in a threaded system you can spawn a new handler thread for each request which terminates when the request is done. maintaining a thread pool is itself purely an efficiency hack
> I think you mean "in parallel" here; the whole meta-system is concurrent, but data races re-emerge in Tokio (or rather, impose sizedness/pinnedness restrictions on data in async contexts in Tokio) where they aren't in JS because Tokio can run in true threaded parallel.
i don't think so. what looks like 'concurrency' at one level of abstraction can look like 'parallelism' at another, but if by 'parallelism' we mean running multiple streams of instructions at physically the same time, no, you can totally get data races in a multithreaded system that is running on an in-order single-processor system and therefore has no parallelism at all, and this was common until 20 years ago. even if the processor is bit-serial, it makes no difference