There are many situations when you can't add at each level a complex
unwinding code to take care of an allocation failure in the middle of
the construction of a complex data structure. It is much more sensible
to set an a priori strategy and not check at each call.
Ah! I see what you mean now. I have actually run across this, and
found that as long as you allocate in a consistent nested order that
there is always a way to clean up properly; though in the worst cases
it requires a bunch of gotos that *could* actually be seen as
spaghetti code.
I suppose if this nested consistency could not be guaranteed, then
these clean up fragments could be very complicated. But then I just
use the other method of getting this done which is to free everything
no matter what -- the point being that there would be some boolean
method for determining if a resource/object/whatever has not even yet
been initialized, in which case freeing is a NOOP on it. In the case
of bstrings, for example, if the bstring pointer is NULL, freeing it
becomes a NOOP.
But in both cases I am relying on conventions (that I am defining) to
make sure I can do it. I am pretty sure that my conventions don't
impact the ultimate scope of programming that I am capable of, so I
could make a claim that I think that its always possible to clean up
in a well defined way, but I can't say this with certainty. Clean-up
from failures along the way is certainly the least structured and
therefore least maintainable code I have ever written. So it would
similarly not surprise me if there were some cases where it was
actually *really hard* to write proper clean up code.
No. lcc-win implements try/catch, but of course the regulars here will
start crying "heresy heresy" so I did not mention that.
Hmmm ... right. Personally, I try to separate each of my "extensions"
from the standard. For example, my pstdint.h file, my SuperFastHash()
function, Bstrlib, my primeat library, etc, etc, all are completely
independent of each other. So people who want to use one of them
don't have to buy into my philosophy on any of the other stuff. (The
one major exception is my CSV library, since I have an agenda of
proving that Bstrlib is also equal or better than state of the art
hand crafted Clib string code even where Clib enjoys is greatest
advantage; namely parsing.)
[snip]
Ok, I see what you are doing here, but this is a desperate strategy
that I don't think fully works as well as you are hoping.
It is one way of trying to cope with this. The same strategy is
implemented for the stack under windows. You get a stack overflow
exception with one LAST page still free to be allocated for
the stack. You can then still call some functions and you have
a stack of 4096 bytes reserved for this purpose. This is the
same strategy.
Yes I see. But in the case of the depleted stack there is a platform
specific design problem with your program that cannot be resolved at
runtime; so some instant desperate strategy *must* be employed. With
the heap, things are different. There many well known algorithm
alternatives which trade of memory footprint for speed -- if you fail
with the memory allocation method, you can often just retry your
algorithm with a slower memory conservative solution. This is
fundamentally why I cannot endorse the xmalloc() design -- it removes
a legitimate programming path in which you literally write recovery
code in your software (if you could no longer use malloc() I mean).
This is just a model OBVIOUSLY. No multi-threading considerations are
in this code. It is just an outline of how this could be solved.
Right. I am just sensitive to this sort of thing, so I feel the need
to bring it up when I see it. Its clear the entire C language
committee seems to care less about these things as they continue to
endorse errno(), strtok(), asctime() and so on.
This is a good point.
It must implement a counter so that it doesn't get into an infinite
loop.
Right. In fact it needs to be a thread safe counter.
I just realized that like any event handlers, you want to have a way
to chain them. In this case you will also need:
allocEvent getAllocEvent (enum incident);
[...]
This are quite reasonable specs. I will try to think them over
and maybe implement them in the lcc-win library.
Well, obviously I have a lot more that I would like to add, like a way
of examining an allocation and get back the size of the allocation
from it (many compilers have this as an extension already), a counter
for the total number of bytes currently allocated, the ability to walk
through your allocated memory blocks (many compilers have something
*similar* to this) and in fact, a memory classifier function
(something that could look at a pointer and give you either a
definitive classification for the pointer (static, auto, program,
heap, unknown) or at least a guess.) There is also potential for an
improved realloc() but there were some issues with my original
thinking about it, and I haven't revisited it.
I think we need to recognize that debugging is a standard part of
development that should be made a standard part of the library.
Because of C's default "handle everything about memory" design, in
order to even compete, in the long term, with GC languages, the
language *needs* to expose as much information as possible to
demonstrate that its still feasible to stick with its approach. Right
now, C programmers have a situation where we not only have to deal
with all memory allocation by ourselves but we have no idea what the
global state of our memory manager is in, or even what the memory
utilization of our program is. In other words C programmers don't
actually know more about the memory state than a Java programmer does
even though C has a totally deterministic interface to memory.