3 ms·
Fully agreed about "what C is". Many, many sins have been committed in the name of saving keystrokes or making things temporarily easier. That little voice in t
by kabdib 11y ago
Fully agreed about "what C is". Many, many sins have been committed in the name of saving keystrokes or making things temporarily easier. That little voice in the back of your head telling you that something might be a bad idea? Listen to it, it's probably right.
On handling out-of-memory conditions:
- Yes, you can do it
- It's also hard to get right, in the general case, for large systems. It's often a pyrrhic victory
Most systems I've worked with have simply restarted, rather than risk getting complicated recovery logic wrong (and winding up in a worse situation -- corrupting persistent data, or giving wrong answers -- than if they simply crashed). A few systems have a 'reserve tank' that they can use to do a controlled crash, saving important state and whatnot before quitting. OOM can get pretty wicked.
- asveikau 11y agoThere are large projects that handle OOM and it isn't that hard to do. Imagine every function in your call stack handles errors consistently. In most cases these functions will bubble up all errors to the caller. In many cases they will perform allocations themselves and free those allocations when they fall out of scope due to either success or error. The function at the top of the stack hits a malloc error. It will bubble up its status to its caller, who will do the same for his caller, etc. The chain of functions will free intermediate allocations they made along the way. By the time you get to some top-level or near-top-level function, you can react to the error, and you likely even have quite a bit of heap space when the rest of the stack frees its work. But if you don't happen to have the heap space, then you can structure that top-level error handler so that it performs very few allocations, does allocations at upfront at initialization time, etc. I don't think any of this is hard and I've seen it work well in practice. It's sad to me when I see the opposite, some unreasonable allocation quite reasonably fails, and it takes down the entire process because whoever wrote that code thought it was too hard to do otherwise.
- alexgartrell 11y agoI totally disagree. I've done a lot of operational and low-level systems work and handling obscure OOM scenarios is /really/, /really/ hard, and not at all worth the engineering effort. But, even assuming you handle every failed page allocation perfectly and your stack does the right thing, you can still screw yourself /trivially/ by blowing out the network stack, or because the writeback daemon can't keep up with your filesystem writes. So now we're talking about clamping kernel resources with userspace logic that does things like periodic fsyncs and userspace tcp acks before attempting to send more -- good luck.
- hp 11y agoThe reason it's hard is that everything you do has to become a "transaction" with the ability to roll back. Say you send five messages in a function, now you have to pre allocate all of them in order to cancel them all if any allocation fails, before sending any. But this pre allocation tends to break abstraction barriers (every API might need separate "prepare" and "fire" calls). It doesn't sound that complicated at first but it gets that way in a hurry. Almost every function can fail, every operation needs the ability to rollback midstream... it makes a mess in a hurry. It's also a LOT of extra code, really material bloat. One experience I had doing this: http://blog.ometer.com/2008/02/04/out-of-memory-handling-d-bus-experience/ http://blog.ometer.com/2008/02/04/out-of-memory-handling-d-b... If you haven't written the test harness to test almost every malloc failing, you might think this is easier than it really is. Adding 30-40% more code to your code base that will almost never get tested except maybe by your unit tests ... no thanks, not if it's possibly avoidable for a given application.
- asveikau 11y agoI find it odd to see a reply written as if I haven't written in a transactional style and seen it working well. But ignoring that for a second. Your blog post says the error handlers did the wrong thing 5% of the time. Can I read from that they did the right thing 95% of the time? And you will dismiss the technique for that? Not to mention that there are coding styles that make the transactional approach less difficult. (OK, so reverting your work gets hairy in the presence of certain side effects. In many cases I would rather chose some behavior and stick with it than take down an entire process dereferencing a null pointer.)
- hp 11y agoall I'm saying is that "not hard" as you put it and "30% more code, transactions, and a complicated test harness" don't go together for me. If they do for you then enjoy :-)