Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > C++ > bad alloc

Reply
Thread Tools

bad alloc

 
 
Adam Skutt
Guest
Posts: n/a
 
      08-31-2011
On Aug 31, 4:30*am, Goran <goran.pu...@gmail.com> wrote:
> Why would bad_alloc be thrown while writing to disk? Guess: because
> writing is made in such a way as to modify program state. Doesn't seem
> all that logical.


It's perfectly logical and quite reasonable. In fact, the standard
library can ( and will, on many implementations) do it for you, even
if your code doesn't call new itself. Iostreams implementations are
free to allocate memory for buffers in order to perform I/O and will
do so. As a result, they can trigger std::bad_alloc if you're close
to an out of memory condition[1].

> 1. walking down the stack upon OOM (or other exception) normally frees
> resources.


Yes, but not in a way to typically enable retrying of the failing
operation (if it were even possible), which you is why you suggested
one should walk down the stack!

> 2. top-level error is not a place to do anything resource-sensitive,
> exactly due to OOM possibility.
>


Catch blocks are almost never the place to do anything 'resource-
sensitive' (whatever that even means) because exceptions typically
lack the detail necessary to safely do any sort of complicated
processing. Where the exception is handled in the call stack has
nothing to do with this.

> It's not as complicated as you make it out to be.


I fail to see how it can get any less complicated than, "Just do what
happens automatically", which is what I've said programmers should do.

> Going "nice" in case
> of OOM might not be worth it in all cases, but is not an and-all
> response to all concerns.


Huh? This is literal nonsense, even assuming you meant to write "end-
all".

Adam

[1] Whether you actually get std::bad_alloc is dependent on the
iostream implementation, however. Regardless, it's perfectly
reasonable to expect iostream code to allocate memory, and for it to
fail if it cannot it.
>
> Goran.


 
Reply With Quote
 
 
 
 
Adam Skutt
Guest
Posts: n/a
 
      08-31-2011
On Aug 31, 5:23*am, yatremblay@bel1lin202.(none) (Yannick Tremblay)
wrote:
> In article <c8b2f277-891c-4f40-a895-6cd78145e...@33g2000yqu.googlegroups.com>,
>
>
>
>
>
>
>
>
>
> Goran *<goran.pu...@gmail.com> wrote:
> >On Aug 31, 5:33*am, Adam Skutt <ask...@gmail.com> wrote:
> >> On Aug 30, 11:02*pm, Noah Roberts <roberts.n...@gmail.com> wrote:

>
> >> > On Aug 30, 4:04*pm, Adam Skutt <ask...@gmail.com> wrote:

>
> >> > > Properly written, exception-safe C++ code will do the right thing when
> >> > > std::bad_alloc is thrown, and most C++ code cannot sensibly handle
> >> > > std::bad_alloc. *As a result, the automatic behavior, which is to let
> >> > > the exception propagate up to main() and terminate the program there,
> >> > > is the correct behavior for the overwhelming majority of C++
> >> > > applications. *As programmers, we win anytime the automatic behavior
> >> > > is the correct behavior.

>
> >> > What if you're within a try block well before main in the call stack?

>
> >> Doesn't change anything and why would it?

>
> >> > What if the catch for that block attempts to do something like save
> >> > what the user's working on, or do some other seemingly reasonable
> >> > attempt at something that seems reasonable?

>
> >> It will almost certainly fail. *But that's OK, what that could happen
> >> anyway. *Consider entry due to another exception, std::bad_alloc being
> >> thrown somehow during the write-out process. *You're stuck with the
> >> exact same result: the attempt to save the state fails.

>
> >Why would bad_alloc be thrown while writing to disk? Guess: because
> >writing is made in such a way as to modify program state. Doesn't seem
> >all that logical.

>
> >(Repeating myself) there's two factors that play:

>
> >1. walking down the stack upon OOM (or other exception) normally frees
> >resources.
> >2. top-level error is not a place to do anything resource-sensitive,
> >exactly due to OOM possibility.

>
> >It's not as complicated as you make it out to be. Going "nice" in case
> >of OOM might not be worth it in all cases, but is not an and-all
> >response to all concerns.

>
> I absolutely agree with Goran and disagree that terminate on OOM is
> *always* the best approach. *There may be programs where it is the
> best approach but it is far from always the case.
>
> A concrete example:
>
> Network server using a standard pattern of one listener/producer and
> multiple worker/consumer threads. *The listener receives and job
> request and hands done the processing of the job to one of the worker
> thread. *
>
> It is a very much possible that processing one particular job might
> actually require too much memory for the system. *The correct thing to
> do in that case is to stop processing this one oversized job, *release
> all the resources acquired to process this job, mark it as error and
> continue processing further jobs.
>
> Since this is a persistent server than needs to be on and alive 24/7,
> it would be totally innapropriate to permanently terminate the server.
> Even if there was an additional monitoring process that restart the
> server if it dies, this would not be a good thing because you would
> loose current progress in the currently running worker threads and
> would also kill current external client connections. *
>
> Yan


 
Reply With Quote
 
 
 
 
Paul
Guest
Posts: n/a
 
      08-31-2011
On Aug 31, 9:33*am, Asger-P <j...@asger-p.dk> wrote:
> Hi Paul
>
> Please, please, please, please, please, please
> delete some of all that unneeded quotation it is so much
> waste of time all the scrolling...
>
> P.s. You are of course not the only one.
>

Ok sorry. New interfase was hiding it from me.
 
Reply With Quote
 
Goran
Guest
Posts: n/a
 
      08-31-2011
On Aug 31, 11:28*am, yatremblay@bel1lin202.(none) (Yannick Tremblay)
wrote:
> In article <f3c7068c-5920-410a-ae72-f544168bc...@y21g2000yqy.googlegroups..com>,
>
>
>
>
>
>
>
>
>
> Paul *<pchris...@yahoo.co.uk> wrote:
> >On Aug 30, 9:58*pm, Dombo <do...@disposable.invalid> wrote:
> >> Op 30-Aug-11 11:14, Krice schreef:

>
> >> > On 30 elo, 09:26, Paul<pchris...@yahoo.co.uk> *wrote:
> >> That is the real question: what do you do when you have run out of
> >> memory? The application could inform the user that the operation failed
> >> because there was not enough memory and keep on running. If a program
> >> has cached a lot of information would flush its caches to free some
> >> memory and retry *the operation. But chances are that it will never get
> >> to that point. My experience is that in a typical PC environment the
> >> system tends to become non-responding or worse long before memory
> >> allocation fails. Before that happens the user has probably restarted
> >> his PC.

>
> >This also depends how the memory is fragmented. Say for example a
> >system has 1GB RAM with 500MG free, 500MB used. *Even though you have
> >500MB free an attempt to allocate 40MB may fail because there is no
> >contiguous block of 40MB free memory.

>
> Google "Virtual Memory". *The two of you are talking totally unrelated
> things. *And given that all modern OS use virtual memory, the describe
> situation will never happen as such. *But once the app starts
> swapping, things get very slow.


Attention, memory fragmentation might turn into a real issue and
virtual memory doesn't help. The above is perhaps a poor explanation
though.

Say you have a 32-bit system. Your pointers are 32-bits long. This
limits your __address space__ to 4GB. That is, all pointers you can
ever have can point only to a location inside these 4GB. It does not
matter that you have 64GB of have virtual memory, you can't
__address__ it, mathematically. Now, consider:

1. you allocate 1.9 GB (addresses from 0 to 1.9GB)
2. you allocate 0.2 GB (addresses from 1.9 to 2.1 GB)
3. you allocate 1.9 GB.
4. you deallocate 1 and 3
5. you have 3.8 GB free, and yet, you can't allocate a contiguous
block of more than 1.9.

I see number of people confused with this. Possibly, we should be
explicit and always say "memory ADDRESS SPACE fragmentation".

Goran.
 
Reply With Quote
 
Paul
Guest
Posts: n/a
 
      08-31-2011
On Aug 31, 8:37*am, Goran <goran.pu...@gmail.com> wrote:
> On Aug 30, 4:00*pm, Paul <pchris...@yahoo.co.uk> wrote:
>
>
>
>
>
> > On Aug 30, 2:30*pm, Leigh Johnston <le...@i42.co.uk> wrote:

>
> > > On 30/08/2011 13:54, Paul wrote:

>
> > > > On Aug 30, 11:26 am, Goran<goran.pu...@gmail.com> *wrote:
> > > >> On Aug 30, 8:26 am, Paul<pchris...@yahoo.co.uk> *wrote:

>
> > > >>> There are numerous C++ examples and code snippets which use STL
> > > >>> allocators in containers such as STL vector an string.
> > > >>> It has come to my attention that nobody ever seems to care about
> > > >>> checking the allocation has been successfull. As we have this
> > > >>> exception handling why is it not used very often in practise?.
> > > >>> Surely it should be common practise to "try" all allocations, or do
> > > >>> people just not care and allow the program to crash if it runs out of
> > > >>> memory?

>
> > > >> Good-style exception-aware C++ code does __NOT__ check for allocation
> > > >> failures. Instead, it's written in such a manner that said (and other)
> > > >> failures don't break it __LOGICALLY__. This is done through a careful
> > > >> design and observation of exception safety guarantees (http://
> > > >> en.wikipedia.org/wiki/Exception_handling#Exception_safety) of the
> > > >> code. Some simple example:

>
> > > >> FILE* f = fopen(...);
> > > >> if (!f) throw whatever();
> > > >> vector<int> *v;
> > > >> v.push_back(2);
> > > >> fclose(f);

>
> > > >> This snippet should have no-leak (basic) exception safety guarantee,
> > > >> but it doesn't (possible resource leak: FILE*, if there's an exception
> > > >> between vector creation and fclose. For example, push_back will throw
> > > >> if there's no memory to allocate internal vector storage.

>
> > > >> To satisfy no-leak guarantee, the above should be:

>
> > > >> FILE* f = fopen(...);
> > > >> if (!f) throw whatever();
> > > >> try
> > > >> {
> > > >> * vector<int> *v;
> > > >> * v.push_back(2);
> > > >> * fclose(f);}

>
> > > >> catch(...)
> > > >> {
> > > >> * fclose(f);
> > > >> * throw;

>
> > > >> }

>
> > > >> The above is pretty horrible, hence one would reach for RAII and use
> > > >> fstream in lieu of FILE* and no try/catch would be needed for the code
> > > >> to function correctly.

>
> > > >> Another example:

>
> > > >> container1 c1;
> > > >> container2 c2;
> > > >> c1.add(stuff);
> > > >> c2.add(stuff);

>
> > > >> Suppose that "stuff" needs to be in both c1 and c, otherwise something
> > > >> is wrong. If so, the above needs strong excetion safety. Correction
> > > >> would be:

>
> > > >> c1.add(stuff);
> > > >> try
> > > >> {
> > > >> * c2.add(stuff);}

>
> > > >> catch(...)
> > > >> {
> > > >> * *c1.remove(stuff);
> > > >> * *throw;

>
> > > >> }

>
> > > >> Again, writing this is annoying, and for this sort of things there's
> > > >> an application of RAII in a trick called "scope guard". Using scope
> > > >> guard, this should turn out as:

>
> > > >> c1.add(stuff);
> > > >> ScopeGuard guardc1 = MakeObjGuard(c1,&container1::remove,
> > > >> ByRef(stuff));
> > > >> c2.add(stuff);
> > > >> guardc1.Dismiss();

>
> > > >> Similar examples can be made for other exception safety levels butIMO
> > > >> the above two happen in vaast majority of cases.

>
> > > > I am not familiar with the scopeguard objects I will need to look them
> > > > up.

>
> > > You are not familiar with scope guard objects as you are not familiar
> > > with RAII hence your n00bish question. *At least you have admitted a gap
> > > in your knowledge for once.

>
> > > >>> I think if people were more conscious of this error checking the
> > > >>> reserve function would be used more often.

>
> > > What is so special about reserve? *See below.

>
> > > >> I am very convinced that this is a wrong way to go reasoning about
> > > >> error checking with exception-aware code. First off, using reserve
> > > >> lulls into a false sense of security. So space is reserved for the
> > > >> vector, and elements will be copied in it. What if copy ctor/
> > > >> assignment throws in the process? Code that is sensitive to exceptions
> > > >> will still be at risk. Second, it pollutes the code with gratuitous
> > > >> snippets no one cares about. There's a better way, see above.

>
> > > >> What's so wrong with this reasoning? The idea that one needs to make
> > > >> sure that each possible failure mode is looked after. This is
> > > >> possible, but is __extremely__ tedious. Instead, one should think in
> > > >> this manner: here are pieces of code that might throw (that shouldbe
> > > >> a vaaaaaast majority of total code). If they throw, what will go wrong
> > > >> with the code (internal state, resources etc)? (e.g. FILE* will leak,
> > > >> c1 and c2 won't be in sync...) For those things, appropriate cleanup
> > > >> action should be taken (in vaaast majority of cases, said cleanup
> > > >> action is going to be "apply RAII"). Said cleanup action must be ano-
> > > >> throw operation (hence use of destructors w/RAII). There should also
> > > >> be a clear idea where no-throw areas are, and they should be a tiny
> > > >> amount of the code (in C++, these are typically primitive type
> > > >> assignments, C-function calls and use of no-throwing swap).

>
> > > > Granted there are other possible exceptions that could be thrown and
> > > > should also be considered. I was using the reserve as an example to
> > > > guard against program crashing from OOM. I would have thought at least
> > > > inform the user then close the program in a civilised manner or
> > > > postone the current operation and inform the user of the memory
> > > > situation and handle this without closing the program (i.e: by freeing
> > > > other recources)

>
> > > Why does reserve help? *If it is a std::vector of std::string for
> > > example then reserve will only allocate space for the std::string
> > > object; it will not allocate space for what each std::string element may
> > > subsequently allocate. *Also not all containers have reserve.

>
> > Well reserve would cause the vector to allocate space. Thus you only
> > need to put the reserve operation in the try-catch block and if this
> > doesn't throw you know the allocation was successfull and the program
> > can continue.

>
> Not necessarily. For example:
>
> // Adds stuff to v, return false if no memory
> bool foo(vector<string>& v)
> {
> try { v.reserve(whatever); }
> catch(const bad_alloc&) { return false; }
> // Yay, we can continue!
> v.push_back("whatever");
> return true;
>
> }
>
> The above is a hallmark example of wrong C++. Suppose that reserve
> worked, but OOM happened when constructing/copying a string object:
> foo does not return false, but throws. IOW, foo lies about what it
> does.
>
> The problem? Programmer set out to guess all failure modes and failed.
> My contention is: programmer will, by and large, fail to guess all
> possible failure modes. Therefore, programmer is best off not doing
> that, but thinking about handling code/data state in face of
> unexpected failures (that is, apply exception safety stuff).
>
> BTW, given that programmer will fail to guess all failure modes,
> programmer could wrap each function into a try/catch. That, however:
>
> 1. is a masive PITA
> 2. will fail to propagate good information of what went wrong, and
> that is almost just as bad as not reporting an error at all.
>

Yes but programmers have to take some responsibility for error
checking, that is their job after all.

The reason I initially mentioned reserve was to give it purpose.
 
Reply With Quote
 
Adam Skutt
Guest
Posts: n/a
 
      08-31-2011
On Aug 31, 5:23*am, yatremblay@bel1lin202.(none) (Yannick Tremblay)
wrote:
> In article <c8b2f277-891c-4f40-a895-6cd78145e...@33g2000yqu.googlegroups.com>,
> Goran *<goran.pu...@gmail.com> wrote:
> >On Aug 31, 5:33*am, Adam Skutt <ask...@gmail.com> wrote:
> >> On Aug 30, 11:02*pm, Noah Roberts <roberts.n...@gmail.com> wrote:

>
> >> > On Aug 30, 4:04*pm, Adam Skutt <ask...@gmail.com> wrote:

>
> >> > > Properly written, exception-safe C++ code will do the right thing when
> >> > > std::bad_alloc is thrown, and most C++ code cannot sensibly handle
> >> > > std::bad_alloc. *As a result, the automatic behavior, which is to let
> >> > > the exception propagate up to main() and terminate the program there,
> >> > > is the correct behavior for the overwhelming majority of C++
> >> > > applications. *As programmers, we win anytime the automatic behavior
> >> > > is the correct behavior.

>
> >> > What if you're within a try block well before main in the call stack?

>
> >> Doesn't change anything and why would it?

>
> >> > What if the catch for that block attempts to do something like save
> >> > what the user's working on, or do some other seemingly reasonable
> >> > attempt at something that seems reasonable?

>
> >> It will almost certainly fail. *But that's OK, what that could happen
> >> anyway. *Consider entry due to another exception, std::bad_alloc being
> >> thrown somehow during the write-out process. *You're stuck with the
> >> exact same result: the attempt to save the state fails.

>
> >Why would bad_alloc be thrown while writing to disk? Guess: because
> >writing is made in such a way as to modify program state. Doesn't seem
> >all that logical.

>
> >(Repeating myself) there's two factors that play:

>
> >1. walking down the stack upon OOM (or other exception) normally frees
> >resources.
> >2. top-level error is not a place to do anything resource-sensitive,
> >exactly due to OOM possibility.

>
> >It's not as complicated as you make it out to be. Going "nice" in case
> >of OOM might not be worth it in all cases, but is not an and-all
> >response to all concerns.

>
> I absolutely agree with Goran and disagree that terminate on OOM is
> *always* the best approach. *There may be programs where it is the
> best approach but it is far from always the case.
>
> A concrete example:
>
> Network server using a standard pattern of one listener/producer and
> multiple worker/consumer threads. *The listener receives and job
> request and hands done the processing of the job to one of the worker
> thread. *
>
> It is a very much possible that processing one particular job might
> actually require too much memory for the system. *The correct thing to
> do in that case is to stop processing this one oversized job, *release
> all the resources acquired to process this job, mark it as error and
> continue processing further jobs.


If it's possible to stop processing a job safely and recover, sure.
Marking the job as an error might require allocating memory, so you
have to avoid that. This is more difficult that it sounds, and
requires code that is explicitly written to ensure that it is
possible. Screw that code up, and you'll almost certainly end up in a
deadlock situation or with a zombie worker thread, at which point you
will be restarting the process anyway!

Anyway, while you're in the process of attempting to cancel the
oversized job, many other jobs will fail (possibly all of them) since
they can no longer allocate memory either. In the meantime, all your I/
O connections will be frozen since there's no more memory for copying
data from your clients. Best case: a transient slow down. Worst
case: internal state becomes corrupted, forcing you to close some
(again, maybe all) of the connections anyway, or the clients
disconnect for you because you took to long to respond. All of your I/
O code must be able to properly handle an OOM condition as well. This
too, requires careful design and code you must write yourself.

So no, it's not a given that attempting to fix the problem is any
better. Heap fragmentation may mean that you're prolonging the
inevitable and that you'll just endlessly spin canceling jobs and
trying to free memory. In a multithreaded application, by the time
you've actually freed the memory, the damage may already have been
done.

> Since this is a persistent server than needs to be on and alive 24/7,
> it would be totally innapropriate to permanently terminate the server.


No, it wouldn't. If you need high reliability, then you need
redundancy. If you have redundancy, then lost of an individual unit
is normally not a big deal. Cleaning staff have the disturbing habit
of knocking out power cables to servers, after all.

> Even if there was an additional monitoring process that restart the
> server if it dies, this would not be a good thing because you would
> loose current progress in the currently running worker threads and
> would also kill current external client connections. *


This can happen anyway, so your solution must simply be prepared to
deal with this eventuality. Making your code handle memory allocation
failure gracefully does not save you from the cleaning staff. If your
system can handle the cleaning staff, then it can handle memory
allocation failure terminating a process, too.

I don't know why people think it's interesting to talk about super
reliable software but neglect super reliable hardware too. It's
impossible to make hardware that never fails (pesky physics) so why
would I ever bother writing software that never fails? Software that
never crashes is useless if the cleaning staff kicks out the power
cable every night.

Adam
 
Reply With Quote
 
Adam Skutt
Guest
Posts: n/a
 
      08-31-2011
On Aug 31, 4:22*am, Goran <goran.pu...@gmail.com> wrote:
> On Aug 31, 1:04*am, Adam Skutt <ask...@gmail.com> wrote:
> > On Aug 30, 6:26*am, Goran <goran.pu...@gmail.com> wrote:

>
> > > There's a school of thought that says that allocation failure should
> > > simply terminate everything. This is based on the notion that, once
> > > there's no memory, world has ended for the code anyhow. This notion is
> > > false in a significant amount of cases (and is not in the spirit of C
> > > or C++; if it were, malloc or new would terminate()). Why is notion
> > > wrong? Because normally, code goes like this: work, work, allocate
> > > some resources, work (with those), free those, allocate more, work,
> > > allocate, work, free, free etc. That is, for many-a-code-path, there's
> > > a "hill climb" where resources are allocated while working "up", and
> > > they are deallocated, all or at least a significant part, while going
> > > "down" (e.g. perhaps calculation result is kept allocated. So once
> > > code hits a brick wall going up, there's a big chance there will be
> > > plenty of breathing room once it comes down (due to all those
> > > freeing). IOW, there's no __immediate need__ to die. Go down, clean up
> > > behind you and you'll be fine. It's kinda better to go back and say "I
> > > couldn't do X due to OOM" is kinda better than dying at the spot,
> > > wouldn't you say?

>
> > For most code, on most platforms, the two will be one and the same.
> > The OS cleans up most resources when a process dies, and most process
> > have no choice but to die.

>
> I disagree (obviously). Here's the way I see it: it all depends on the
> number of functions program executes. A simple programs who only does
> one thing (e.g. a "main" function with no "until something external
> says stop" in the call chain) in C++ benefits slightly from "die on
> OOM" approach (in C, or something else without exceptions, benefit is
> greater because there, error checking is very labor-demanding). In
> fact, it benefits from "die on any problem" approach.
>
> Programs that do more than one function are at a net loss with "die on
> OOM" approach, and the loss is bigger the more the functions there are
> (and the more important they are. Imagine an image processing program.
> So you apply a transformation, and that OOMs. You die, your user loses
> his latest changes that worked. But if you go back the stack, clean
> all those resources transformation needed and say "sorry, OOM", he
> could have saved (heck, you could have done it for the user, given
> that we hit OOM). And... Dig this: trying to do the same at the spot
> you hit OOM is a __mighty__ bad idea. Why? Because memory, and other
> resources, are likely already scarce, and an attempt to do anything
> might fail do to that.


Trying to do it at any point is a mighty bad idea. Rolling back the
stack and deallocating memory _does not ensure_ future allocations
will succeed. Once you reach OOM, you're not assured the user will be
able to save; you're not assured you can do anything at all. Trying
may very well be pointless.

It's considerably smarter, and absurdly easier, to save off the file
before attempting the operation in the first place. This way, your
code can die in a clean fashion, and the user has a recovery file with
the changes up to the failed operation. Trying to create the recovery
file under or after an OOM condition is very difficult. However,
trying to create the recovery file before the OOM condition is
trivial.

In all of the counter-examples everyone's provided so far, the correct
way to ensure reliability is not to attempt to avoid crashing on OOM.
The correct way is to write your code so that crashing on OOM becomes
irrelevant. This has the added bonus that your code will still be
reliable even if your process is never told of the OOM condition. As
I mentioned before, in some languages you're not promised to be told
about allocation failure, and other have mentioned that on some
operating systems your process may simply die instead of being told
about the failure.

> Or imagine an HTTP server. One request OOMs, you die. You terminate
> and restart, and you cut off all other concurrent request processing
> not nice, nor necessary. And so on.


That may happen anyway, as I explained to Yan. The situation is much
harder to handle when there is concurrent processing occurring, not
easier.

>
> > Straightforward C++ on most implementations will deallocate memory as
> > it goes, so when the application runs out of memory, there won't be
> > anything to free up: retrying the operation will cause the code to
> > fail in the same place. *Making more memory available requires
> > rewriting the code to avoid unnecessarily holding on to resources that
> > it no longer needs.

>
> That is true, but only if peak memory memory use is actually used to
> hold program state (heap fragmentation plays it's part, too). My
> contention is that this the case much less often that you make it out
> to be.


No, I don't believe most applications unnecessarily cache data and
would benefit from attempting to free those caches under OOM
conditions. Even if I did, the code has to be written to make that
possible, which is very difficult.

> > Even when there's memory to free up, writing an exception handler that
> > actually safely runs under an out-of-memory condition is impressively
> > difficult.

>
> I disagree with that, too.


Then I shudder to think about what you actually find difficult. Have
you ever tried to do this? Your commentary strongly suggests not only
have you not, but you don't have any experience in building reliable
systems anyway.

> First off, when you actually hit the top-
> level exception handler, chances are, you will have freed some memory.


Which doesn't mean you can make use of it. When you get
std::bad_alloc, you must assume there's 0 bytes available. No new, or
malloc, etc., anywhere.

> Second, OOM-handling facilities are already made not to allocate
> anything. E.g. bad_alloc will not try to do it in any implementation
> I've seen.


It's not the behavior of std::bad_alloc that's problematic.

> I've also seen OOM exception objects pre-allocated
> statically in non-C++ environments, too (what else?).


Figuring out what you need to handle an OOM condition is not a trivial
task. Quick, I want to write out a file during OOM (using iostreams)
and have it not fail due to lack of memory. Tell me everything I must
do to ensure this.

> There is difficulty, I agree with that, but it's actually trivial: keep in mind
> that, once you hit that OOM handler (most likely, some top-level
> exception handler not necessarily tied to OOM), you have all you might
> need prepared upfront.


Which is exceptionally hard. Let's say I'm going to attempt your file
writing idea. I have to have the stream itself already open and
ready. I have to make sure the internal streambuf has enough memory
to buffer my I/O (or is unbuffered). I have to make sure my data
structures are constructed in such a fashion so that no memory
allocation occurs when I access the data, and that the data is
accessible when the handler runs. This means no copying of anything,
which is not impossible to ensure but difficult.

You gloss over the difficultly, especially when you don't know what
code will allocate memory, nor when it will do it. Memory allocation
can and does occur in places you don't expect, so ensuring memory
allocation doesn't happen means being intimately familiar with the
implementation of every type involved in your OOM handler.

Personally, I have better things to do then familiarize my self with
the inner guts of my C++ runtime implementation.

> Yeah, I agree that one cannot sensibly "handle" bad_alloc. It can
> sensibly __report__ it though.


Reporting is handling and you're kidding yourself by trying to
distinguish them. Even reporting may not be possible. Reporting
requires I/O, I/O requires memory and resources. Most exceptions
occur because: 1) you're a bad programmer 2) you're out of some
resource (that you probably ran out of doing I/O anyway).

> The thing is though, a vaaaast majority
> of exceptions, code can't "handle". It can only report them, and in
> rare cases, retry upon some sort o operator's reaction (like, check
> the network and retry saving a file on a share). That makes OOM much
> less special than any other exception, and less of a reason to
> terminate.


No, it means the right thing to do for most exceptions is to
terminate. Logging or reporting is great when it's possible, but
hanging your hat on it being possible is simply absurd.

Adam
 
Reply With Quote
 
Paul
Guest
Posts: n/a
 
      08-31-2011
On Aug 31, 12:47*pm, Adam Skutt <ask...@gmail.com> wrote:
> On Aug 31, 5:23*am, yatremblay@bel1lin202.(none) (Yannick Tremblay)
> wrote:

<snip>
>
> I don't know why people think it's interesting to talk about super
> reliable software but neglect super reliable hardware too. *It's
> impossible to make hardware that never fails (pesky physics) so why
> would I ever bother writing software that never fails? *Software that
> never crashes is useless if the cleaning staff kicks out the power
> cable every night.
>

So attempting to deliver robust software is a waste of time because
some cleaner may switch of the machine at the wall?
The amazing thing about this post is that you're serious.
 
Reply With Quote
 
Goran
Guest
Posts: n/a
 
      08-31-2011
On Aug 31, 2:18*pm, Adam Skutt <ask...@gmail.com> wrote:
> > Programs that do more than one function are at a net loss with "die on
> > OOM" approach, and the loss is bigger the more the functions there are
> > (and the more important they are. Imagine an image processing program.
> > So you apply a transformation, and that OOMs. You die, your user loses
> > his latest changes that worked. But if you go back the stack, clean
> > all those resources transformation needed and say "sorry, OOM", he
> > could have saved (heck, you could have done it for the user, given
> > that we hit OOM). And... Dig this: trying to do the same at the spot
> > you hit OOM is a __mighty__ bad idea. Why? Because memory, and other
> > resources, are likely already scarce, and an attempt to do anything
> > might fail do to that.

>
> Trying to do it at any point is a mighty bad idea. *Rolling back the
> stack and deallocating memory _does not ensure_ future allocations
> will succeed. *Once you reach OOM, you're not assured the user will be
> able to save; you're not assured you can do anything at all. *Trying
> may very well be pointless.


It may be, but you are less helpless than what you are making it out
to be.

Say that you want to save. If your file stream is already there, and
given that saving is logically a "const" operation, there's little
reason for things to go wrong (it's possible, but not likely). Or say
that you want to log the error. Logging is something that must a no-
throw operation, therefore, logging facilities are already there and
ready.

I tried, a long time ago, to eat all my memory and then proceed to
disk I/O. This works on e.g. Unix and windows. Why wouldn't it?

> It's considerably smarter, and absurdly easier, to save off the file
> before attempting the operation in the first place. *This way, your
> code can die in a clean fashion, and the user has a recovery file with
> the changes up to the failed operation. *Trying to create the recovery
> file under or after an OOM condition is very difficult. *However,
> trying to create the recovery file before the OOM condition is
> trivial.


You can't be serious with this. I would really like to see a codebase
that saves state to disk prior to any allocation (any failure
condition, really).

Actually, what could be attempted is saving after any change. But that
won't work well for many-a-editor either. Best you can reasonably do
is to save recovery from time to time.

> In all of the counter-examples everyone's provided so far, the correct
> way to ensure reliability is not to attempt to avoid crashing on OOM.
> The correct way is to write your code so that crashing on OOM becomes
> irrelevant. *This has the added bonus that your code will still be
> reliable even if your process is never told of the OOM condition. *As
> I mentioned before, in some languages you're not promised to be told
> about allocation failure, and other have mentioned that on some
> operating systems your process may simply die instead of being told
> about the failure.
>
> > Or imagine an HTTP server. One request OOMs, you die. You terminate
> > and restart, and you cut off all other concurrent request processing
> > not nice, nor necessary. And so on.

>
> That may happen anyway, as I explained to Yan. *The situation is much
> harder to handle when there is concurrent processing occurring, not
> easier.
>
>
>
> > > Straightforward C++ on most implementations will deallocate memory as
> > > it goes, so when the application runs out of memory, there won't be
> > > anything to free up: retrying the operation will cause the code to
> > > fail in the same place. *Making more memory available requires
> > > rewriting the code to avoid unnecessarily holding on to resources that
> > > it no longer needs.

>
> > That is true, but only if peak memory memory use is actually used to
> > hold program state (heap fragmentation plays it's part, too). My
> > contention is that this the case much less often that you make it out
> > to be.

>
> No, I don't believe most applications unnecessarily cache data and
> would benefit from attempting to free those caches under OOM
> conditions. *Even if I did, the code has to be written to make that
> possible, which is very difficult.


It's not unnecessary caching, it's transient peaks in memory usage
during some work. You often don't know how much memory a given system
has, nor you don't know what e.g. other processes are doing wrt memory
at a time you need more memory.

>
> > > Even when there's memory to free up, writing an exception handler that
> > > actually safely runs under an out-of-memory condition is impressively
> > > difficult.

>
> > I disagree with that, too.

>
> Then I shudder to think about what you actually find difficult. *Have
> you ever tried to do this? *Your commentary strongly suggests not only
> have you not, but you don't have any experience in building reliable
> systems anyway.


But I have. I have been intentionally droving code up the wall with
memory usage and looked at what happens. If you have your resources
prepared up front, it's not hard doing something meaningful in that
handler (depends also what one considers reasonable).

> > First off, when you actually hit the top-
> > level exception handler, chances are, you will have freed some memory.

>
> Which doesn't mean you can make use of it. *When you get
> std::bad_alloc, you must assume there's 0 bytes available. *No new, or
> malloc, etc., anywhere.
>
> > Second, OOM-handling facilities are already made not to allocate
> > anything. E.g. bad_alloc will not try to do it in any implementation
> > I've seen.

>
> It's not the behavior of std::bad_alloc that's problematic.
>
> > I've also seen OOM exception objects pre-allocated
> > statically in non-C++ environments, too (what else?).

>
> Figuring out what you need to handle an OOM condition is not a trivial
> task. *Quick, I want to write out a file during OOM (using iostreams)
> and have it not fail due to lack of memory. *Tell me everything I must
> do to ensure this.


Quick? Why? Because the way to write a critical piece of code is off
the top of one's head? That's not serious.

>
> > There is difficulty, I agree with that, but it's actually trivial: keepin mind
> > that, once you hit that OOM handler (most likely, some top-level
> > exception handler not necessarily tied to OOM), you have all you might
> > need prepared upfront.

>
> Which is exceptionally hard. *Let's say I'm going to attempt your file
> writing idea. *I have to have the stream itself already open and
> ready. *I have to make sure the internal streambuf has enough memory
> to buffer my I/O (or is unbuffered). *I have to make sure my data
> structures are constructed in such a fashion so that no memory
> allocation occurs when I access the data, and that the data is
> accessible when the handler runs. *This means no copying of anything,
> which is not impossible to ensure but difficult.


Meh. You are trying to construct a case of trying to do a lot in case
of a resource shortage in order to prove that __nothing__ can be done
in case of resource shortage. I find this dishonest.

Realistically, here's what I'd do for save case:

try
{
throw zone, lotsa work
}
catch(const whatever& e)
{
inform_operator(e, ...); // nothrow zone
try { save(); } // throw zone
catch(const whatever& e)
{
inform_operator(e, ...); // nothrow zone
}
}

Then, I would specifically test inform_operator under load and try to
make it reasonably resilient to it. But whatever happens, I would not
allow exception to escape out of it. That's all there is to it
conceptually, and practically, there's always room for later
improvement, but __without__ changing conceptual model.

> You gloss over the difficultly, especially when you don't know what
> code will allocate memory, nor when it will do it. *Memory allocation
> can and does occur in places you don't expect, so ensuring memory
> allocation doesn't happen means being intimately familiar with the
> implementation of every type involved in your OOM handler.
>
> Personally, I have better things to do then familiarize my self with
> the inner guts of my C++ runtime implementation.
>
> > Yeah, I agree that one cannot sensibly "handle" bad_alloc. It can
> > sensibly __report__ it though.

>
> Reporting is handling and you're kidding yourself by trying to
> distinguish them. *


I disagree. For me, there's actually no such thing as "error
handling". There's error reporting and there's __program state
handling__ (in face of errors). This is IMO a very important
distinction.

> Even reporting may not be possible. *Reporting
> requires I/O, I/O requires memory and resources.
> *Most exceptions
> occur because: 1) you're a bad programmer 2) you're out of some
> resource (that you probably ran out of doing I/O anyway).


Hmmm... We are most likely in disagreement what are exceptions used
for.

Goran.
 
Reply With Quote
 
Noah Roberts
Guest
Posts: n/a
 
      08-31-2011
On Aug 31, 4:04*am, Paul <pchris...@yahoo.co.uk> wrote:
> On Aug 31, 4:07*am, Noah Roberts <roberts.n...@gmail.com> wrote:> On Aug 30, 6:38*pm, Paul <pchris...@yahoo.co.uk> wrote:
> <snip>
>
> > I'm confused now. *I thought that was YOUR argument. *Is this an
> > example of a self-induced reductio ad absurdum?- Hide quoted text -

>
> My argument was that bad_alloc exceptions should be handled. I don't
> see a reason to ignore them when all the mechanics are in place to
> catch such exceptions and handle them in some appropriate way.


That was surely your thesis, yes, but you repeatedly claimed that
those who disagree were writing horrible code that would crash all the
time.
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Re: Catching exceptions, bad alloc revisited red floyd C++ 3 09-10-2011 03:11 PM
Bad media, bad files or bad Nero? John Computer Information 23 01-08-2008 09:17 PM
ActiveX apologetic Larry Seltzer... "Sun paid for malicious ActiveX code, and Firefox is bad, bad bad baad. please use ActiveX, it's secure and nice!" (ok, the last part is irony on my part) fernando.cassia@gmail.com Java 0 04-16-2005 10:05 PM
24 Season 3 Bad Bad Bad (Spoiler) nospam@nospam.com DVD Video 12 02-23-2005 03:28 AM
24 Season 3 Bad Bad Bad (Spoiler) nospam@nospam.com DVD Video 0 02-19-2005 01:10 AM



Advertisments
 



1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57