9 ms·
The point is extremely valid. It has never been an issue in my use case of Rust, but the lack of an interface for when the system is out of memory is problemat
by siscia 5y ago
The point is extremely valid.
It has never been an issue in my use case of Rust, but the lack of an interface for when the system is out of memory is problematic.
Not only in kernel, but in all system-level software.
Then, it happens, less and less frequently, but still, I want to know and I want to be able to handle it.
- skohan 5y agoHaving this feature is one of the USP's of Zig isn't it?
- deleted 5y ago[deleted]
- volta83 5y agoThe problems are that: - most such interfaces do have a constant overhead that all programmers using the language have to pay. - some major operating systems (like Linux, ehem) make these interfaces useless for all their user space apps by enabling overcommit by default, which makes it hard/impossible to write portable code that can handle OOM Explicit OOM handling would mean that most users end up paying a relatively high upfront cost for something that in practice for them (e.g. if they are Linux programmers) delivers no value. For example, on Linux with overcommit (the default), even if the system is out of memory, malloc won't return null. It returns a pointer that's not null, such that your if(ptr == nullptr) will act as if everything is "ok", but then, when you try to read/write that memory, that will trigger a hardware exception, that the kernel will catch, and then the kernel will tell the OOM-killer to "make space" by killing "some process", and maybe your app is just killed, or some other app that your app is working with is killed leading to a race condition, or... or.... When your app is killed by the OOM killer, the signal that your app gets is "irrecoverable", which means that your app will die, you can at best try to do some cleanup before it does, but it will die nevertheless. So I find it extremely ironic for Linus to argue that practical programming languages are hard to use for environments that must handle OOM errors, when they are championing one of the major platforms that makes handling OOM useless by default. I keep saying this, but the obvious fix is for the Linux kernel to use overcommit internally just like they expect user space to do. When the kernel then runs out of memory, it should then start killing drivers at random to make for some space. If they think that's such a great default behavior, they should commit to it. \s
- teknopaul 5y agoYou can't solve memory allocation problems by turning off overcommit and handling alloc errors in rust. You don't get Null, you get panic. That's a problem for the kernel, even if you argue it's not a problem elsewhere. Start killing drivers is nuts, oh look my screen went blank, and three HDs stopped working. Damn where is my swap.
- volta83 5y ago> Start killing drivers is nuts, I don't disagree, just saying that its as nuts as killing random user-space programs on OOM. "The web browser misbehaved, lets kill the pulseaudio daemon, and if that doesn't fix it, lets kill WiFi, and if that doesn't fix it, lets kill vim, and... " FFS if some app tried to allocate too much memory just let the app know, or kill that app, but don't start randomly killing user-space processes.
- simiones 5y agoWhile I agree that OOMKiller is pretty mad, it's also important to note that the shared nature of memory means that the app which will die when the system is out of memory is anyway random. Even without overcommit, you can get into the situation of "the web browser is occupying 99% of RAM to open YouTube, and now pulseaudio tried to allocate another 50 bytes that the system just doesn't have, so pulseaudio should handle this, and then the WiFi manager will be the next to need 100 bytes and be refused etc". Even worse, Linux by design stalls to a complete crawl when memory almost runs out, even with 0 swap and a fast disk, as it will first start swapping out code pages before giving up. Which means that every time a process is context switched, its code ends up first needing to be read from disk, the worst possible kind of cache thrashing imaginable.
- Too 5y agoSomehow a LRU-algorithm makes the most sense, if only Linux and humans would have a common definition of what "recently used" means here. And if applications had safe-shutdown and restart on demand mechanisms. On a high level of abstraction this is how a lot of VMs deal with things, such as tabs in your browser or open apps on your smartphone.
- CJefferson 5y agoI agree Rust should have it, but it is VERY hard to do correctly. I've worked on some systems which claimed they were dealing with it, but when I purposefully pushed it (by making a malloc which would occasionally fail), I quickly uncovered dozens of bugs, and the solution was to stop pretending would could sensibly handle low-memory situations. SQLite famously does handle this situation correctly, but it is a huge amount of work.
- Fronzie 5y agoThere are counter examples. For example, data processing as part of a measurement application: If allocating the data fails, the application should abort that, but keep running to allow further control and let the user reduce e.g. the sampling size. I know of one application with a worldwide customer base that supports this.
- zlynx 5y agoYes, if allocation failure is not tested, it is not likely to work correctly. SQLite is also famous for its strict and complete test sets.
- darthrupert 5y agoCheck out Zig. That language will work much better as a kernel/driver language because it is simple by design.
- dathinab 5y ago> but the lack of an interface It's not lacking, the standard library just defaults to not use it by default because handling OOM is a conceptual mess (yes, the OOM handling features C/C++ has are included in this). But even in the standard library you have methods (some not stable) like `try_reserve` which returns an error is allocation fails. Anyway all allocation parts are part of the standard library (or the alloc library), i.e. they are not a core part of the language itself. I don't think anyone ever planed to use rust's standard library as-it-is in the kernel (it's just not designed for this, e.g. see panics) or pull in external dependencies from cargo/crates.io without vendoring them. So it's totally possible to run rust without alloc caused panics. Now besides that there is the question about panics outside of allocations. Like e.g. if code realizes it ran into a violated invariant, e.g. it knows we have a bug which isn't explicitly handled. And guess what the kernel already has a handler for it: `BUG()` so basically any panic will call `BUG()` (and we don't panic on memory allocations). Now maybe some tweaks to the panic system are necessary to make sure this all works well (panic=BUG(), is like panic=abort and on abort call BUG()!). Now wrt. the integer and float parts, there are two thinks first the panic will likely call without debug assertions like overflow checks (which would call BUG()!! So many are gone, but here are some special cases around floats and 128bit integers on platforms which don't support it where calling BUG() is inappropriate but I have to look into this, tbh. So short: - panic on mem-alloc failure is a (lib)std/alloc thing, kernel code would anyway have used something easel - panic in the kernel can be made into being basically BUG() - issues around float and 128-bit integers should be fixable, at worst with a compiler flag. But embedded code is by it affected, too. So there is a good chance there is already a fix.
- prussian 5y agoIt honestly isn't. Out of memory means, your system is simply not designed for the task at hand. The kernel returning -ENOMEM only masks the fact that eventually Linux will have to OOM Panic if it can't OOM Kill. Hell imagine the swapping and the I/O spike because your VFS cache has been or is currently being purged. I honestly think the best case is to just fail when a fundamental resource is simply not there.
- lxgr 5y ago> Out of memory means, your system is simply not designed for the task at hand. Speaking as somebody who's never built such a system, wouldn't maintaining some opportunistic cache (e.g. for some space-time-tradeoff) be a valid use case of asking for more memory and gracefully degrading in case it's not available? Error handling would simply consist of not expanding the cache size (if it happens during an allocation related to the cache) or freeing up some cache memory (if it happens for an essential allocation).
- marcosdumay 5y agoYou can free opportunistic caches, undo some calculations (and optionally put a descriptor of them into the disk), freeze some internal services into disk, not start some large task (and keep the small but time critical ones running)... There are all sorts of things you could do on out of memory errors. But on practice programs ask the users to decide those things, and just fail if the user choice is invalid. It's very rare that some program makes those decisions by itself, and it's usually frowned upon, because each single program can not assume that it owns the entire system.
- prussian 5y agoI mean, it sounds like you're describing overcommit to me. Ask whatever large amount you want. Maybe even be like webkit and use overcommit for heap isolation. It works out great for the userspace case, until the limits are actually reached and you still have a failure problem, one probably harder to deal with than without overcommit.
- cies 5y ago
- kzrdude 5y agoRust std::alloc has the same standard interface as everything else- return null on failure to allocate. It's just that the development in std collections to use this is in progress. And it's being retrofitted to collections that assume they can just abort or panic on alloc failure.
- steveklabnik 5y ago(Some of) Those collections live in std::alloc as well, which is the issue here.