|
|
Subscribe / Log in / New account

Are you sure?

Are you sure?

Posted Jun 28, 2011 20:17 UTC (Tue) by khim (subscriber, #9252)
In reply to: Zeuthen: Writing a C library, part 1 by dps
Parent article: Zeuthen: Writing a C library, part 1

IMHO a good program should handle all errors, including malloc failure, in a sane manner. It really is less painful that people that refuse to do it think.

What kind of handling are we talking about? xmalloc-style handling (which makes sense), "malloc masturbation" (where all calls to malloc are surrounded with explicit checks) or "really robust handling" (where you really handle all memory errors?)

First approach makes sense - I heartily reccomend it: early crash is great help with debugging. Third one is very hard while second one is utterly pointless and only uselessly adds thousands of lines of poorly tested code.

Why it's hard to handle out of memory errors properly? Well, modern systems include tons of ways to make you life miserable if you try to achieve this goal. Malloced "memory" comes uninitialized so even if malloc returned "success" actuall access may fail (think overcommit), and even if you'll fill all your allocated pages with something in your malloc wrapper it'll be not enough. Basically you must write your program in such a way a to handle SIGSEGVs everywhere - or all these checks you are so proud about are pointless. And it's not easy to support proper SIGSEGV handling if you program uses a lot of libraries and it not trivial itself.

Sadly I've seen so many libraries of type 2 and pitiful number of libraries of type 3 thus now my rule of thumb is: use xmalloc everywhere and compartmentalization a-la Chrome if you need some pretty out-of-memory UI.


to post comments

Are you sure?

Posted Jun 28, 2011 20:32 UTC (Tue) by nix (subscriber, #2304) [Link] (5 responses)

malloc masturbation is not utterly pointless. Once, it saved my bacon and stopped a large international bank's production trading platform running OOM in the middle of the trading day. The thing is, even if you don't recover -- even if you only exit() -- error-checking all malloc() failures lets you determine *which* malloc() failed. So when a program of mine had a malloc() failure in testing one day, on an allocation of a mere twelve bytes, the fact that I was checking it let me tell *which* malloc() it was, which let me diagnose a nasty great big ~3Mb/s cross-process leak which was likely to be triggered on various customer sites at that moment. I emailed said customers with a diagnosis recipe (ps -o vsz -C ...) and found out that said large bank were in the same failing state (with about an hour to go before they ran out of memory) and got them to kill the processes involved before they OOMed their machine. (Yes, they should have been using ulimits. They weren't. They were 'too disruptive', like the whole machine going OOM wasn't going to be a disruption.)

Yes, doing these checks makes it a bit more annoying to allocate memory, but not much. And the benefit is *definitely* worthwhile.

Are you sure?

Posted Jun 28, 2011 23:33 UTC (Tue) by brouhaha (subscriber, #1698) [Link] (4 responses)

You can do the same thing with malloc/realloc/strdup wrappers by having the wrapper log a stack backtrace, e.g., using GNU libc backtrace().

Alternatively, for platforms without backtrace() or equivalent, write your wrappers to accept additional arguments for the message to log for failure. If desired, you could use macros to make that a compile-time conditional, though I personally don't ever turn off any low-overhead debugging code.

Either approach seems far better than littering the source code with explict tests after each allocation. That just makes the code harder to read and maintain.

Are you sure?

Posted Jun 29, 2011 10:59 UTC (Wed) by nix (subscriber, #2304) [Link] (3 responses)

Your approach (the second one) would have worked, if I could have revamped the entire source base to use it. But it is rare that one gets the opportunity to do that, and malloc() tests at least don't look strange. xmalloc() with a message will make people stop and think when they see it: it's strange and new. malloc() without one and a test: everyone can read that.

Are you sure?

Posted Jun 29, 2011 20:44 UTC (Wed) by brouhaha (subscriber, #1698) [Link] (1 responses)

For a large source base, I definitely wouldn't make a major change throughout, unless it was broken throughout.

I'm not thrilled with the name xmalloc(); I'd probably steal Perl's convention and call it malloc_or_die().

None of these approaches are well-suited to the original problem of doing memory management inside a library. The best practice I know of there is to pass in pointers to allocation/deallocation functions when the library is initialized, and make sure that any failures reported by those functions are handled appropriately. Appropriate means that if the library can't do anything sensible, it at least reports the allocation failure back to the caller.

When I use such a library, I might well pass in a malloc wrapper that prints a stack backtrace and exits.

Are you sure?

Posted Jun 30, 2011 12:53 UTC (Thu) by nix (subscriber, #2304) [Link]

Yes exactly. Generally, library functions which immediately allocate storage will be returning a pointer to that storage: they can return NULL. Library functions which *call* such functions may have more complex work to do, including possibly unwinding partially-completed work. If *this* would require memory allocation, you may need to find a different algorithm, or arrange to do all allocations first (I've done both at various times).

But saying "I won't bother, I'll just abort" is a disservice to your users, no matter *how* hard it is to handle OOM.

Are you sure?

Posted Jun 30, 2011 6:19 UTC (Thu) by cpeterso (guest, #305) [Link]

> Your approach (the second one) would have worked, if I could have revamped the entire source base to use it. But it is rare that one gets the opportunity to do that, and malloc() tests at least don't look strange.

If you're brave, there's always the C preprocessor:

#define malloc(size) my_xmalloc_with_msg(size, __FILE__, __LINE__, __FUNCTION__)

This approach could still be useful if your macro was only #included by a subset of .c files, leaving some object code still calling libc's malloc().

Are you sure?

Posted Jun 29, 2011 15:40 UTC (Wed) by dgm (subscriber, #49227) [Link]

>>IMHO a good program should handle all errors, including malloc failure, in a sane manner. It really is less painful that people that refuse to do it think.
> What kind of handling are we talking about? xmalloc-style handling (which makes sense), "malloc masturbation" (where all calls to malloc are surrounded with explicit checks) or "really robust handling" (where you really handle all memory errors?)

In the context of your reply, I'm with you... most of the time. xmalloc() makes sense for most _programs_ (although not all of them). But, if we're discussing libraries (the subject of the article), then "really robust handling" is _the_ option.


Copyright © 2025, Eklektix, Inc.
Comments and public postings are copyrighted by their creators.
Linux is a registered trademark of Linus Torvalds