- From: Daniel W. Connolly <connolly@hal.com>
- Date: Wed, 20 Jul 1994 18:43:37 -0500
- To: www-lib@www0.cern.ch
Currently, a 0 return from malloc() is considered a fatal error throughout wwwlib. The error is reported to stderr, and the process exits. For some applications, this is unsuitable. A user may be editing some data, and exiting in this manner will cause the data to be lost. As long as we're writing read-only tools like browsers, this isn't such a big deal. But as we start building annotation and full editing applications, it will become more and more of an issue. The optimal behaviour of the library would be for all API entry points that might call malloc() to include a failure return mode. Unfortunately, this would require significant re-engineering of the library -- not to mention the impact on future work. There is a precedent in the Xt library. It also considers 0 from malloc() a fatal error, but it provides a "hook" function so that applications can do a non-local return (longjump) up through the Xt code and back to the application. The Xt spec doesn't guarantee that this will work, and I'm sure some memory leaks would result from this behaviour. Consider: XtFoo() { char *xxx = XtMalloc(100); XtBar(); XtFree(xxx); } XtBar() { char *yyy = XtMalloc(200); XtFree(yyy); } If the xxx alloc succeeds, but the yyy malloc fails, and the XtError handler lonjumps up through XtFoo, xxx will never be freed: memory leak. The DCE programming API includes a longjump-based exception implementation, so code can be written like this: DCEFoo() { char *xxx = malloc(100); if(!xxx) RAISE(OutOfMemory); TRY{ DCEBar(); }FINALLY{ free(xxx); } } DCEBar() { char *yyy = malloc(200); if(!yyy) RAISE(OutOfMemory); free(yyy); } That way, if the xxx alloc succeeds, even if DCEBar raises and exception, xxx will be freed as the stack is unwound. In summary, here are some proposals for what the library code should do when malloc() returns 0: A. Fatal exit. + easy to code - unsuitable for editing apps - unsuitable for DOS/Windows, other small-memory machines - makes the library unusable from otherwise robust applications B. Call fatal-exit hook function + easy to code + allows editing apps to do "last ditch save" + allows graceful exit on small-memory machines - still unsuitable for integration with otherwise robust apps C. Raise an exception -- export exception API to library clients - requires significant library reengineering - requires integration of external technology (who's exception package do we use?) - requres library clients to use exceptions API + allows robust editing applications to use the library D. Raise an exception, catch it before exiting the API, return an error code - requires significant library reengineering - requires integration of external technology (who's exception package do we use?) - requires distinction between "public" API and "internal" API - requires library clients to do lots of error checking + suitable for use in robust applications E. Support "malloc failed" return code throughout the library - requires significant library reengineering - error prone - requires library clients to do lots of error checking + suitable for use in robust applications Enclosed is an interface I've used successfully to build applications. Coding to this interfaces allows proposal A or B. It allows all sorts of heap tracing (for the purify-less), or none, and you can turn the tracing on and off by recompiling just the .o file that implements mem_*(), without recompiling the rest of the library. It's used in stead of malloc(), free(), and strdup() from <stdlib.h>. /* memalloc.h -- coding idioms for heap allocation * $Id: memalloc.h,v 1.3 1994/05/11 23:23:21 connolly Exp $ */ #ifndef __memalloc_h #define __memalloc_h #include "stdclang.h" #include <stddef.h> /* for size_t */ /* does NOT return 0 */ #define CNEW(type, annotation) ((type*)mem_alloc(sizeof(type), annotation, __FILE__, __LINE__)) /* does NOT return 0 */ #define CNEWSIZE(type, qty, annotation) ((type*)mem_alloc(sizeof(type)*(qty), annotation, __FILE__, __LINE__)) /* does NOT return 0 */ #define CRESIZE(type, qty, old, annotation) ((type*)mem_realloc(sizeof(type)*(qty), old, annotation, __FILE__, __LINE__)) /* does NOT return 0 */ #define STRDUP(str) mem_strdup(str, "STRDUP", __FILE__, __LINE__) /* null str allowed */ /* does NOT return 0 on allocation failure*/ #define ZSTRDUP(str) mem_zstrdup(str, "ZSTRDUP", __FILE__, __LINE__) #define DEALLOC(old) mem_dealloc(old, __FILE__, __LINE__) /* NOTE: this evaluates its arg more than once */ #define DISPOSE(old) do{ if(old){ DEALLOC(old); old = NULL; } }while(0) extern void* mem_alloc PARAMS((size_t bytes, CONST char *annotation, CONST char* file, int line)); extern void* mem_realloc PARAMS((size_t bytes, void* old, CONST char *annotation, CONST char* file, int line)); extern void mem_dealloc PARAMS((void* old, CONST char* file, int line)); extern char* mem_strdup PARAMS((CONST char*, CONST char *annotation, CONST char* file, int line)); extern char* mem_zstrdup PARAMS((CONST char*, CONST char *annotation, CONST char* file, int line)); #endif /* __memalloc_h */
Received on Thursday, 21 July 1994 01:43:09 UTC