If you are out of memory, then the executor is likely unable to allocate the memory needed to track the future returned from the async allocator. That kind of thing is the big challenge with failable allocation: the fail code path needs to be allocation free and (like with panic mentioned in the video) we don't have a good way to ensure a given code path is allocation free in Rust.
At least in Rust, futures don't necessarily HAVE to allocate, and in the experimental allocator we were discussing, the allocation future doesn't. The future is stored in the task (so you would have allocated the task when you spawned it), meaning that the allocation path has no allocations of its own.
> we don't have a good way to ensure a given code path is allocation free in Rust. I encountered this when I needed to ensure a code path is async-signal-safe :). Almost all wakers potentially allocate.
My personal opinion is that async alloc and/or drop should be an optional library thing rather than the default behavior of the language. Async drop for example would complicate RAII a lot since that would mean that now the lifetime of objects must be async, and since the async state machines arent strictly guaranteed to ever complete, any time you drop something you have an issue of "destructor running somewhere on an undetermined scheduler at an undetermined point in time". Which kinda defeats the point of RAII that says that objects are dropped when they exit scope. It would also mean that you have to implicitly schedule a drop, otherwise you cant drop things once they exit a scope. And for a zero-overhead language where scheduling is an explicit action its a definite no-no.
Also just to clarify, nobody is proposing changing any default behaviour. Async drop is something a type has to opt into by implementing AsyncDrop, and it actually _extends_ the ability to use RAII to cases where the cleanup needs to await.
So using a u8 pointer or slice has always confused me a bit. You can only free the exact u8 pointer that was allocated, right? Not part of it? So should it not be something like Allocation(NonNull([u8]))?
allocations need to be atomic right. So if async alloc is called twice you might have cases where one of the allocations gets invalidated. eg. freeBlock->next gets set twice. If you're using mutexes for async, might as well block the whole time which is the current interface
Solving the wrong problem most likely. Would be better if the code could be restructured such that arena allocators could be used and freed all at once, rather than worrying about the lifecycle of every object individually.
My unpopular opinion is that we shouldn't have async anything. Just use a proper thread and stop dealing with ambiguity. It would also eliminate the viral nature of async functions. This is really just another N-cost abstraction where you have to know the internals of how it works to know the actual cost.
But what if overcommit is turned on. Also what is the program really supposed to do on OOM? If I would write an OOM safe program it would be one that allocates everything upfront so it cannot fail during execution, one could even punt that job to the loader and allocate out of a static char array (with strict aliasing turned off),
@@theevilcottonball i'm kinda new to this, but i'd guess the following: if overcommit is on, you're out of luck. as for what to do on OOM, it'd depend on the program and how critical the allocation is, most programs probably can't do much with it, but, for example, it could enable implementing allocation retries. about allocating everything upfront, if your program allows for that then sure, not usually the case though.