5 ms·
Without exceptions most of your STL containers become unsable, unless aborting is an option when otherwise bad_alloc would get thrown. Also without exceptions
by ensiferum 9y ago
Without exceptions most of your STL containers become unsable, unless aborting is an option when otherwise bad_alloc would get thrown.
Also without exceptions you really need double initialize pattern for your class types. I.e. a ctor that cannot (and will not throw) and then a "error_code Init(...)" method that can return an error code.
- sillysaurus3 9y agoSure, just abort. It's a fine choice in a bad_alloc situation.
- ensiferum 9y agoWhy? Would you also abort on other resource allocation errors, such as opening a socket, or creating a process wide mutex or whatever? I mean sometimes for some apps aborting on such errors (on some embedded platforms where they must work within specific resource constraints) can be an OK strategy but in general people seem to have this special idea about "bad alloc" conditions. The usual misconceptions are that a) it cannot happen and b) nothing can be done. Both are false.
- bluGill 9y agoIf the system is out of memory there isn't much you can do. The general answer is you don't run out of memory in the first place. Linux will kernel panic in similar situations and you are done there too. (in most cases linux unwinds. fails the operation, and continues; but there cases where the only thing left to do is panic and halt)
- Jach 9y agoI heard Lisp machines would just stop everything and give you a REPL so you have a chance to clean things up, or just reboot. I don't see why modern systems can't reserve some amount of memory for such a recovery process. In my Linux experience I'm either saved by the OOM Killer (which is dubious 'safety' at best) or the system just hardlocks and I have to either REISUB or sometimes force a power off.
- lispm 9y agoOn a Symbolics Lisp Machine one lands in a low-level command loop, adds more virtual memory (swapping space on some disk) and then can continue from there.
- jerf 9y ago"Would you also abort on other resource allocation errors, such as opening a socket, or creating a process wide mutex or whatever?" You seem to believe this is not a common choice? I've seen plenty of programs crash on being unable to open a socket/file. I'm not saying this is a good thing, just that your rhetorical implication that it is unusual seems misplaced. And it's not always even that bad. It is true that something can be done, yes, but that something often requires massive fundamental rearchitecting of the core paradigm of the system or something. In most modern languages it isn't that easy to try to deal with resources problems. Erlang could probably do it, and... uhh... I'm already running out of possibilities here.
- malkia 9y agoIf you can't allocate a socket, can your app work (game in offline mode)? If you can't allocate memory, can your app work (... example ...)? I guess something like trying to load a huge file (say spreadsheet) in excel-type of app.
- smelterdemon 9y agoCan you give an example of how one could recover from a bad alloc (especially in a "bare metal" situation where you probably can't move anything to disk)?
- maccard 9y agoIt's contrived, but; imagine a mark and sweep garbage collector. Instead of freeing memory, you mark it as free-able. When you get a bad alloc you an then sweep and actually free the resources, and retry the allocation. A more realistic example might be if you have an application that allocates a large chunk of memory up front for processing later. If it fails, you can stall until a point where you know the memory will be available, or you could retry with a smaller allocation.
- ensiferum 9y ago@malkia, jerf, smeterdemon bluGill Ok first thing first. Like I said for some specicial resource constrained system abort() can be a viable solution, or if you just don't want to deal with any resource allocation failures. In general though if you have a std::vector and a push_back throws bad_alloc, that bad_alloc really only pertains to that particular vector using that particular allocator. If you assume that a bad_alloc coming from anywhere indicates some kind of system/process wide condition you're assuming too much. UNLESS you've designed the system, i.e. in this case the allocators and such and know this assumption to be true, otherwise no. Additionally consider a case where your code is for example reading some data off a disk in a loop and the Nth iteration and push_back throws, the stack is unwound, the vector is destroyed and the memory it consumed before is released. In a "normal" application the bad_alloc can propagate up the stack until it's handled by whatever strategy that the application uses for general resource failures. The two takeaways here are that bad_alloc doesn't really indicate any system wide condition (unless you know that it does), so don't assume such. Secondly bad_alloc is just like any other resource allocation failure. If your application aborts() when it cannot open a socket, that's fine. But if it doesn't, then why would it abort on memory allocation failure? And I really want to stress here that bad_alloc doesn't mean same as OOM for example from the kernel's perspective. It really only pertains to a particular allocator, UNLESS you KNOW otherwise.
- ryandrake 9y agoYea, I've always wondered, for shops that try to make that "C++ but without exceptions" policy work, how do you handle things like vector::push_back() or map::operator[] failing? Just throw up your hands and crash?
- maxxxxx 9y agoSeems to me if vector::push_back fails you are already deep into memory problems. I may put a global catch around most code to avoid crashes but I think once push_back fails, a lot of other stuff won't work either.
- ensiferum 9y agoC++ can work but yeah, you really can't use the STL stuff since the API doesn't leave you other choice but to abort() if an exception was to be thrown. If you want some other strategy then you have no choice but to implement your own containers with APIs that can support such operation.
- astrobe_ 9y agoI didn't read the book but if you're "bare metal" you want to avoid dynamic allocations in the place. Secondly, if you really run out of memory then you've screwed up something really badly and yes, in these situations your best option is to let the watchdog eat your poor carcass.
- Gibbon1 9y agoWhat you really want to do in these cases is log debugging info to a noinit section of memory or nonvolatile memory and then reset the processor. Then the firmware can check for and report any logged errors on startup. If you do this religiously you might make it to fifty with most of your hair.
- monocasa 9y agoAlso, for those programmers who have some input on the EE's designs, a stuff option for MRAM is amazing. It's essentially equivalent to battery backed SRAM, but one chip. We stick logs and hardware exception stack traces/register dumps in there on dev units.
- jononor 9y agoDynamic allocation is banned for many bare metal applications. Dynamic allocations make it harder to reason about worst-case memory usage, especially with fragmentation. I find the improved initializers in C++11 a very nice addition for such usecases. One can use a struct for the application state (with struct/std::array members etc), and initialize it statically in one statement. Nicely declarative, space/code efficient, and with good type-safety.
- com2kid 9y ago> Dynamic allocation is banned for many bare metal applications. Dynamic allocations make it harder to reason about worst-case memory usage, especially with fragmentation. This right here. There is no STL in true embedded C++ apps. It is possible to make compile time allocated templated data structures, they work just fine, but they aren't the STL by any stretch. IMHO this type of C++ is much better to use. Having to plan ahead for worst case memory usage for all classes and statically allocate memory at compile time simplifies a LOT of code. Pointers don't disappear on you (although contents can obviously change!), and removing all the code handling alloc and de-alloc removes a huge chunk of code. Honestly having gone from "C with classes C++" to "full on C++11", I'm missing the C with classes. Fewer ways to abstract means finding the code that is actually running becomes a lot easier. Also having code size be an issue means people don't get to write their own set of abstraction layers that end up having only 1 or 2 concrete implementations. :/
- dragontamer 9y ago> Without exceptions most of your STL containers become unsable, unless aborting is an option when otherwise bad_alloc would get thrown. On bare-metal, you probably want to be using std::array<> most often (Thanks C++11), which I don't believe have any errors or allocations. Dynamic Memory is useful in application code, but under bare metal... its often less useful than carefully managed static allocations. Or perhaps... carefully managed static allocations (std::array) are easier to "prove" that edge-cases won't happen. --------- Something like std::vector or std::unordered_map which reallocate themselves and moves all around memory seems like a huge, unnecessary complication in memory-restricted situations that warrant bare-metal coding. I guess with a proper std::Allocator class, std::vector and std::unordered_map would be useful. But... that gets complicated. And not very many C++ programmers are aware of how to use "Placement New" or other allocator features. Its probably best to just stick to the simple std::array and avoid dynamic memory.