Discussion:
[std-discussion] throwing exception under low memory conditions
Michael Kilburn
2017-08-15 07:33:08 UTC
Permalink
Hi,

I have discovered recently that "throw X(...);" terminates application if
it can't allocate memory for exception being thrown. This happens with GCC.
Standard does not specify what should happen in this situation (see C++11
15.1.p4).

I am trying to discuss this situation with someone who is closer to
committee than me... Is it a defect? Standard does not specify behavior
here and implementations are free to terminate (and they do) -- problem is
that this makes impossibly to write a C++ program that survives
out-of-memory situation -- even throwing std::bad_alloc can terminate your
app.

If it isn't a defect -- how to write such program?

I've investigated GCC and apparently it can even hang your application in
this case (deadlock) if you are unlucky enough. If you are interested,
details are here:
https://stackoverflow.com/questions/45497684/what-happens-if-throw-fails-to-allocate-memory-for-exception-object/45552806#45552806

If I am wrong somewhere -- I'd appreciate if my mistake is pointed out.

Thank you,
Michael.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Chris Hallock
2017-08-15 12:18:16 UTC
Permalink
Post by Michael Kilburn
I have discovered recently that "throw X(...);" terminates application if
it can't allocate memory for exception being thrown. This happens with GCC.
Standard does not specify what should happen in this situation (see C++11
15.1.p4).
I am trying to discuss this situation with someone who is closer to
committee than me... Is it a defect? Standard does not specify behavior
here and implementations are free to terminate (and they do) -- problem is
that this makes impossibly to write a C++ program that survives
out-of-memory situation -- even throwing std::bad_alloc can terminate your
app.
This is an implementation-internal memory allocation. What could the
Standard possibly say?
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Michael Kilburn
2017-08-15 15:18:54 UTC
Permalink
Post by Chris Hallock
Post by Michael Kilburn
I have discovered recently that "throw X(...);" terminates application if
it can't allocate memory for exception being thrown. This happens with GCC.
Standard does not specify what should happen in this situation (see C++11
15.1.p4).
I am trying to discuss this situation with someone who is closer to
committee than me... Is it a defect? Standard does not specify behavior
here and implementations are free to terminate (and they do) -- problem is
that this makes impossibly to write a C++ program that survives
out-of-memory situation -- even throwing std::bad_alloc can terminate your
app.
This is an implementation-internal memory allocation. What could the
Standard possibly say?
A lot of different things... For example:
"This allocation failure should result in std::bad_alloc being thrown.
Implementation should never fail to throw std::bad_alloc"

it certainly may come as a surprise to those who use exception
specification, but at least I will be able to write application that
doesn't mysteriously die when it runs out of memory
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Thiago Macieira
2017-08-15 15:27:58 UTC
Permalink
Post by Michael Kilburn
"This allocation failure should result in std::bad_alloc being thrown.
Implementation should never fail to throw std::bad_alloc"
it certainly may come as a surprise to those who use exception
specification, but at least I will be able to write application that
doesn't mysteriously die when it runs out of memory
I thought libstdc++ already had an emergency buffer for throwing
std::bad_alloc in OOM conditions. I don't know if that is something that needs
to be opted in, though. Please see its documentation.

Requiring that the buffer always be allocated could be unnecessary overhead
and violation of C++'s principle of "don't pay for what you don't use".

In any case, you're running on an RTOS of your own, not a generic OS. You
should be able to modify the C++ support library's build to enable the buffer
allocation by default.
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Michael Kilburn
2017-08-16 01:03:39 UTC
Permalink
You should be able to modify the C++ support library's build to enable the
buffer
allocation by default.
I don't see how this will help
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Thiago Macieira
2017-08-16 06:33:40 UTC
Permalink
Post by Michael Kilburn
You should be able to modify the C++ support library's build to enable the
buffer
allocation by default.
I don't see how this will help
How won't it? If you make it work like you want it, then it will.

What part am I missing?
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Michael Kilburn
2017-08-16 07:05:21 UTC
Permalink
Post by Thiago Macieira
Post by Michael Kilburn
You should be able to modify the C++ support library's build to enable
the
Post by Michael Kilburn
buffer
allocation by default.
I don't see how this will help
How won't it? If you make it work like you want it, then it will.
What part am I missing?
I think you confuse my problem with another one. What I am saying is that
throwing any exception can have unspecified behavior under low memory
conditions. 'Unspecified' means standard doesn't mandate certain outcome
and leaves it to implementation. Most popular ones -- call std::terminate.
This means I can't write a (portable) program that reliably handles
out-of-memory condition.

Activating buffer allocation doesn't help really -- first of all it is not
portable, second -- you still end up with limited resource that can run
out. Instead of trying to play race with environment, I'd rather have a
reliable way to handle this situation in the code.

Does it make sense?
Post by Thiago Macieira
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Ville Voutilainen
2017-08-16 09:02:00 UTC
Permalink
Post by Michael Kilburn
I think you confuse my problem with another one. What I am saying is that
throwing any exception can have unspecified behavior under low memory
conditions. 'Unspecified' means standard doesn't mandate certain outcome and
leaves it to implementation. Most popular ones -- call std::terminate. This
means I can't write a (portable) program that reliably handles out-of-memory
condition.
Your earlier example runs out of stack space. See how it behaves if
dynamic allocation is
done instead:
https://wandbox.org/permlink/wLk01q0LnzEdFJvS

The standard can't mandate that constructing an exception cannot
consume stack space.
You can't avoid stack exhaustion in a portable manner, because
detecting it and recovering from it
has a cost that some users would not want to pay, and might not even
be possible on some platforms.
Post by Michael Kilburn
Activating buffer allocation doesn't help really -- first of all it is not
portable, second -- you still end up with limited resource that can run out.
Instead of trying to play race with environment, I'd rather have a reliable
way to handle this situation in the code.
Does it make sense?
Your problem has nothing particular to do with exceptions.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Michael Kilburn
2017-08-16 22:21:24 UTC
Permalink
Post by Michael Kilburn
Post by Michael Kilburn
I think you confuse my problem with another one. What I am saying is
that
Post by Michael Kilburn
throwing any exception can have unspecified behavior under low memory
conditions. 'Unspecified' means standard doesn't mandate certain outcome
and
Post by Michael Kilburn
leaves it to implementation. Most popular ones -- call std::terminate.
This
Post by Michael Kilburn
means I can't write a (portable) program that reliably handles
out-of-memory
Post by Michael Kilburn
condition.
Your earlier example runs out of stack space. See how it behaves if
dynamic allocation is
https://wandbox.org/permlink/wLk01q0LnzEdFJvS
Your example throws pointer to an object -- and therefore succeeds. Here is
how running of stack space looks like, notice the difference in output from
my original example:
https://wandbox.org/permlink/dBBaSsLQ9Rwa9WzI
Post by Michael Kilburn
The standard can't mandate that constructing an exception cannot
consume stack space.
You can't avoid stack exhaustion in a portable manner, because
detecting it and recovering from it
has a cost that some users would not want to pay, and might not even
be possible on some platforms.
Post by Michael Kilburn
Activating buffer allocation doesn't help really -- first of all it is
not
Post by Michael Kilburn
portable, second -- you still end up with limited resource that can run
out.
Post by Michael Kilburn
Instead of trying to play race with environment, I'd rather have a
reliable
Post by Michael Kilburn
way to handle this situation in the code.
Does it make sense?
Your problem has nothing particular to do with exceptions.
Yes, it does.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Ville Voutilainen
2017-08-17 05:02:14 UTC
Permalink
Post by Michael Kilburn
Post by Ville Voutilainen
Your earlier example runs out of stack space. See how it behaves if
dynamic allocation is
https://wandbox.org/permlink/wLk01q0LnzEdFJvS
Your example throws pointer to an object -- and therefore succeeds. Here is
how running of stack space looks like, notice the difference in output from
https://wandbox.org/permlink/dBBaSsLQ9Rwa9WzI
Well, great, you have just shown yourself that you can't catch stack
allocation failures.
If the initialization of an exception object runs into that problem,
you won't get a bad_alloc.
Post by Michael Kilburn
Post by Ville Voutilainen
Your problem has nothing particular to do with exceptions.
Yes, it does.
Except that it does not, as your own example shows.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Michael Kilburn
2017-08-17 05:13:06 UTC
Permalink
Post by Ville Voutilainen
Post by Michael Kilburn
Post by Ville Voutilainen
Your earlier example runs out of stack space. See how it behaves if
dynamic allocation is
https://wandbox.org/permlink/wLk01q0LnzEdFJvS
Your example throws pointer to an object -- and therefore succeeds. Here
is
Post by Michael Kilburn
how running of stack space looks like, notice the difference in output
from
Post by Michael Kilburn
https://wandbox.org/permlink/dBBaSsLQ9Rwa9WzI
Well, great, you have just shown yourself that you can't catch stack
allocation failures.
If the initialization of an exception object runs into that problem,
you won't get a bad_alloc.
Can you show me where 'stack allocation' happens in this disassembled code:
https://godbolt.org/g/kcL1ks
?
Post by Ville Voutilainen
Post by Michael Kilburn
Post by Ville Voutilainen
Your problem has nothing particular to do with exceptions.
Yes, it does.
Except that it does not, as your own example shows.
I can't decide if you are trolling me or not...
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Ville Voutilainen
2017-08-17 05:24:05 UTC
Permalink
Post by Michael Kilburn
Post by Ville Voutilainen
Well, great, you have just shown yourself that you can't catch stack
allocation failures.
If the initialization of an exception object runs into that problem,
you won't get a bad_alloc.
https://godbolt.org/g/kcL1ks
?
In __cxa_allocate_exception?
Post by Michael Kilburn
Post by Ville Voutilainen
Post by Michael Kilburn
Post by Ville Voutilainen
Your problem has nothing particular to do with exceptions.
Yes, it does.
Except that it does not, as your own example shows.
I can't decide if you are trolling me or not...
You seem to be suggesting that I shouldn't take you seriously. I can
certainly accommodate
such wishes with relatively little effort.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Michael Kilburn
2017-08-17 05:28:37 UTC
Permalink
Post by Ville Voutilainen
Post by Michael Kilburn
Post by Ville Voutilainen
Well, great, you have just shown yourself that you can't catch stack
allocation failures.
If the initialization of an exception object runs into that problem,
you won't get a bad_alloc.
Can you show me where 'stack allocation' happens in this disassembled
https://godbolt.org/g/kcL1ks
?
In __cxa_allocate_exception?
http://refspecs.linuxbase.org/abi-eh-1.21.html#cxx-throw

Quote:
Memory will be allocated by the __cxa_allocate_exception runtime library
routine. This routine is passed the size of the exception object to be
thrown (not including the size of the __cxa_exception header), and returns
a pointer to the temporary space for the
Post by Ville Voutilainen
Post by Michael Kilburn
Post by Ville Voutilainen
Post by Ville Voutilainen
Your problem has nothing particular to do with exceptions.
Yes, it does.
Except that it does not, as your own example shows.
I can't decide if you are trolling me or not...
You seem to be suggesting that I shouldn't take you seriously. I can
certainly accommodate
such wishes with relatively little effort.
I am going to ignore any further non-serious messages from you
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Ville Voutilainen
2017-08-17 05:39:52 UTC
Permalink
Post by Michael Kilburn
Post by Ville Voutilainen
You seem to be suggesting that I shouldn't take you seriously. I can
certainly accommodate
such wishes with relatively little effort.
I am going to ignore any further non-serious messages from you
By all means. Once you have convinced the folks on this forum that
there is a problem
that can be solved in a reasonable manner and you have a proposal that
can be considered,
make sure it's backed by a recommendation from a seriously convincing
committee member,
preferably an implementation vendor, lest it is just ignored and not
actually discussed.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Michael Kilburn
2017-08-17 05:42:14 UTC
Permalink
Post by Ville Voutilainen
Post by Michael Kilburn
Post by Ville Voutilainen
You seem to be suggesting that I shouldn't take you seriously. I can
certainly accommodate
such wishes with relatively little effort.
I am going to ignore any further non-serious messages from you
By all means. Once you have convinced the folks on this forum that
there is a problem
that can be solved in a reasonable manner and you have a proposal that
can be considered,
make sure it's backed by a recommendation from a seriously convincing
committee member,
preferably an implementation vendor, lest it is just ignored and not
actually discussed.
I am not sure it is a problem. I don't know for sure that it can be solved.
I came here with questions, ended up doing more answering than listening.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Nicol Bolas
2017-08-17 05:51:48 UTC
Permalink
Post by Michael Kilburn
Post by Michael Kilburn
Post by Michael Kilburn
Post by Ville Voutilainen
Your earlier example runs out of stack space. See how it behaves if
dynamic allocation is
https://wandbox.org/permlink/wLk01q0LnzEdFJvS
Your example throws pointer to an object -- and therefore succeeds.
Here is
Post by Michael Kilburn
how running of stack space looks like, notice the difference in output
from
Post by Michael Kilburn
https://wandbox.org/permlink/dBBaSsLQ9Rwa9WzI
Well, great, you have just shown yourself that you can't catch stack
allocation failures.
If the initialization of an exception object runs into that problem,
you won't get a bad_alloc.
https://godbolt.org/g/kcL1ks
?
Your compiler happens to be eliding the temporary. But if it
didn't/couldn't do that, then it would be forced to create a temporary,
then copy/move into it.

A temporary that would be on the stack. And thus, not throw `bad_alloc`
when it causes a stack overflow.

So why should elision of the large exception cause a recoverable error,
when not eliding the exception will cause an unrecoverable failure?

Your general issue is potentially valid, but your specific example is not.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Michael Kilburn
2017-08-17 06:06:59 UTC
Permalink
Post by Nicol Bolas
Post by Michael Kilburn
Post by Michael Kilburn
Post by Michael Kilburn
Post by Ville Voutilainen
Your earlier example runs out of stack space. See how it behaves if
dynamic allocation is
https://wandbox.org/permlink/wLk01q0LnzEdFJvS
Your example throws pointer to an object -- and therefore succeeds.
Here is
Post by Michael Kilburn
how running of stack space looks like, notice the difference in output
from
Post by Michael Kilburn
https://wandbox.org/permlink/dBBaSsLQ9Rwa9WzI
Well, great, you have just shown yourself that you can't catch stack
allocation failures.
If the initialization of an exception object runs into that problem,
you won't get a bad_alloc.
https://godbolt.org/g/kcL1ks
?
Your compiler happens to be eliding the temporary. But if it
didn't/couldn't do that, then it would be forced to create a temporary,
then copy/move into it.
In this case you'd get different message, as demonstrated by my previous
example. Which I provided in response to "your code overflows the stack" as
a proof that stack overflow results in different outcome than what was
observed.
Post by Nicol Bolas
A temporary that would be on the stack. And thus, not throw `bad_alloc`
when it causes a stack overflow.
So why should elision of the large exception cause a recoverable error,
when not eliding the exception will cause an unrecoverable failure?
Here is my view on this -- there is no copy elision here. Memory for
exception object is allocated in "unspecified way" then expression is used
to construct an object there. If implementation (MSVC) decides to use stack
for this allocation -- error may not be recoverable (depends if compiler
can detect where stack ends or not; and if decided to do this check at
all). In any case stack overflow is an acknowledged case when app should
die.

If implementation (GCC) uses other means -- it should be recoverable,
right? This is basically a variation of the question I asked in original
post but reformulated for GCC. I don't know the answer -- I am looking for
it right now, in this forum. Because this is the best place I found to
discuss it so far.
Post by Nicol Bolas
Your general issue is potentially valid, but your specific example is not.
It was a specific example compiled on specific compiler to show that while
C++ standard does not mandate specific behavior in this case -- popular
implementation chose std::terminate. How this not a valid example?
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Nicol Bolas
2017-08-15 15:28:27 UTC
Permalink
Post by Michael Kilburn
Post by Chris Hallock
Post by Michael Kilburn
I have discovered recently that "throw X(...);" terminates application
if it can't allocate memory for exception being thrown. This happens with
GCC. Standard does not specify what should happen in this situation (see
C++11 15.1.p4).
I am trying to discuss this situation with someone who is closer to
committee than me... Is it a defect? Standard does not specify behavior
here and implementations are free to terminate (and they do) -- problem is
that this makes impossibly to write a C++ program that survives
out-of-memory situation -- even throwing std::bad_alloc can terminate your
app.
This is an implementation-internal memory allocation. What could the
Standard possibly say?
"This allocation failure should result in std::bad_alloc being thrown.
Implementation should never fail to throw std::bad_alloc"
Well that would change nothing, since "should" only specifies a
recommendation ;) The word you're looking for is "must".

That being said, I don't think I would consider this a "defect" in the
standard. It seems likely that this is deliberately left as an
implementation detail. Basically, it's a quality-of-implementation issue;
an implementation can provide a guarantee that all
standard-library-generated `std::bad_alloc`s will be able to be generated.
But they aren't *required* to do so.

it certainly may come as a surprise to those who use exception
Post by Michael Kilburn
specification, but at least I will be able to write application that
doesn't mysteriously die when it runs out of memory
Unless it runs out of memory due to a stack allocation. In which case,
you're in undefined behavior land. At least if an exception cannot be
allocated, `std::terminate` will be called. With a stack allocation OOM
error, you won't even get that.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Michael Kilburn
2017-08-15 15:43:36 UTC
Permalink
Post by Nicol Bolas
Post by Michael Kilburn
Post by Chris Hallock
Post by Michael Kilburn
I have discovered recently that "throw X(...);" terminates application
if it can't allocate memory for exception being thrown. This happens with
GCC. Standard does not specify what should happen in this situation (see
C++11 15.1.p4).
I am trying to discuss this situation with someone who is closer to
committee than me... Is it a defect? Standard does not specify behavior
here and implementations are free to terminate (and they do) -- problem is
that this makes impossibly to write a C++ program that survives
out-of-memory situation -- even throwing std::bad_alloc can terminate your
app.
This is an implementation-internal memory allocation. What could the
Standard possibly say?
"This allocation failure should result in std::bad_alloc being thrown.
Implementation should never fail to throw std::bad_alloc"
Well that would change nothing, since "should" only specifies a
recommendation ;) The word you're looking for is "must".
Naturally :-)


That being said, I don't think I would consider this a "defect" in the
Post by Nicol Bolas
standard. It seems likely that this is deliberately left as an
implementation detail. Basically, it's a quality-of-implementation issue;
an implementation can provide a guarantee that all
standard-library-generated `std::bad_alloc`s will be able to be generated.
But they aren't *required* to do so.
I believe QoI isn't an issue here -- real issue is that standard does not
allow std::bad_alloc to be thrown in this case (am I wrong?). Therefore
implementations opt for std::terminate(). If it was allowed -- every
exception specification would need to be implicitly extended with
std::bad_alloc -- and I am pretty sure Standard doesn't mention that.
Post by Nicol Bolas
it certainly may come as a surprise to those who use exception
Post by Michael Kilburn
specification, but at least I will be able to write application that
doesn't mysteriously die when it runs out of memory
Unless it runs out of memory due to a stack allocation. In which case,
you're in undefined behavior land. At least if an exception cannot be
allocated, `std::terminate` will be called. With a stack allocation OOM
error, you won't even get that.
I can control local variables and call depth in my code -- therefore I can
avoid running out of stack. I can't avoid out-of-memory including running
out of these emergency exception buffers, especially considering that now I
can have many threads and retain (and chain) exceptions indefinitely via
std::current_exception.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Thiago Macieira
2017-08-15 17:40:42 UTC
Permalink
Post by Michael Kilburn
Post by Nicol Bolas
That being said, I don't think I would consider this a "defect" in the
standard. It seems likely that this is deliberately left as an
implementation detail. Basically, it's a quality-of-implementation issue;
an implementation can provide a guarantee that all
standard-library-generated `std::bad_alloc`s will be able to be generated.
But they aren't *required* to do so.
I believe QoI isn't an issue here -- real issue is that standard does not
allow std::bad_alloc to be thrown in this case (am I wrong?). Therefore
implementations opt for std::terminate(). If it was allowed -- every
exception specification would need to be implicitly extended with
std::bad_alloc -- and I am pretty sure Standard doesn't mention that.
Why would the standard forbid throwing? Where did you find the text that gives
you that impression?

Forget exception specifications, they are deprecated and removed from the
language now. There's only noexcept(true) and noexcept(false).
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Michael Kilburn
2017-08-16 01:33:43 UTC
Permalink
Post by Thiago Macieira
Post by Michael Kilburn
I believe QoI isn't an issue here -- real issue is that standard does
not
Post by Michael Kilburn
allow std::bad_alloc to be thrown in this case (am I wrong?). Therefore
implementations opt for std::terminate(). If it was allowed -- every
exception specification would need to be implicitly extended with
std::bad_alloc -- and I am pretty sure Standard doesn't mention that.
Why would the standard forbid throwing? Where did you find the text that gives
you that impression?
I can't find anything that forbids throwing another exception instead of
one I specified in my throw statement. Though (especially since this
possibility isn't mentioned) expectation is that it shouldn't happen

How about this argument:

Leaving this case unspecified prevents me from writing portable
code that handles OOM.

Is it good enough? One implementation will decide to throw bad_alloc,
another -- runtime_error. There is no indication in standard wrt what to
expect...
Post by Thiago Macieira
Forget exception specifications, they are deprecated and removed from the
language now. There's only noexcept(true) and noexcept(false).
Ok, so if you believe this isn't a language defect -- there was a followup
question:

Can you write C++ program that handles OOM and how?

I know for a fact that I can do it in C. Would really like to be able to do
it in C++.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Nicol Bolas
2017-08-16 02:14:34 UTC
Permalink
Forget exception specifications, they are deprecated and removed from the
Post by Michael Kilburn
Post by Thiago Macieira
language now. There's only noexcept(true) and noexcept(false).
Ok, so if you believe this isn't a language defect -- there was a followup
Can you write C++ program that handles OOM and how?
I know for a fact that I can do it in C. Would really like to be able to
do it in C++.
Catch `std::bad_alloc` at the appropriate locations. It's easier than in C.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Michael Kilburn
2017-08-16 03:00:59 UTC
Permalink
Post by Thiago Macieira
Forget exception specifications, they are deprecated and removed from the
Post by Michael Kilburn
Post by Thiago Macieira
language now. There's only noexcept(true) and noexcept(false).
Ok, so if you believe this isn't a language defect -- there was a
Can you write C++ program that handles OOM and how?
I know for a fact that I can do it in C. Would really like to be able to
do it in C++.
Catch `std::bad_alloc` at the appropriate locations. It's easier than in C.
Well, it doesn't work -- app gets terminated (GCC) and according to
standard it is ok. I don't see how this constitutes "handling OOM" .
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Nicol Bolas
2017-08-16 03:09:37 UTC
Permalink
Post by Michael Kilburn
Post by Thiago Macieira
Forget exception specifications, they are deprecated and removed from the
Post by Michael Kilburn
Post by Thiago Macieira
language now. There's only noexcept(true) and noexcept(false).
Ok, so if you believe this isn't a language defect -- there was a
Can you write C++ program that handles OOM and how?
I know for a fact that I can do it in C. Would really like to be able to
do it in C++.
Catch `std::bad_alloc` at the appropriate locations. It's easier than in C.
Well, it doesn't work -- app gets terminated (GCC) and according to
standard it is ok. I don't see how this constitutes "handling OOM" .
Are you sure the problem is the inability of the implementation to throw an
exception? What evidence do you have to support that conclusion?
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Michael Kilburn
2017-08-16 03:25:56 UTC
Permalink
Post by Nicol Bolas
Post by Michael Kilburn
Post by Thiago Macieira
Forget exception specifications, they are deprecated and removed from
Post by Michael Kilburn
Post by Thiago Macieira
the
language now. There's only noexcept(true) and noexcept(false).
Ok, so if you believe this isn't a language defect -- there was a
Can you write C++ program that handles OOM and how?
I know for a fact that I can do it in C. Would really like to be able
to do it in C++.
Catch `std::bad_alloc` at the appropriate locations. It's easier than in C.
Well, it doesn't work -- app gets terminated (GCC) and according to
standard it is ok. I don't see how this constitutes "handling OOM" .
Are you sure the problem is the inability of the implementation to throw
an exception? What evidence do you have to support that conclusion?
I have three pieces of evidence:
- my copy of C++11 standard that leaves behavior in this case unspecified
(according to my reading)
- trivial code that tries to allocate very big exception:
https://wandbox.org/permlink/xe3OZMxKp5f4BQ0o
- Itanium C++ ABI that is apparently used by GCC and Clang that mandates
std::terminate in this case:
https://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html#cxx-throw
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Nicol Bolas
2017-08-16 03:47:15 UTC
Permalink
Post by Michael Kilburn
Post by Nicol Bolas
Post by Michael Kilburn
Well, it doesn't work -- app gets terminated (GCC) and according to
standard it is ok. I don't see how this constitutes "handling OOM" .
Are you sure the problem is the inability of the implementation to throw
an exception? What evidence do you have to support that conclusion?
- my copy of C++11 standard that leaves behavior in this case unspecified
(according to my reading)
https://wandbox.org/permlink/xe3OZMxKp5f4BQ0o
- Itanium C++ ABI that is apparently used by GCC and Clang that mandates
https://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html#cxx-throw
... OK, I think I see what you're saying. You're not talking about when
`std::bad_alloc` would be thrown but can't be thrown due to lack of memory.
You're talking about when throwing an *unrelated* exception fails due to
lack of memory; that the exception is what is *causing* the lack of memory.

In that case, there is logically nothing that *can* be done. You're
basically talking about a failure of the system's ability to *report a
failure*. Functionally, it's no different from having the copy/move
constructor for an exception type emit an exception. If the mechanics
involved in a throw expression fail (whether it's the copy/move into the
exception object or the allocation of the exception object), then that
means the system is too broken to continue.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Michael Kilburn
2017-08-16 03:59:50 UTC
Permalink
Post by Nicol Bolas
Post by Michael Kilburn
Post by Nicol Bolas
Post by Michael Kilburn
Well, it doesn't work -- app gets terminated (GCC) and according to
standard it is ok. I don't see how this constitutes "handling OOM" .
Are you sure the problem is the inability of the implementation to throw
an exception? What evidence do you have to support that conclusion?
- my copy of C++11 standard that leaves behavior in this case unspecified
(according to my reading)
https://wandbox.org/permlink/xe3OZMxKp5f4BQ0o
- Itanium C++ ABI that is apparently used by GCC and Clang that mandates
https://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html#cxx-throw
... OK, I think I see what you're saying. You're not talking about when
`std::bad_alloc` would be thrown but can't be thrown due to lack of memory.
You're talking about when throwing an *unrelated* exception fails due to
lack of memory; that the exception is what is *causing* the lack of memory.
In that case, there is logically nothing that *can* be done. You're
basically talking about a failure of the system's ability to *report a
failure*. Functionally, it's no different from having the copy/move
constructor for an exception type emit an exception. If the mechanics
involved in a throw expression fail (whether it's the copy/move into the
exception object or the allocation of the exception object), then that
means the system is too broken to continue.
Not exactly -- from my (admittedly limited) point of view nothing prevents
Standard from mandating smth like this:

"If implementation fails to allocate memory in this case --
std::bad_alloc must be thrown. Throwing std::bad_alloc must not fail."


and implementation can simply pre-can std::bad_alloc object and always use
it when object of this type is thrown. Or treat std::bad_alloc case as smth
special (and pass NULL internally -- smth that doesn't need to touch heap).
In old times you'd also need to update related section of standard that
deals with exception specifications, but nowadays it is not required (since
that feature was deprecated).


Questions is: what prevents standard from doing this? why it leaves this
case unspecified?

I keep asking around but can't get any good answers.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Nicol Bolas
2017-08-16 04:06:13 UTC
Permalink
Post by Michael Kilburn
Post by Nicol Bolas
Post by Michael Kilburn
Post by Nicol Bolas
Post by Michael Kilburn
Well, it doesn't work -- app gets terminated (GCC) and according to
standard it is ok. I don't see how this constitutes "handling OOM" .
Are you sure the problem is the inability of the implementation to
throw an exception? What evidence do you have to support that conclusion?
- my copy of C++11 standard that leaves behavior in this case
unspecified (according to my reading)
https://wandbox.org/permlink/xe3OZMxKp5f4BQ0o
- Itanium C++ ABI that is apparently used by GCC and Clang that mandates
https://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html#cxx-throw
... OK, I think I see what you're saying. You're not talking about when
`std::bad_alloc` would be thrown but can't be thrown due to lack of memory.
You're talking about when throwing an *unrelated* exception fails due to
lack of memory; that the exception is what is *causing* the lack of memory.
In that case, there is logically nothing that *can* be done. You're
basically talking about a failure of the system's ability to *report a
failure*. Functionally, it's no different from having the copy/move
constructor for an exception type emit an exception. If the mechanics
involved in a throw expression fail (whether it's the copy/move into the
exception object or the allocation of the exception object), then that
means the system is too broken to continue.
Not exactly -- from my (admittedly limited) point of view nothing prevents
"If implementation fails to allocate memory in this case --
std::bad_alloc must be thrown. Throwing std::bad_alloc must not fail."
and implementation can simply pre-can std::bad_alloc object and always use
it when object of this type is thrown. Or treat std::bad_alloc case as smth
special (and pass NULL internally -- smth that doesn't need to touch heap).
In old times you'd also need to update related section of standard that
deals with exception specifications, but nowadays it is not required (since
that feature was deprecated).
Questions is: what prevents standard from doing this? why it leaves this
case unspecified?
Because it doesn't work. You can have multiple exceptions in flight. You
can have multiple `bad_alloc` exceptions in flight. Each of those objects
will need separate and distinct storage, since they are separate and
distinct objects.

And therefore, you will run out of that "smth that doesn't need to touch
heap" memory. At which point, you'll be right back here at `std::terminate`.

So your suggestion solves nothing.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Michael Kilburn
2017-08-16 04:12:30 UTC
Permalink
Post by Nicol Bolas
Post by Michael Kilburn
Post by Nicol Bolas
Post by Michael Kilburn
Post by Nicol Bolas
Post by Michael Kilburn
Well, it doesn't work -- app gets terminated (GCC) and according to
standard it is ok. I don't see how this constitutes "handling OOM" .
Are you sure the problem is the inability of the implementation to
throw an exception? What evidence do you have to support that conclusion?
- my copy of C++11 standard that leaves behavior in this case
unspecified (according to my reading)
https://wandbox.org/permlink/xe3OZMxKp5f4BQ0o
- Itanium C++ ABI that is apparently used by GCC and Clang that
https://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html#cxx-throw
... OK, I think I see what you're saying. You're not talking about when
`std::bad_alloc` would be thrown but can't be thrown due to lack of memory.
You're talking about when throwing an *unrelated* exception fails due
to lack of memory; that the exception is what is *causing* the lack of
memory.
In that case, there is logically nothing that *can* be done. You're
basically talking about a failure of the system's ability to *report a
failure*. Functionally, it's no different from having the copy/move
constructor for an exception type emit an exception. If the mechanics
involved in a throw expression fail (whether it's the copy/move into the
exception object or the allocation of the exception object), then that
means the system is too broken to continue.
Not exactly -- from my (admittedly limited) point of view nothing
"If implementation fails to allocate memory in this case --
std::bad_alloc must be thrown. Throwing std::bad_alloc must not fail."
and implementation can simply pre-can std::bad_alloc object and always
use it when object of this type is thrown. Or treat std::bad_alloc case as
smth special (and pass NULL internally -- smth that doesn't need to touch
heap). In old times you'd also need to update related section of standard
that deals with exception specifications, but nowadays it is not required
(since that feature was deprecated).
Questions is: what prevents standard from doing this? why it leaves this
case unspecified?
Because it doesn't work. You can have multiple exceptions in flight. You
can have multiple `bad_alloc` exceptions in flight. Each of those objects
will need separate and distinct storage, since they are separate and
distinct objects.
And therefore, you will run out of that "smth that doesn't need to touch
heap" memory. At which point, you'll be right back here at `std::terminate`.
So your suggestion solves nothing.
Why each std::bad_alloc instance has to use a separate and distinct
storage? What prevents standard from mandating that each "throw
std::bad_alloc()" has to use the same instance? It is not like it is
mutable...

Plus, I imagine there might be other ways to deal with this problem.
Effectively all we need to do is to pass a 'no memory' flag to another
function (that unwinds stack, matches exception types against one mentioned
in catch block and etc).
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Patrice Roy
2017-08-16 03:02:41 UTC
Permalink
If the issue is whether to do some last chance action or not, catch(...) is
probably your friend. When everything fails, fine diagnostics might not cut
it, but taking some action such as closing down a nuclear reactor can be
interesting.

As was mentioned before, the behavior of the underlying exception handling
mechanism, allocating memory or not, is not something the standard imposes.
If you are dealing with an open source implementation, you can probably get
the control you need.

Out of curiosity, have you seen this presentation on surviving bad_alloc?
(maybe it doesn't relate to
your use-case, but just in case... :) )
Post by Thiago Macieira
Forget exception specifications, they are deprecated and removed from the
Post by Michael Kilburn
Post by Thiago Macieira
language now. There's only noexcept(true) and noexcept(false).
Ok, so if you believe this isn't a language defect -- there was a
Can you write C++ program that handles OOM and how?
I know for a fact that I can do it in C. Would really like to be able to
do it in C++.
Catch `std::bad_alloc` at the appropriate locations. It's easier than in C.
--
---
You received this message because you are subscribed to the Google Groups
"ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an
Visit this group at https://groups.google.com/a/isocpp.org/group/std-
discussion/.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Michael Kilburn
2017-08-16 03:21:20 UTC
Permalink
Post by Patrice Roy
If the issue is whether to do some last chance action or not, catch(...)
is probably your friend. When everything fails, fine diagnostics might not
cut it, but taking some action such as closing down a nuclear reactor can
be interesting.
Once again, this code can terminate in GCC in low memory situation before
executing catch block:

void foo()
try
{
int k = read_user_input()
if (k <= 0)
throw std::runtime_error("Value has to be greater than zero");
}
catch(...)
{
lower_reactor_rods();
}

and according to my reading of standard it is an acceptable behavior. I am
looking for answer to these questions:
- why is it like this?
- is it possible to write C++ program (that throws exceptions) that is
capable of reliably handling out-of-memory?



As was mentioned before, the behavior of the underlying exception handling
Post by Patrice Roy
mechanism, allocating memory or not, is not something the standard imposes.
If you are dealing with an open source implementation, you can probably get
the control you need.
If this is true -- this makes it impossible to write code that reliably
handles OOMs (unless I am missing smth very obvious). It seems like a
deficiency in standard and (unless there is a very good reason not to) it
needs to be fixed.
Post by Patrice Roy
Out of curiosity, have you seen this presentation on surviving bad_alloc?
http://youtu.be/QIiFsqsb9HM (maybe it doesn't relate to
your use-case, but just in case... :) )
Yes, I saw it. In fact I (with my friend) built at least one system that
was surviving std::bad_alloc on regular basis (obscure stock trading
engine). I was very proud of that system and thought that it is
bullet-proof -- turned out it isn't, it still can crash.
Post by Patrice Roy
Post by Thiago Macieira
Forget exception specifications, they are deprecated and removed from the
Post by Michael Kilburn
Post by Thiago Macieira
language now. There's only noexcept(true) and noexcept(false).
Ok, so if you believe this isn't a language defect -- there was a
Can you write C++ program that handles OOM and how?
I know for a fact that I can do it in C. Would really like to be able to
do it in C++.
Catch `std::bad_alloc` at the appropriate locations. It's easier than in C.
--
---
You received this message because you are subscribed to the Google Groups
"ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an
Visit this group at
https://groups.google.com/a/isocpp.org/group/std-discussion/.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Nicol Bolas
2017-08-16 03:59:24 UTC
Permalink
Post by Michael Kilburn
Post by Patrice Roy
If the issue is whether to do some last chance action or not, catch(...)
is probably your friend. When everything fails, fine diagnostics might not
cut it, but taking some action such as closing down a nuclear reactor can
be interesting.
Once again, this code can terminate in GCC in low memory situation before
void foo()
try
{
int k = read_user_input()
if (k <= 0)
throw std::runtime_error("Value has to be greater than zero");
}
catch(...)
{
lower_reactor_rods();
}
and according to my reading of standard it is an acceptable behavior. I am
- why is it like this?
Because when the ability to report errors stops working, the ability to
report errors has stopped working. ;) Much like if a program issues a wild
write that happens to land on `errno`'s storage, you can't rely on the
value of `errno` to be consistent with expectations.

This is also why such a scenario calls `std::terminate`; this function
exists for dealing with "things got *really* broken" scenarios. You can
register your own termination function, and thus be able to deal with
catastrophic conditions.

- is it possible to write C++ program (that throws exceptions) that is
Post by Michael Kilburn
capable of reliably handling out-of-memory?
"Reliably" is of course in the eyes of the beholder. The above code is
quite reliable. Indeed, implementations might even guarantee that it works.
If you're in a safety critical scenario, you always register a terminate
handler.
Post by Michael Kilburn
Post by Patrice Roy
Therefore implementations opt for std::terminate(). If it was allowed --
Post by Michael Kilburn
every exception specification would need to be implicitly extended with
std::bad_alloc -- and I am pretty sure Standard doesn't mention that.
That's Java, not C++. In C++, when we had exception specifications, if
nothing was listed, you throw *anything*.
I know, but if standard mandate throwing bad_alloc in this case code like
You've merely restated reason #4,658 as to why C++ doesn't have this
feature anymore ;)
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Michael Kilburn
2017-08-16 04:07:29 UTC
Permalink
Post by Nicol Bolas
Post by Michael Kilburn
Post by Patrice Roy
If the issue is whether to do some last chance action or not, catch(...)
is probably your friend. When everything fails, fine diagnostics might not
cut it, but taking some action such as closing down a nuclear reactor can
be interesting.
Once again, this code can terminate in GCC in low memory situation before
void foo()
try
{
int k = read_user_input()
if (k <= 0)
throw std::runtime_error("Value has to be greater than zero");
}
catch(...)
{
lower_reactor_rods();
}
and according to my reading of standard it is an acceptable behavior. I
- why is it like this?
Because when the ability to report errors stops working, the ability to
report errors has stopped working. ;) Much like if a program issues a wild
write that happens to land on `errno`'s storage, you can't rely on the
value of `errno` to be consistent with expectations.
I am not convinced that "ability to report errors stop working" (see my
answer to your other message). After all, I don't need to allocate memory
to pass flag that there is no memory left. Why do you believe that this
case can't be handled?


This is also why such a scenario calls `std::terminate`; this function
Post by Nicol Bolas
exists for dealing with "things got *really* broken" scenarios. You can
register your own termination function, and thus be able to deal with
catastrophic conditions.
- is it possible to write C++ program (that throws exceptions) that is
Post by Michael Kilburn
capable of reliably handling out-of-memory?
"Reliably" is of course in the eyes of the beholder. The above code is
quite reliable. Indeed, implementations might even guarantee that it works.
If you're in a safety critical scenario, you always register a terminate
handler.
Yes, but it seems that most popular implementations decided not to provide
this guarantee. And you can't do anything useful in terminate handler.
Post by Nicol Bolas
Post by Michael Kilburn
Post by Patrice Roy
Therefore implementations opt for std::terminate(). If it was allowed --
Post by Michael Kilburn
every exception specification would need to be implicitly extended with
std::bad_alloc -- and I am pretty sure Standard doesn't mention that.
That's Java, not C++. In C++, when we had exception specifications, if
nothing was listed, you throw *anything*.
I know, but if standard mandate throwing bad_alloc in this case code like
You've merely restated reason #4,658 as to why C++ doesn't have this
feature anymore ;)
I wasn't a big fan of that feature anyway :)
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Thiago Macieira
2017-08-16 06:36:45 UTC
Permalink
Post by Michael Kilburn
Leaving this case unspecified prevents me from writing portable
code that handles OOM.
You can't portably handle OOM anyway.

On a modern, multitasking operating system with virtual memory, OOM is
signalled by killing some processes. You don't get notified by it. Your memory
allocations succeed, but you may get SIGBUS when you try to access it.
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Michael Kilburn
2017-08-16 07:00:32 UTC
Permalink
Post by Thiago Macieira
Post by Michael Kilburn
Leaving this case unspecified prevents me from writing portable
code that handles OOM.
You can't portably handle OOM anyway.
I can limit my memory allocations (by intercepting malloc), I can run 32bit
app on 64 bit machine with 16GB of memory, and etc.
Post by Thiago Macieira
On a modern, multitasking operating system with virtual memory, OOM is
signalled by killing some processes. You don't get notified by it. Your
Post by Thiago Macieira
memory
allocations succeed, but you may get SIGBUS when you try to access it.
Yes, everyone knows about OOM-killer. Linux can be configured to overcommit
-- in this case apps aren't killed.
Post by Thiago Macieira
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Thiago Macieira
2017-08-16 15:43:00 UTC
Permalink
Post by Michael Kilburn
Post by Thiago Macieira
Post by Michael Kilburn
Leaving this case unspecified prevents me from writing portable
code that handles OOM.
You can't portably handle OOM anyway.
I can limit my memory allocations (by intercepting malloc), I can run 32bit
app on 64 bit machine with 16GB of memory, and etc.
None of which guard you against another process consuming 15.99 GB of RAM.
Post by Michael Kilburn
Post by Thiago Macieira
On a modern, multitasking operating system with virtual memory, OOM is
signalled by killing some processes. You don't get notified by it. Your memory
allocations succeed, but you may get SIGBUS when you try to access it.
Yes, everyone knows about OOM-killer. Linux can be configured to overcommit
-- in this case apps aren't killed.
Did you mean "not overcommit"? Because if you do configure Linux to overcommit,
then brk() and anonymous mmap may succeed, but then fail to deliver when the
pages are faulted. If that's the case, you get a SIGBUS.

But even in non-overcommit configuration, I'm pretty sure you can cause the
kernel to commit to more, such as by mapping files, creating files on a tmpfs or
using memfd.

My point is that it's extremely difficult to deal with OOM conditions in a *non*
portable manner by fiddling with your OOM score adjustment, doing privileged-
user operations like mlock() or changing the overcommit settings, or even
compiling your own kernel. If you are prepared to do all that, then you should
be able to deal with the platform-specific way of throwing exceptions too.
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Andrey Semashev
2017-08-16 15:54:52 UTC
Permalink
Post by Thiago Macieira
My point is that it's extremely difficult to deal with OOM conditions in a *non*
portable manner by fiddling with your OOM score adjustment, doing privileged-
user operations like mlock() or changing the overcommit settings, or even
compiling your own kernel. If you are prepared to do all that, then you should
be able to deal with the platform-specific way of throwing exceptions too.
Although I agree with the status quo that you have to deal with
platform-specific stuff to handle OOM gracefully, I disagree with the
"let's just not handle it" response to the problem. And no, calling
std::terminate or deadlocking when one tries to throw an exception is
not a good solution in my view. Throwing std::bad_alloc (reliably)
instead of user's exception would be.

I think the language has to mandate some sort of escape latch for
std::bad_alloc, even if it means special casing this particular
exception in some ways. Although I realize that that would cause ABI
breakage and therefore would not be an easy change.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Thiago Macieira
2017-08-16 16:01:32 UTC
Permalink
Post by Andrey Semashev
Although I agree with the status quo that you have to deal with
platform-specific stuff to handle OOM gracefully, I disagree with the
"let's just not handle it" response to the problem. And no, calling
std::terminate or deadlocking when one tries to throw an exception is
not a good solution in my view. Throwing std::bad_alloc (reliably)
instead of user's exception would be.
I think the language has to mandate some sort of escape latch for
std::bad_alloc, even if it means special casing this particular
exception in some ways. Although I realize that that would cause ABI
breakage and therefore would not be an easy change.
I agree that the language should mandate some sort of escape latch, but I
think it should be std::terminate. That's how we deal with double faults
elsewhere and this is a double fault.

Still, the text of the language needs to be careful, because other things
besides OOM can cause the exception/unwind system to fail. For example, if you
are in a stack overflow condition, you can't *call* __cxa_throw(), so you can't
throw. If the unwind tables have been damaged or don't cover the piece of code
being unwound (fallen off the edge of a function? called a faulty pointer?),
you can't unwind either.

In other words, we need an escape latch for the escape latch (an exception to
the exception to throwing exceptions) which is back again to allowed to fail.
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Andrey Semashev
2017-08-16 16:26:45 UTC
Permalink
Post by Thiago Macieira
Post by Andrey Semashev
Although I agree with the status quo that you have to deal with
platform-specific stuff to handle OOM gracefully, I disagree with the
"let's just not handle it" response to the problem. And no, calling
std::terminate or deadlocking when one tries to throw an exception is
not a good solution in my view. Throwing std::bad_alloc (reliably)
instead of user's exception would be.
I think the language has to mandate some sort of escape latch for
std::bad_alloc, even if it means special casing this particular
exception in some ways. Although I realize that that would cause ABI
breakage and therefore would not be an easy change.
I agree that the language should mandate some sort of escape latch, but I
think it should be std::terminate. That's how we deal with double faults
elsewhere and this is a double fault.
std::terminate leaves no room for recovery, that's my main problem with
it. And the user's code may be written in a way that makes recovery
possible, if only in majority of cases.
Post by Thiago Macieira
Still, the text of the language needs to be careful, because other things
besides OOM can cause the exception/unwind system to fail. For example, if you
are in a stack overflow condition, you can't *call* __cxa_throw(), so you can't
throw. If the unwind tables have been damaged or don't cover the piece of code
being unwound (fallen off the edge of a function? called a faulty pointer?),
you can't unwind either.
Damaged stack or unwind tables are only possible as a result of program
misbehavior or miscompilation, the language need not guarantee anything
in this case. OOM, on the other hand, can happen for a perfectly valid
program, so the language has to offer the programmer ways to handle it
gracefully.

Stack overflow I tend to consider as a result of misbehavior as well, as
it is unlikely to be a result of a well behaved program, but I could
probably be convinced otherwise on this particular case. In any case,
stack overflow is a separate problem that is not related to exceptions,
and it may have a different solution. For example, the language could
introduce another exception for this purpose, and the implementation
would be bound to always reserve enough stack space to be able to throw
this exception.
Post by Thiago Macieira
In other words, we need an escape latch for the escape latch (an exception to
the exception to throwing exceptions) which is back again to allowed to fail.
I don't think so. For example, allowing the implementation to throw
shared pre-allocated instance of std::bad_alloc might be the solution to
the particular problem with exceptions. The other problems you mentioned
either don't need to be solved or can have different solutions.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Thiago Macieira
2017-08-16 22:51:27 UTC
Permalink
Post by Andrey Semashev
Post by Thiago Macieira
Post by Andrey Semashev
I think the language has to mandate some sort of escape latch for
std::bad_alloc, even if it means special casing this particular
exception in some ways. Although I realize that that would cause ABI
breakage and therefore would not be an easy change.
I agree that the language should mandate some sort of escape latch, but I
think it should be std::terminate. That's how we deal with double faults
elsewhere and this is a double fault.
std::terminate leaves no room for recovery, that's my main problem with
it. And the user's code may be written in a way that makes recovery
possible, if only in majority of cases.
But you shouldn't try and recover. If you can t properly report your failure
condition, then you can't recover from it. Changing the exception type to
std::bad_alloc may cause further problems up the stack.

If you have a double fault, std::terminate is the only thing you can do.
Post by Andrey Semashev
Stack overflow I tend to consider as a result of misbehavior as well, as
it is unlikely to be a result of a well behaved program, but I could
probably be convinced otherwise on this particular case. In any case,
stack overflow is a separate problem that is not related to exceptions,
and it may have a different solution. For example, the language could
introduce another exception for this purpose, and the implementation
would be bound to always reserve enough stack space to be able to throw
this exception.
I don't agree. A well-behaved program could very well exhaust stack space
without causing an infinite recursion. You may call it poorly-written to
allocate gobs of memory on the stack, but that's still well-behaved in other
conditions.

Also, the system configuration may have lowered the stack size for some reason.
So your program that is well-behaved somewhere isn't elsewhere.
Post by Andrey Semashev
Post by Thiago Macieira
In other words, we need an escape latch for the escape latch (an exception
to the exception to throwing exceptions) which is back again to allowed
to fail.
I don't think so. For example, allowing the implementation to throw
shared pre-allocated instance of std::bad_alloc might be the solution to
the particular problem with exceptions. The other problems you mentioned
either don't need to be solved or can have different solutions.
Call std::terminate. It's double fault.
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Andrey Semashev
2017-08-17 09:12:23 UTC
Permalink
Post by Thiago Macieira
Post by Andrey Semashev
std::terminate leaves no room for recovery, that's my main problem with
it. And the user's code may be written in a way that makes recovery
possible, if only in majority of cases.
But you shouldn't try and recover. If you can t properly report your failure
condition, then you can't recover from it. Changing the exception type to
std::bad_alloc may cause further problems up the stack.
Well, it's up to the application design requirements whether it should
recover from OOM condition or not. Some server applications are required
to be more resillient than others, so I disagree with the premise that
all you should do in case of OOM is complain and exit.

(Personally, I find this mindset that OOM is fatal and should not be
handled rather disappointing because it really degrades the quality of
software I'm using every day. Having a critical application killed
because of OOM, leaving data in an inconsistent state, is the worst thing.)
Post by Thiago Macieira
If you have a double fault, std::terminate is the only thing you can do.
There is no double fault when user throws an exception (aside from when
stack unwind is in progress). The code is valid, no fault happened, it
should not terminate the application.
Post by Thiago Macieira
Post by Andrey Semashev
Stack overflow I tend to consider as a result of misbehavior as well, as
it is unlikely to be a result of a well behaved program, but I could
probably be convinced otherwise on this particular case. In any case,
stack overflow is a separate problem that is not related to exceptions,
and it may have a different solution. For example, the language could
introduce another exception for this purpose, and the implementation
would be bound to always reserve enough stack space to be able to throw
this exception.
I don't agree. A well-behaved program could very well exhaust stack space
without causing an infinite recursion. You may call it poorly-written to
allocate gobs of memory on the stack, but that's still well-behaved in other
conditions.
Also, the system configuration may have lowered the stack size for some reason.
So your program that is well-behaved somewhere isn't elsewhere.
If the application allocates unbounded buffers on the stack, this is
definitely a bug. If the application allocates a bounded amount of stack
that is larger than the typical stack size on the intended target
platforms, that is still a bug in my view. If an application is run on a
system configured to provide less stack than the application requires,
that is likely a configuration error. You may argue that the application
may be required to work in these conditions also, and in that case it
would have to be written with that new limit in mind or maybe detect it
in runtime. The major difference with OOM here is that you have a
certain guaranteed limit before the overflow happens or at least you can
say your application supports. While with OOM there is no way to predict
when it happens.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Matthew Woehlke
2017-08-17 14:14:59 UTC
Permalink
Post by Andrey Semashev
Post by Thiago Macieira
If you have a double fault, std::terminate is the only thing you can do.
There is no double fault when user throws an exception (aside from when
stack unwind is in progress). The code is valid, no fault happened, it
should not terminate the application.
I think the assumption here is that an exception is... *exceptional*.
That is, yes, an exception *is* a fault. If this is not the case, you
probably should be using something other than an exception to
communicate failure.

(Besides, exceptions are a rather heavy-handed way to communicate
non-exceptional events. Given that we're apparently talking about
constrained systems, there is a certain irony that you'd be using
exceptions in such a manner...)
--
Matthew
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Nicol Bolas
2017-08-16 16:17:42 UTC
Permalink
Post by Thiago Macieira
Post by Thiago Macieira
My point is that it's extremely difficult to deal with OOM conditions in
a *non*
Post by Thiago Macieira
portable manner by fiddling with your OOM score adjustment, doing
privileged-
Post by Thiago Macieira
user operations like mlock() or changing the overcommit settings, or
even
Post by Thiago Macieira
compiling your own kernel. If you are prepared to do all that, then you
should
Post by Thiago Macieira
be able to deal with the platform-specific way of throwing exceptions
too.
Although I agree with the status quo that you have to deal with
platform-specific stuff to handle OOM gracefully, I disagree with the
"let's just not handle it" response to the problem. And no, calling
std::terminate or deadlocking when one tries to throw an exception is
not a good solution in my view. Throwing std::bad_alloc (reliably)
instead of user's exception would be.
I think the language has to mandate some sort of escape latch for
std::bad_alloc, even if it means special casing this particular
exception in some ways. Although I realize that that would cause ABI
breakage and therefore would not be an easy change.
If you're going to special case this situation, then it *should not* throw
`std::bad_alloc`. That exception is specifically for normal memory
allocation failures. Users need to be able to differentiate between "I
could not allocated what you requested" and "I could not throw an
exception." One of these is a normal allocation failure; the other is a
failure of the exception system to do what it was told.

Why does there need to be a difference? Because being OOM does not mean
that the exception system will *certainly* fail. Similarly, the exception
system can fail *despite* not being OOM. You can have a de-facto overflow
of the exception system by having "too many" in flight, but how many that
is is implementation-dependent.

If you have an exception failure, you can no longer reliably throw
exceptions. If you have an allocation failure, you may well be able to
throw exceptions.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Andrey Semashev
2017-08-16 16:38:51 UTC
Permalink
Post by Andrey Semashev
I think the language has to mandate some sort of escape latch for
std::bad_alloc, even if it means special casing this particular
exception in some ways. Although I realize that that would cause ABI
breakage and therefore would not be an easy change.
If you're going to special case this situation, then it /should not/
throw `std::bad_alloc`. That exception is specifically for normal memory
allocation failures. Users need to be able to differentiate between "I
could not allocated what you requested" and "I could not throw an
exception." One of these is a normal allocation failure; the other is a
failure of the exception system to do what it was told.
Why does there need to be a difference? Because being OOM does not mean
that the exception system will /certainly/ fail. Similarly, the
exception system can fail /despite/ not being OOM. You can have a
de-facto overflow of the exception system by having "too many" in
flight, but how many that is is implementation-dependent.
I'm not opposed to the idea of having a different exception to indicate
errors other than OOM from the language support system (we already have
a few), although so far we didn't have a need for one. Maybe I'm wrong,
but I don't remember reading/hearing compiler vendor complaints about
inability to report errors from the exception handling system, other
than OOM. Anyways, I'm not discussing those other kinds of errors here.

But if the exception handling system fails exactly because of the OOM,
it would be most natural for it to throw std::bad_alloc. It would also
be natural for the user's code to handle the exception in the
std::bad_alloc handler regardless of where exactly memory depletion has
been detected - whether that is the language support library, standard
library or user's code.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Nicol Bolas
2017-08-16 17:24:45 UTC
Permalink
Post by Andrey Semashev
On Wednesday, August 16, 2017 at 11:54:57 AM UTC-4, Andrey Semashev
I think the language has to mandate some sort of escape latch for
std::bad_alloc, even if it means special casing this particular
exception in some ways. Although I realize that that would cause ABI
breakage and therefore would not be an easy change.
If you're going to special case this situation, then it /should not/
throw `std::bad_alloc`. That exception is specifically for normal memory
allocation failures. Users need to be able to differentiate between "I
could not allocated what you requested" and "I could not throw an
exception." One of these is a normal allocation failure; the other is a
failure of the exception system to do what it was told.
Why does there need to be a difference? Because being OOM does not mean
that the exception system will /certainly/ fail. Similarly, the
exception system can fail /despite/ not being OOM. You can have a
de-facto overflow of the exception system by having "too many" in
flight, but how many that is is implementation-dependent.
I'm not opposed to the idea of having a different exception to indicate
errors other than OOM from the language support system (we already have
a few), although so far we didn't have a need for one. Maybe I'm wrong,
but I don't remember reading/hearing compiler vendor complaints about
inability to report errors from the exception handling system, other
than OOM. Anyways, I'm not discussing those other kinds of errors here.
But if the exception handling system fails exactly because of the OOM,
it would be most natural for it to throw std::bad_alloc. It would also
be natural for the user's code to handle the exception in the
std::bad_alloc handler regardless of where exactly memory depletion has
been detected - whether that is the language support library, standard
library or user's code.
Here's the thing. In every other place you catch `bad_alloc`, you have at
least the theoretical power to resolve the problem by deallocating stuff.
`bad_alloc` is thrown by dynamic allocations.

If you catch the "I can't throw an exception" exception, you can deallocate
every byte of dynamically allocated memory you use, but that *will not*
guarantee that you can still get an exception thrown. This is because
exceptions don't *have* to come from the same pool as regular dynamic
allocations.

The cause of the problem is different, what you have to do to resolve it
may be different, and therefore it should be a different type.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Andrey Semashev
2017-08-16 17:36:46 UTC
Permalink
Post by Andrey Semashev
But if the exception handling system fails exactly because of the OOM,
it would be most natural for it to throw std::bad_alloc. It would also
be natural for the user's code to handle the exception in the
std::bad_alloc handler regardless of where exactly memory depletion has
been detected - whether that is the language support library, standard
library or user's code.
Here's the thing. In every other place you catch `bad_alloc`, you have
at least the theoretical power to resolve the problem by deallocating
stuff. `bad_alloc` is thrown by dynamic allocations.
If you catch the "I can't throw an exception" exception, you can
deallocate every byte of dynamically allocated memory you use, but that
/will not/ guarantee that you can still get an exception thrown. This is
because exceptions don't /have/ to come from the same pool as regular
dynamic allocations.
The cause of the problem is different, what you have to do to resolve it
may be different, and therefore it should be a different type.
Ok, fair enough. Let me put it another way then. If the implementation
uses conventional heap for throwing exceptions (i.e. in order to resolve
the problem the user can release heap memory) then it should throw
std::bad_alloc. If it uses another kind of storage then it should throw
another type of exception. The standard would describe both
std::bad_alloc and this new exception and say that std::bad_alloc is
thrown when conventional heap is exhausted and that other exception when
other (implementation-defined) kinds of resorces are exhausted. Both
exceptions will have to be reliably throwable.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Michael Kilburn
2017-08-16 22:46:37 UTC
Permalink
Post by Nicol Bolas
Post by Thiago Macieira
Post by Thiago Macieira
My point is that it's extremely difficult to deal with OOM conditions
in a *non*
Post by Thiago Macieira
portable manner by fiddling with your OOM score adjustment, doing
privileged-
Post by Thiago Macieira
user operations like mlock() or changing the overcommit settings, or
even
Post by Thiago Macieira
compiling your own kernel. If you are prepared to do all that, then you
should
Post by Thiago Macieira
be able to deal with the platform-specific way of throwing exceptions
too.
Although I agree with the status quo that you have to deal with
platform-specific stuff to handle OOM gracefully, I disagree with the
"let's just not handle it" response to the problem. And no, calling
std::terminate or deadlocking when one tries to throw an exception is
not a good solution in my view. Throwing std::bad_alloc (reliably)
instead of user's exception would be.
I think the language has to mandate some sort of escape latch for
std::bad_alloc, even if it means special casing this particular
exception in some ways. Although I realize that that would cause ABI
breakage and therefore would not be an easy change.
If you're going to special case this situation, then it *should not*
throw `std::bad_alloc`. That exception is specifically for normal memory
allocation failures. Users need to be able to differentiate between "I
could not allocated what you requested" and "I could not throw an
exception." One of these is a normal allocation failure; the other is a
failure of the exception system to do what it was told.
Why does there need to be a difference? Because being OOM does not mean
that the exception system will *certainly* fail. Similarly, the exception
system can fail *despite* not being OOM. You can have a de-facto overflow
of the exception system by having "too many" in flight, but how many that
is is implementation-dependent.
I agree with everything until this point with one note -- as a user I don't
care if std::bad_alloc is thrown in both cases -- all I need is a sensible
'backout' mechanism that would allow my application to survive. I don't
really need to distinguish between these two situations, but it might be a
bonus.


If you have an exception failure, you can no longer reliably throw
Post by Nicol Bolas
exceptions.
This is correct only on assumption that throwing std::bad_alloc (or
std::cant_throw) consumes some limited resource. This can be avoided. Here
is a very trivialized example:

// takes pointer to constructed exception object
// if NULL -- this means we are processing std::bad_alloc
void locate_catch_clause_and_unwind_stack(void* exception_ptr);

void throw_impl(exception_type* p)
{
void* xp = alloc_from_exception_heap(p->size); // return NULL if
we ran out of magic
locate_catch_clause_and_unwind_stack(xp);
}
Post by Nicol Bolas
If you have an allocation failure, you may well be able to throw
exceptions.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Thiago Macieira
2017-08-16 23:00:46 UTC
Permalink
Post by Michael Kilburn
I agree with everything until this point with one note -- as a user I don't
care if std::bad_alloc is thrown in both cases -- all I need is a sensible
'backout' mechanism that would allow my application to survive. I don't
really need to distinguish between these two situations, but it might be a
bonus.
Explain to me what you're going to do if you can't free any more memory. That
is, what happens if the OOM situation is caused by another program on the same
machine.
Post by Michael Kilburn
Post by Nicol Bolas
If you have an exception failure, you can no longer reliably throw
exceptions.
This is correct only on assumption that throwing std::bad_alloc (or
std::cant_throw) consumes some limited resource. This can be avoided. Here
// takes pointer to constructed exception object
// if NULL -- this means we are processing std::bad_alloc
void locate_catch_clause_and_unwind_stack(void* exception_ptr);
void throw_impl(exception_type* p)
{
void* xp = alloc_from_exception_heap(p->size); // return NULL if
we ran out of magic
locate_catch_clause_and_unwind_stack(xp);
}
You're assuming that "locate_catch_clause_and_unwind_stack" can operate
without allocating memory too. The act of unwinding may require resources too.
That means either dynamic (which can fail) or static (which we can exhaust).
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Michael Kilburn
2017-08-16 23:16:32 UTC
Permalink
Post by Michael Kilburn
Post by Michael Kilburn
I agree with everything until this point with one note -- as a user I
don't
Post by Michael Kilburn
care if std::bad_alloc is thrown in both cases -- all I need is a
sensible
Post by Michael Kilburn
'backout' mechanism that would allow my application to survive. I don't
really need to distinguish between these two situations, but it might be
a
Post by Michael Kilburn
bonus.
Explain to me what you're going to do if you can't free any more memory. That
is, what happens if the OOM situation is caused by another program on the same
machine.
It depends on application. My favorite example for this situation is a
server that accepts connections (creates a thread for each one), reads data
(user commands), executes them and in case of exception -- everything in
given thread unwinds, connection gets closed and server survives (along
with other connections).
Post by Michael Kilburn
Post by Michael Kilburn
If you have an exception failure, you can no longer reliably throw
Post by Ville Voutilainen
exceptions.
This is correct only on assumption that throwing std::bad_alloc (or
std::cant_throw) consumes some limited resource. This can be avoided.
Here
Post by Michael Kilburn
// takes pointer to constructed exception object
// if NULL -- this means we are processing std::bad_alloc
void locate_catch_clause_and_unwind_stack(void* exception_ptr);
void throw_impl(exception_type* p)
{
void* xp = alloc_from_exception_heap(p->size); // return NULL
if
Post by Michael Kilburn
we ran out of magic
locate_catch_clause_and_unwind_stack(xp);
}
You're assuming that "locate_catch_clause_and_unwind_stack" can operate
without allocating memory too. The act of unwinding may require resources too.
That means either dynamic (which can fail) or static (which we can exhaust).
I am pretty sure current implementations don't allocate additional memory
besides extra stack space -- but this problem exists in C too and crash on
stack overflow is an long accepted behaviour.
Post by Michael Kilburn
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Thiago Macieira
2017-08-16 23:39:30 UTC
Permalink
Post by Michael Kilburn
Post by Thiago Macieira
Explain to me what you're going to do if you can't free any more memory. That
is, what happens if the OOM situation is caused by another program on the same
machine.
It depends on application. My favorite example for this situation is a
server that accepts connections (creates a thread for each one), reads data
(user commands), executes them and in case of exception -- everything in
given thread unwinds, connection gets closed and server survives (along
with other connections).
Ok, suppose the connection is active and you're about to send a packet. In the
calculation for that packet, some operation wants to throw and fails. That
means you're going to get a stack unwinding.

At what point do you catch it? What memory will you free? If you're going to
close the socket, are you going to try and send a clean shutdown? That may
need to allocate memory again.

Do you have any other recourse besides closing the socket uncleanly and
exiting the thread? And how much memory will that free?
Post by Michael Kilburn
Post by Thiago Macieira
Post by Michael Kilburn
// takes pointer to constructed exception object
// if NULL -- this means we are processing std::bad_alloc
void locate_catch_clause_and_unwind_stack(void* exception_ptr);
void throw_impl(exception_type* p)
{
void* xp = alloc_from_exception_heap(p->size); // return NULL
if
Post by Michael Kilburn
we ran out of magic
locate_catch_clause_and_unwind_stack(xp);
}
You're assuming that "locate_catch_clause_and_unwind_stack" can operate
without allocating memory too. The act of unwinding may require resources too.
That means either dynamic (which can fail) or static (which we can exhaust).
I am pretty sure current implementations don't allocate additional memory
besides extra stack space -- but this problem exists in C too and crash on
stack overflow is an long accepted behaviour.
I've never understood how you can allocate stack space while unwinding the
stack. During the unwind process, the stack pointers need to be adjusted back
to where they used to be. On Linux/x86, once you adjust the SP register, any
data below the pointer becomes garbage and no longer guaranteed to be retained
(or more than 128 bytes below it, or some other limit).

That means any resources the unwinder may need to preserve during the
unwinding need to be somewhere else other than the stack.
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Michael Kilburn
2017-08-16 23:55:07 UTC
Permalink
Post by Nicol Bolas
Post by Michael Kilburn
Post by Thiago Macieira
Explain to me what you're going to do if you can't free any more
memory.
Post by Michael Kilburn
Post by Thiago Macieira
That
is, what happens if the OOM situation is caused by another program on
the
Post by Michael Kilburn
Post by Thiago Macieira
same
machine.
It depends on application. My favorite example for this situation is a
server that accepts connections (creates a thread for each one), reads
data
Post by Michael Kilburn
(user commands), executes them and in case of exception -- everything in
given thread unwinds, connection gets closed and server survives (along
with other connections).
Ok, suppose the connection is active and you're about to send a packet. In the
calculation for that packet, some operation wants to throw and fails. That
means you're going to get a stack unwinding.
At what point do you catch it?
I'll catch it at the root of thread stack, send note to client (smth like
"error: std::bad_alloc"), close connection and exit (which in turn kill the
thread and deallocate it's stack)
Post by Nicol Bolas
What memory will you free?
I will free all memory allocated in operations generated by this connection.
Post by Nicol Bolas
If you're going to close the socket, are you going to try and send a clean
shutdown?
On my side error string will be preallocated (or I will fallback to static
string). On OS side `send()` will fail and I close socket in a way that
will signal user that smth went wrong (if possible).
Post by Nicol Bolas
Do you have any other recourse besides closing the socket uncleanly and
exiting the thread? And how much memory will that free?
It will free exactly as much memory as given thread allocated. Server will
stay up and all it's other users will continue whatever they are doing. And
if OS suddenly discover more memory (some other process exited) -- it will
be transparently utilized when serving subsequent requests.
Post by Nicol Bolas
Post by Michael Kilburn
Post by Thiago Macieira
Post by Michael Kilburn
// takes pointer to constructed exception object
// if NULL -- this means we are processing std::bad_alloc
void locate_catch_clause_and_unwind_stack(void* exception_ptr);
void throw_impl(exception_type* p)
{
void* xp = alloc_from_exception_heap(p->size); // return
NULL
Post by Michael Kilburn
Post by Thiago Macieira
if
Post by Michael Kilburn
we ran out of magic
locate_catch_clause_and_unwind_stack(xp);
}
You're assuming that "locate_catch_clause_and_unwind_stack" can
operate
Post by Michael Kilburn
Post by Thiago Macieira
without allocating memory too. The act of unwinding may require
resources
Post by Michael Kilburn
Post by Thiago Macieira
too.
That means either dynamic (which can fail) or static (which we can exhaust).
I am pretty sure current implementations don't allocate additional
memory
Post by Michael Kilburn
besides extra stack space -- but this problem exists in C too and crash
on
Post by Michael Kilburn
stack overflow is an long accepted behaviour.
I've never understood how you can allocate stack space while unwinding the
stack. During the unwind process, the stack pointers need to be adjusted back
to where they used to be. On Linux/x86, once you adjust the SP register, any
data below the pointer becomes garbage and no longer guaranteed to be retained
(or more than 128 bytes below it, or some other limit).
As far as I know MSVC (during unwinding) goes up the stack and calls
destructors using current "remainder" of stack. Once unwinding is done, it
executes catch() clause in similar way and adjusts stack related registers
(this "freeing" potentially large portion of stack).

Not sure about GCC, but I imagine it does something similar.
Post by Nicol Bolas
That means any resources the unwinder may need to preserve during the
unwinding need to be somewhere else other than the stack.
This is assuming unwinder needs any resources (besides more stack). For
example in case of table-driven unwinding you basically end up searching
and traversing a tree stored in static data (loaded from disk as part of
your executable) -- you don't need extra memory for that. In old days MSVC
was walking stack frames and looking for data it knew should be there. Not
sure what it is doing nowadays.
--
Post by Nicol Bolas
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Thiago Macieira
2017-08-17 00:31:27 UTC
Permalink
Post by Michael Kilburn
Post by Nicol Bolas
If you're going to close the socket, are you going to try and send a clean
shutdown?
On my side error string will be preallocated (or I will fallback to static
string). On OS side `send()` will fail and I close socket in a way that
will signal user that smth went wrong (if possible).
If it's a UDP socket, closing it will not send anything to the other side. The
other side will only timeout.

If you preallocate memory for the OOM condition, you may cause the OOM
condition you were trying to avoid.

That reminds me of developing for Symbian, where a 32 MB VRAM system reserved
1 MB for displaying the OOM message. When the camera app was running, it
consumed 30 MB VRAM. That meant everything else needed to work with just 1
MB...
Post by Michael Kilburn
Post by Nicol Bolas
I've never understood how you can allocate stack space while unwinding the
stack. During the unwind process, the stack pointers need to be adjusted back
to where they used to be. On Linux/x86, once you adjust the SP register, any
data below the pointer becomes garbage and no longer guaranteed to be retained
(or more than 128 bytes below it, or some other limit).
As far as I know MSVC (during unwinding) goes up the stack and calls
destructors using current "remainder" of stack. Once unwinding is done, it
executes catch() clause in similar way and adjusts stack related registers
(this "freeing" potentially large portion of stack).
Not sure about GCC, but I imagine it does something similar.
It does not.

The problem is where it can store the information it needs to save while those
destructors are running. It can't be below the stack, since the destructors
may call other functions that consume stack space. Even if they didn't, Unix
signals may be delivered and those also consume stack space.

Since it's not below the stack, it needs to be either in the heap or above the
stack (thread-specific reserved area).
Post by Michael Kilburn
Post by Nicol Bolas
That means any resources the unwinder may need to preserve during the
unwinding need to be somewhere else other than the stack.
This is assuming unwinder needs any resources (besides more stack). For
example in case of table-driven unwinding you basically end up searching
and traversing a tree stored in static data (loaded from disk as part of
your executable) -- you don't need extra memory for that. In old days MSVC
was walking stack frames and looking for data it knew should be there. Not
sure what it is doing nowadays.
Great if it can be implemented without consuming more resources. But that's
not a given: on some ABI, allocating and keeping memory during unwinding may
be necessary. And that's aside from the exception object itself, which is most
definitely not stored in a static table.

If there are ABIs like that and C++ starts requiring them to change, it would
be a huge breakage for existing users. Big enough that I can predict
objections strong enough to the C++ Standard change that would back it out.

So, no, you have to cope with the fact that throwing exceptions may fail, even
for the smallest and/or pre-defined types. And it may be that the systems thus
affected are mainstream.
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Michael Kilburn
2017-08-17 04:47:50 UTC
Permalink
Post by Nicol Bolas
Post by Michael Kilburn
Post by Nicol Bolas
If you're going to close the socket, are you going to try and send a
clean
Post by Michael Kilburn
Post by Nicol Bolas
shutdown?
On my side error string will be preallocated (or I will fallback to
static
Post by Michael Kilburn
string). On OS side `send()` will fail and I close socket in a way that
will signal user that smth went wrong (if possible).
If it's a UDP socket, closing it will not send anything to the other side. The
other side will only timeout.
How your system is designed to behave on a higher level is irrelevant to
the objective -- server that handles OOMs without dying. You may rely on
keep-alive packets to discover that connection was lost or smth else.
Post by Nicol Bolas
If you preallocate memory for the OOM condition, you may cause the OOM
condition you were trying to avoid.
That reminds me of developing for Symbian, where a 32 MB VRAM system reserved
1 MB for displaying the OOM message. When the camera app was running, it
consumed 30 MB VRAM. That meant everything else needed to work with just 1
MB...
It doesn't really matter how much memory you have -- sooner or later 640kb
won't be enough. Some people choose cheaper variant (upgrade hardware,
process watchers, etc), some people choose code that handles OOM, or
combination of both.
Post by Nicol Bolas
Post by Michael Kilburn
I've never understood how you can allocate stack space while unwinding
the
Post by Michael Kilburn
Post by Nicol Bolas
stack. During the unwind process, the stack pointers need to be
adjusted
Post by Michael Kilburn
Post by Nicol Bolas
back
to where they used to be. On Linux/x86, once you adjust the SP
register,
Post by Michael Kilburn
Post by Nicol Bolas
any
data below the pointer becomes garbage and no longer guaranteed to be retained
(or more than 128 bytes below it, or some other limit).
As far as I know MSVC (during unwinding) goes up the stack and calls
destructors using current "remainder" of stack. Once unwinding is done,
it
Post by Michael Kilburn
executes catch() clause in similar way and adjusts stack related
registers
Post by Michael Kilburn
(this "freeing" potentially large portion of stack).
Not sure about GCC, but I imagine it does something similar.
It does not.
The problem is where it can store the information it needs to save while those
destructors are running. It can't be below the stack, since the destructors
may call other functions that consume stack space. Even if they didn't, Unix
signals may be delivered and those also consume stack space.
I don't know these details, if I knew -- I would not be asking these
questions here. I always assumed that stack unwinding can't fail if
destructors don't throw (and there is enough stack left).

Which information needs to be saved 'on-the-side' to run destructors with
GCC?
Post by Nicol Bolas
Since it's not below the stack, it needs to be either in the heap or above the
stack (thread-specific reserved area).
Post by Michael Kilburn
Post by Nicol Bolas
That means any resources the unwinder may need to preserve during the
unwinding need to be somewhere else other than the stack.
This is assuming unwinder needs any resources (besides more stack). For
example in case of table-driven unwinding you basically end up searching
and traversing a tree stored in static data (loaded from disk as part of
your executable) -- you don't need extra memory for that. In old days
MSVC
Post by Michael Kilburn
was walking stack frames and looking for data it knew should be there.
Not
Post by Michael Kilburn
sure what it is doing nowadays.
Great if it can be implemented without consuming more resources. But that's
not a given: on some ABI, allocating and keeping memory during unwinding may
be necessary.
Can you elaborate on this portion a bit? What kind of information?
Post by Nicol Bolas
And that's aside from the exception object itself, which is most
definitely not stored in a static table.
I thought with GCC exception object is constructed on a heap (or in an
emergency buffer) before unwinding machinery is invoked...


If there are ABIs like that and C++ starts requiring them to change, it
Post by Nicol Bolas
would
be a huge breakage for existing users. Big enough that I can predict
objections strong enough to the C++ Standard change that would back it out.
So, no, you have to cope with the fact that throwing exceptions may fail, even
for the smallest and/or pre-defined types. And it may be that the systems thus
affected are mainstream.
It would a shame... I hope it isn't the case.
--
Post by Nicol Bolas
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Thiago Macieira
2017-08-17 05:14:20 UTC
Permalink
Post by Michael Kilburn
Post by Thiago Macieira
The problem is where it can store the information it needs to save while those
destructors are running. It can't be below the stack, since the destructors
may call other functions that consume stack space. Even if they didn't, Unix
signals may be delivered and those also consume stack space.
I don't know these details, if I knew -- I would not be asking these
questions here. I always assumed that stack unwinding can't fail if
destructors don't throw (and there is enough stack left).
Which information needs to be saved 'on-the-side' to run destructors with
GCC?
That's exactly the problem. The standard cannot mandate something that would
be unimplementable, be it for either technical unfeasibility or because the
cost for existing implementations would be too high. What you're asking is not
a problem of the standard, it's a problem of the implementations. And though
there are several developers here who are developers in GCC and Clang, they
may not be paying attention to this thread or be aware of the specific details
of the unwind mechanism.

Without checking the code, I would assume that unwinding can't fail once it's
started. The problem is starting it, since the act of throwing requires using
some resources. At the very least, it needs to store the exception object
you've thrown and any information that can be obtained by the C++ API like
std::uncaught_exceptions(), std::current_exception().

But I wouldn't be surprised if certain unwinders need to allocate a bit of
memory per frame being unwound to calculate something, like in the code that
determines whether a catch's expression matches the object thrown. We simply
can't exclude that possibility in other ABIs, even if the IA-64 C++ portable
ABI's libunwind doesn't need to.

Of course, this isn't including the fact that catching by value an object with
non-noexcept copy constructor could throw too.
Post by Michael Kilburn
Post by Thiago Macieira
Great if it can be implemented without consuming more resources. But that's
not a given: on some ABI, allocating and keeping memory during unwinding may
be necessary.
Can you elaborate on this portion a bit? What kind of information?
See above, I'm speculating. The point is that I cannot rule out that
possibility.

Remember also that we need to deal with ABIs that may have been patched over
and over for the last 25-30 years to deal with C++ and compiler improvements.
Post by Michael Kilburn
Post by Thiago Macieira
And that's aside from the exception object itself, which is most
definitely not stored in a static table.
I thought with GCC exception object is constructed on a heap (or in an
emergency buffer) before unwinding machinery is invoked...
And that's exactly the problem. What happens if the object is too large for
the emergency buffer and the heap allocation fails?
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Michael Kilburn
2017-08-17 06:41:32 UTC
Permalink
Note: we are already discussing some of questions below on different branch.
Post by Thiago Macieira
Post by Michael Kilburn
Post by Thiago Macieira
The problem is where it can store the information it needs to save
while
Post by Michael Kilburn
Post by Thiago Macieira
those
destructors are running. It can't be below the stack, since the destructors
may call other functions that consume stack space. Even if they
didn't,
Post by Michael Kilburn
Post by Thiago Macieira
Unix
signals may be delivered and those also consume stack space.
I don't know these details, if I knew -- I would not be asking these
questions here. I always assumed that stack unwinding can't fail if
destructors don't throw (and there is enough stack left).
Which information needs to be saved 'on-the-side' to run destructors
with
Post by Michael Kilburn
GCC?
That's exactly the problem. The standard cannot mandate something that would
be unimplementable, be it for either technical unfeasibility or because the
cost for existing implementations would be too high. What you're asking is not
a problem of the standard, it's a problem of the implementations.
If standard doesn't mandate any behavior -- you can't write code that uses
it. You'll end up with chain of ifdefs -- one for each implementation.
Post by Thiago Macieira
And though
there are several developers here who are developers in GCC and Clang, they
may not be paying attention to this thread or be aware of the specific details
of the unwind mechanism.
Shame, but I don't blame them -- I think we are about to hit 100 posts
here. Who is going to dig through this?
Post by Thiago Macieira
Without checking the code, I would assume that unwinding can't fail once it's
started. The problem is starting it, since the act of throwing requires using
some resources. At the very least, it needs to store the exception object
you've thrown and any information that can be obtained by the C++ API like
std::uncaught_exceptions(), std::current_exception().
As far as understand MSVC copies exception from stack to heap in
std::current_exception(), GCC doesn't need to. Since this function is
noexcept -- I imagine MSVC performs std::bad_alloc substitution there. At
least I hope...


But I wouldn't be surprised if certain unwinders need to allocate a bit of
Post by Thiago Macieira
memory per frame being unwound to calculate something, like in the code that
determines whether a catch's expression matches the object thrown. We simply
can't exclude that possibility in other ABIs, even if the IA-64 C++ portable
ABI's libunwind doesn't need to.
Of course, this isn't including the fact that catching by value an object with
non-noexcept copy constructor could throw too.
Behavior in this case is defined by standard.
Post by Thiago Macieira
Post by Michael Kilburn
Post by Thiago Macieira
Great if it can be implemented without consuming more resources. But that's
not a given: on some ABI, allocating and keeping memory during
unwinding
Post by Michael Kilburn
Post by Thiago Macieira
may
be necessary.
Can you elaborate on this portion a bit? What kind of information?
See above, I'm speculating. The point is that I cannot rule out that
possibility.
And I am trying to get answers :)
Post by Thiago Macieira
Remember also that we need to deal with ABIs that may have been patched over
and over for the last 25-30 years to deal with C++ and compiler improvements.
Why? Just like with other C++ features -- given compiler can just declare
that it doesn't support it until version X.X.X.
Post by Thiago Macieira
Post by Michael Kilburn
Post by Thiago Macieira
And that's aside from the exception object itself, which is most
definitely not stored in a static table.
I thought with GCC exception object is constructed on a heap (or in an
emergency buffer) before unwinding machinery is invoked...
And that's exactly the problem. What happens if the object is too large for
the emergency buffer and the heap allocation fails?
auto-substitution to no-fail "throw std::bad_alloc" or "throw
std::cant_throw". Assuming it is possible.
Post by Thiago Macieira
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Thiago Macieira
2017-08-17 07:32:13 UTC
Permalink
Post by Michael Kilburn
Post by Thiago Macieira
Remember also that we need to deal with ABIs that may have been patched over
and over for the last 25-30 years to deal with C++ and compiler improvements.
Why? Just like with other C++ features -- given compiler can just declare
that it doesn't support it until version X.X.X.
You're assuming that the compiler can make that change in the first place. That
is not a given, since it could be a major ABI-breaking change. There's more to
this than a theorerical possibility: this change would have large impacts in
support and compatibility of existing code. It may be theoretically possible
but not economically viable.

I don't know if you were around when we did an ABI change the last time (for
GCC 3.3 to 3.4). That made the C++11 std::string change in libstdc++ look like
a minor hiccup in comparison -- I did a system upgrade one day and it was
over.
Post by Michael Kilburn
Post by Thiago Macieira
Post by Michael Kilburn
Post by Thiago Macieira
And that's aside from the exception object itself, which is most
definitely not stored in a static table.
I thought with GCC exception object is constructed on a heap (or in an
emergency buffer) before unwinding machinery is invoked...
And that's exactly the problem. What happens if the object is too large for
the emergency buffer and the heap allocation fails?
auto-substitution to no-fail "throw std::bad_alloc" or "throw
std::cant_throw". Assuming it is possible.
And assuming it's a good idea. I'm not satisfied it is.

Take the following example:

void f()
{
throw LargeObject;
}
void g() noexcept
[
try {
f();
} catch (const LargeObject &o) {
return;
}
}

Is the code above safe?

If the throw in f() does throw LargeObject, then g() will catch it and consume
the exception. That function is appropriately noexcept.

But if the throw in f() replaces LargeObject with something else -- ANYTHING
else -- then the catch block in g() won't catch it. Since the g() function is
marked noexcept, the unwinder runtime will call std::terminate().

Now, this is no different than guaranteeing a call to std::terminate() at the
throw point, since you end up there anyway. But the type replacement does
allow unwinding in other code conditions.

Still, we haven't ruled out the unwinder failing in the first place, unrelated
to the type in question. So long as that is a possibility, there will be the
need for an escape hatch that isn't an exception.
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Michael Kilburn
2017-08-17 07:43:10 UTC
Permalink
Post by Thiago Macieira
Post by Michael Kilburn
Post by Thiago Macieira
Remember also that we need to deal with ABIs that may have been
patched
Post by Michael Kilburn
Post by Thiago Macieira
over
and over for the last 25-30 years to deal with C++ and compiler improvements.
Why? Just like with other C++ features -- given compiler can just
declare
Post by Michael Kilburn
that it doesn't support it until version X.X.X.
You're assuming that the compiler can make that change in the first place. That
is not a given, since it could be a major ABI-breaking change. There's more to
this than a theorerical possibility: this change would have large impacts in
support and compatibility of existing code. It may be theoretically possible
but not economically viable.
Sigh... Yes, I understand.


I don't know if you were around when we did an ABI change the last time
Post by Thiago Macieira
(for
GCC 3.3 to 3.4). That made the C++11 std::string change in libstdc++ look like
a minor hiccup in comparison -- I did a system upgrade one day and it was
over.
I was always complaining about COW in libstd++'s implementation of
std::string -- turns every single s[i] into potentially throwing :-D
Post by Thiago Macieira
Post by Michael Kilburn
Post by Thiago Macieira
Post by Michael Kilburn
Post by Thiago Macieira
And that's aside from the exception object itself, which is most
definitely not stored in a static table.
I thought with GCC exception object is constructed on a heap (or in
an
Post by Michael Kilburn
Post by Thiago Macieira
Post by Michael Kilburn
emergency buffer) before unwinding machinery is invoked...
And that's exactly the problem. What happens if the object is too
large
Post by Michael Kilburn
Post by Thiago Macieira
for
the emergency buffer and the heap allocation fails?
auto-substitution to no-fail "throw std::bad_alloc" or "throw
std::cant_throw". Assuming it is possible.
And assuming it's a good idea. I'm not satisfied it is.
void f()
{
throw LargeObject;
}
void g() noexcept
[
try {
f();
} catch (const LargeObject &o) {
return;
}
}
Is the code above safe?
I hate to nitpick, but if LargeObject is a variable and it's cctor throws
-- it is broken in any case. It is also broken if you write it like this:
throw LargeObject(...);

and it's ctor can throw something (different from LargeObject).

Assuming that change we discussed is implemented throw becomes similar to
new -- you expect it to do one thing, but it also can throw std::bad_alloc.
Post by Thiago Macieira
If the throw in f() does throw LargeObject, then g() will catch it and consume
the exception. That function is appropriately noexcept.
But if the throw in f() replaces LargeObject with something else -- ANYTHING
else -- then the catch block in g() won't catch it. Since the g() function is
marked noexcept, the unwinder runtime will call std::terminate().
Now, this is no different than guaranteeing a call to std::terminate() at the
throw point, since you end up there anyway. But the type replacement does
allow unwinding in other code conditions.
Still, we haven't ruled out the unwinder failing in the first place, unrelated
to the type in question. So long as that is a possibility, there will be the
need for an escape hatch that isn't an exception.
Well, we already have one -- terminate handler. But it is very limited.
Post by Thiago Macieira
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Matthew Woehlke
2017-08-17 14:23:13 UTC
Permalink
Post by Michael Kilburn
Post by Thiago Macieira
Explain to me what you're going to do if you can't free any more
memory. That is, what happens if the OOM situation is caused by
another program on the same machine.
It depends on application. My favorite example for this situation is a
server that accepts connections (creates a thread for each one), reads data
(user commands), executes them and in case of exception -- everything in
given thread unwinds, connection gets closed and server survives (along
with other connections).
Better servers use a separate *process* per connection. This sort of
thing is one reason why, but there are many. (Address space isolation is
a pretty big one.)

Granted, even in such case, it might still be nice to shut down the
connection "gracefully".
--
Matthew
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Michael Kilburn
2017-08-16 22:25:19 UTC
Permalink
Post by Michael Kilburn
On Wednesday, August 16, 2017 at 1:37:24 AM UTC-5, Thiago Macieira
Post by Thiago Macieira
Post by Michael Kilburn
Leaving this case unspecified prevents me from writing
portable
Post by Thiago Macieira
Post by Michael Kilburn
code that handles OOM.
You can't portably handle OOM anyway.
I can limit my memory allocations (by intercepting malloc), I can run
32bit
app on 64 bit machine with 16GB of memory, and etc.
None of which guard you against another process consuming 15.99 GB of RAM.
Post by Thiago Macieira
On a modern, multitasking operating system with virtual memory, OOM
is
Post by Thiago Macieira
signalled by killing some processes. You don't get notified by it.
Your
Post by Thiago Macieira
memory
allocations succeed, but you may get SIGBUS when you try to access it.
Yes, everyone knows about OOM-killer. Linux can be configured to
overcommit
-- in this case apps aren't killed.
Did you mean "not overcommit"? Because if you do configure Linux to overcommit,
then brk() and anonymous mmap may succeed, but then fail to deliver when the
pages are faulted. If that's the case, you get a SIGBUS.
But even in non-overcommit configuration, I'm pretty sure you can cause the
kernel to commit to more, such as by mapping files, creating files on a tmpfs or
using memfd.
My point is that it's extremely difficult to deal with OOM conditions in a *non*
portable manner by fiddling with your OOM score adjustment, doing privileged-
user operations like mlock() or changing the overcommit settings, or even
compiling your own kernel. If you are prepared to do all that, then you should
be able to deal with the platform-specific way of throwing exceptions too.
Regardless of these points -- there is a large class of systems where OS
won't kill your process because there are no memory left. Process gets NULL
from related malloc and it is up to the process to handle it. I want to be
able to handle it in some other way than calling std::terminate().

Same for a situation where I (user) explicitly (for whatever reason) want
to limit my process to lets say 1Mb of footprint. I want this process to
behave when it hit this limit, not crash. And I want that behavior to be
robust.
Post by Michael Kilburn
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Thiago Macieira
2017-08-16 23:02:40 UTC
Permalink
Post by Michael Kilburn
Regardless of these points -- there is a large class of systems where OS
won't kill your process because there are no memory left. Process gets NULL
from related malloc and it is up to the process to handle it. I want to be
able to handle it in some other way than calling std::terminate().
Same for a situation where I (user) explicitly (for whatever reason) want
to limit my process to lets say 1Mb of footprint. I want this process to
behave when it hit this limit, not crash. And I want that behavior to be
robust.
Then you need to talk to those system vendors and ask them to provide such
robustness.

You were asking for portability. You can't portably handle OOM, despite what
the C++ standard may say.
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Michael Kilburn
2017-08-16 23:12:17 UTC
Permalink
Post by Thiago Macieira
Post by Michael Kilburn
Regardless of these points -- there is a large class of systems where OS
won't kill your process because there are no memory left. Process gets
NULL
Post by Michael Kilburn
from related malloc and it is up to the process to handle it. I want to
be
Post by Michael Kilburn
able to handle it in some other way than calling std::terminate().
Same for a situation where I (user) explicitly (for whatever reason)
want
Post by Michael Kilburn
to limit my process to lets say 1Mb of footprint. I want this process to
behave when it hit this limit, not crash. And I want that behavior to be
robust.
Then you need to talk to those system vendors and ask them to provide such
robustness.
I prefer to get this guarantee from C++ itself -- it will give me
portability. Especially, considering that I can do it in C and (so far)
don't see why C++ can't do the same.


You were asking for portability. You can't portably handle OOM, despite
Post by Thiago Macieira
what
the C++ standard may say.
If given system doesn't comply with requirements outlined in C++ standard
-- I seriously don't care about them. I write C++ code and I expect it to
be executed only on compliant systems.
Post by Thiago Macieira
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Thiago Macieira
2017-08-16 23:15:21 UTC
Permalink
Post by Michael Kilburn
If given system doesn't comply with requirements outlined in C++ standard
-- I seriously don't care about them. I write C++ code and I expect it to
be executed only on compliant systems.
You don't have to care. That's your choice.

Just note you'll be excluding Linux, FreeBSD, macOS and Windows.
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Michael Kilburn
2017-08-16 23:18:40 UTC
Permalink
Post by Nicol Bolas
Post by Michael Kilburn
If given system doesn't comply with requirements outlined in C++
standard
Post by Michael Kilburn
-- I seriously don't care about them. I write C++ code and I expect it
to
Post by Michael Kilburn
be executed only on compliant systems.
You don't have to care. That's your choice.
Just note you'll be excluding Linux, FreeBSD, macOS and Windows.
I don't understand how this remark contributes anything to this discussion.
Also, I am pretty sure all these systems are considered compliant with
current C++ requirements.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Thiago Macieira
2017-08-16 23:31:09 UTC
Permalink
Post by Michael Kilburn
Post by Nicol Bolas
Post by Michael Kilburn
If given system doesn't comply with requirements outlined in C++
standard
Post by Michael Kilburn
-- I seriously don't care about them. I write C++ code and I expect it
to
Post by Michael Kilburn
be executed only on compliant systems.
You don't have to care. That's your choice.
Just note you'll be excluding Linux, FreeBSD, macOS and Windows.
I don't understand how this remark contributes anything to this discussion.
Also, I am pretty sure all these systems are considered compliant with
current C++ requirements.
Except for the part you quoted elsewhere in this thread: that if new succeeds,
you can access those bytes without problem. That's not the case: new may
succeed and you may still SIGBUS when accessing those bytes.
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Michael Kilburn
2017-08-16 23:42:47 UTC
Permalink
Post by Michael Kilburn
On Wednesday, August 16, 2017 at 6:15:38 PM UTC-5, Thiago Macieira
Post by Nicol Bolas
Post by Michael Kilburn
If given system doesn't comply with requirements outlined in C++
standard
Post by Michael Kilburn
-- I seriously don't care about them. I write C++ code and I expect
it
Post by Nicol Bolas
to
Post by Michael Kilburn
be executed only on compliant systems.
You don't have to care. That's your choice.
Just note you'll be excluding Linux, FreeBSD, macOS and Windows.
I don't understand how this remark contributes anything to this
discussion.
Also, I am pretty sure all these systems are considered compliant with
current C++ requirements.
Except for the part you quoted elsewhere in this thread: that if new succeeds,
you can access those bytes without problem. That's not the case: new may
succeed and you may still SIGBUS when accessing those bytes.
I am pretty sure you are wrong about this one. I am coding for more than 20
years -- never heard of this. I would appreciate if you provide some
information about this particular case.
Post by Michael Kilburn
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Patrice Roy
2017-08-16 23:56:29 UTC
Permalink
Michael : how about writing a paper recommending that implementations
always ensure there's at least enough resources available to throw
std::bad_alloc? You might encounter resistance from embedded systems
developers, but at least it would be discussed by the committee.

Just a thought :)
Post by Michael Kilburn
Post by Michael Kilburn
On Wednesday, August 16, 2017 at 6:15:38 PM UTC-5, Thiago Macieira
Post by Nicol Bolas
Post by Michael Kilburn
If given system doesn't comply with requirements outlined in C++
standard
Post by Michael Kilburn
-- I seriously don't care about them. I write C++ code and I expect
it
Post by Nicol Bolas
to
Post by Michael Kilburn
be executed only on compliant systems.
You don't have to care. That's your choice.
Just note you'll be excluding Linux, FreeBSD, macOS and Windows.
I don't understand how this remark contributes anything to this
discussion.
Also, I am pretty sure all these systems are considered compliant with
current C++ requirements.
Except for the part you quoted elsewhere in this thread: that if new succeeds,
you can access those bytes without problem. That's not the case: new may
succeed and you may still SIGBUS when accessing those bytes.
I am pretty sure you are wrong about this one. I am coding for more than
20 years -- never heard of this. I would appreciate if you provide some
information about this particular case.
Post by Michael Kilburn
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
--
---
You received this message because you are subscribed to the Google Groups
"ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an
Visit this group at https://groups.google.com/a/isocpp.org/group/std-
discussion/.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Michael Kilburn
2017-08-17 00:08:32 UTC
Permalink
Post by Patrice Roy
Michael : how about writing a paper recommending that implementations
always ensure there's at least enough resources available to throw
std::bad_alloc? You might encounter resistance from embedded systems
developers, but at least it would be discussed by the committee.
Just a thought :)
:)

I know there is an intent behind every rule standard puts forth. I'd like
to understand the intent (and reasons) behind 15.1/4 and why it leaves
behavior unspecified. Don't want to raise questions that were probably
answered long ago.

If it turns out to be an oversight (or oversimplification) that might be
'tightened' a bit without breaking too many things -- then yes, I would
really like this to be done.

Not sure about writing my own paper about it and driving related process --
I have no idea how it works. And frankly -- I'd rather be coding than
dealing with bureaucracy usually associated with these processes. :-)
Post by Patrice Roy
Post by Michael Kilburn
Post by Michael Kilburn
On Wednesday, August 16, 2017 at 6:15:38 PM UTC-5, Thiago Macieira
Post by Nicol Bolas
Post by Michael Kilburn
If given system doesn't comply with requirements outlined in C++
standard
Post by Michael Kilburn
-- I seriously don't care about them. I write C++ code and I
expect it
Post by Nicol Bolas
to
Post by Michael Kilburn
be executed only on compliant systems.
You don't have to care. That's your choice.
Just note you'll be excluding Linux, FreeBSD, macOS and Windows.
I don't understand how this remark contributes anything to this
discussion.
Also, I am pretty sure all these systems are considered compliant with
current C++ requirements.
Except for the part you quoted elsewhere in this thread: that if new succeeds,
you can access those bytes without problem. That's not the case: new may
succeed and you may still SIGBUS when accessing those bytes.
I am pretty sure you are wrong about this one. I am coding for more than
20 years -- never heard of this. I would appreciate if you provide some
information about this particular case.
Post by Michael Kilburn
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
--
---
You received this message because you are subscribed to the Google Groups
"ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an
Visit this group at
https://groups.google.com/a/isocpp.org/group/std-discussion/.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Patrice Roy
2017-08-17 01:45:59 UTC
Permalink
See https://isocpp.org/std/submit-a-proposal and consider you've begun step
1 :)

If you think you have a valid point, you can come to an actual meeting to
express it. It's open and costs nothing (apart from such things as food,
lodging and travel costs, which are on you; it's all volunteer work). You
probably won't be able to vote in plenary, but you'll be able to
participate otherwise, express yourself and take part in straw polls.

I think you'll get (quite reasonable) opposition from people working in
memory-constrained environments, and from those who do not use exceptions.
You'll probably want to explore what ensuring there's enough memory to
throw std::bad_alloc at all times means to these groups (in one case, every
byte counts; in the other case, using precious memory for an unused
feature). It's a matter of tradeoffs, or so I suppose. You'll want to make
a convincing argument.

Another avenue to consider is to contact your compiler vendor(s), as for
the moment it's an implementation-specific thing. Maybe that would be
sufficient (compiler options or somesuch might do it), and it would be less
work for you.

Good luck!
Post by Michael Kilburn
Post by Patrice Roy
Michael : how about writing a paper recommending that implementations
always ensure there's at least enough resources available to throw
std::bad_alloc? You might encounter resistance from embedded systems
developers, but at least it would be discussed by the committee.
Just a thought :)
:)
I know there is an intent behind every rule standard puts forth. I'd like
to understand the intent (and reasons) behind 15.1/4 and why it leaves
behavior unspecified. Don't want to raise questions that were probably
answered long ago.
If it turns out to be an oversight (or oversimplification) that might be
'tightened' a bit without breaking too many things -- then yes, I would
really like this to be done.
Not sure about writing my own paper about it and driving related process
-- I have no idea how it works. And frankly -- I'd rather be coding than
dealing with bureaucracy usually associated with these processes. :-)
Post by Patrice Roy
Post by Michael Kilburn
Post by Michael Kilburn
On Wednesday, August 16, 2017 at 6:15:38 PM UTC-5, Thiago Macieira
Post by Nicol Bolas
Post by Michael Kilburn
If given system doesn't comply with requirements outlined in C++
standard
Post by Michael Kilburn
-- I seriously don't care about them. I write C++ code and I
expect it
Post by Nicol Bolas
to
Post by Michael Kilburn
be executed only on compliant systems.
You don't have to care. That's your choice.
Just note you'll be excluding Linux, FreeBSD, macOS and Windows.
I don't understand how this remark contributes anything to this
discussion.
Also, I am pretty sure all these systems are considered compliant
with
current C++ requirements.
Except for the part you quoted elsewhere in this thread: that if new succeeds,
you can access those bytes without problem. That's not the case: new may
succeed and you may still SIGBUS when accessing those bytes.
I am pretty sure you are wrong about this one. I am coding for more than
20 years -- never heard of this. I would appreciate if you provide some
information about this particular case.
Post by Michael Kilburn
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
--
---
You received this message because you are subscribed to the Google
Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send
Visit this group at https://groups.google.com/a/is
ocpp.org/group/std-discussion/.
--
---
You received this message because you are subscribed to the Google Groups
"ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an
Visit this group at https://groups.google.com/a/isocpp.org/group/std-
discussion/.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Michael Kilburn
2017-08-17 05:50:53 UTC
Permalink
Post by Patrice Roy
See https://isocpp.org/std/submit-a-proposal and consider you've begun
step 1 :)
If you think you have a valid point, you can come to an actual meeting to
express it. It's open and costs nothing (apart from such things as food,
lodging and travel costs, which are on you; it's all volunteer work). You
probably won't be able to vote in plenary, but you'll be able to
participate otherwise, express yourself and take part in straw polls.
I think you'll get (quite reasonable) opposition from people working in
memory-constrained environments, and from those who do not use exceptions.
You'll probably want to explore what ensuring there's enough memory to
throw std::bad_alloc at all times means to these groups (in one case, every
byte counts; in the other case, using precious memory for an unused
feature). It's a matter of tradeoffs, or so I suppose. You'll want to make
a convincing argument.
Another avenue to consider is to contact your compiler vendor(s), as for
the moment it's an implementation-specific thing. Maybe that would be
sufficient (compiler options or somesuch might do it), and it would be less
work for you.
Good luck!
Thank you, Patrice. I should consider it, but chances are it is not going
to happen in near future. I clearly don't have full understanding of this
and came here with questions, not proposals. Unfortunately, no one who
decided to be involved seems to know answers.

Ultimately, if this can't be implemented in most popular compilers without
too much blood -- it is bound to fail. And I don't know how it is
implemented, nor can I find anyone who knows.

Having one particular vendor to implement it is better than nothing but
very far from ideal...
Post by Patrice Roy
Post by Michael Kilburn
Post by Patrice Roy
Michael : how about writing a paper recommending that implementations
always ensure there's at least enough resources available to throw
std::bad_alloc? You might encounter resistance from embedded systems
developers, but at least it would be discussed by the committee.
Just a thought :)
:)
I know there is an intent behind every rule standard puts forth. I'd like
to understand the intent (and reasons) behind 15.1/4 and why it leaves
behavior unspecified. Don't want to raise questions that were probably
answered long ago.
If it turns out to be an oversight (or oversimplification) that might be
'tightened' a bit without breaking too many things -- then yes, I would
really like this to be done.
Not sure about writing my own paper about it and driving related process
-- I have no idea how it works. And frankly -- I'd rather be coding than
dealing with bureaucracy usually associated with these processes. :-)
Post by Patrice Roy
Post by Michael Kilburn
Post by Michael Kilburn
On Wednesday, August 16, 2017 at 6:15:38 PM UTC-5, Thiago Macieira
Post by Nicol Bolas
Post by Michael Kilburn
If given system doesn't comply with requirements outlined in C++
standard
Post by Michael Kilburn
-- I seriously don't care about them. I write C++ code and I
expect it
Post by Nicol Bolas
to
Post by Michael Kilburn
be executed only on compliant systems.
You don't have to care. That's your choice.
Just note you'll be excluding Linux, FreeBSD, macOS and Windows.
I don't understand how this remark contributes anything to this
discussion.
Also, I am pretty sure all these systems are considered compliant
with
current C++ requirements.
Except for the part you quoted elsewhere in this thread: that if new succeeds,
you can access those bytes without problem. That's not the case: new may
succeed and you may still SIGBUS when accessing those bytes.
I am pretty sure you are wrong about this one. I am coding for more
than 20 years -- never heard of this. I would appreciate if you provide
some information about this particular case.
Post by Michael Kilburn
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
--
---
You received this message because you are subscribed to the Google
Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send
Visit this group at
https://groups.google.com/a/isocpp.org/group/std-discussion/.
--
---
You received this message because you are subscribed to the Google Groups
"ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an
Visit this group at
https://groups.google.com/a/isocpp.org/group/std-discussion/.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Thiago Macieira
2017-08-17 00:43:58 UTC
Permalink
Post by Michael Kilburn
Post by Thiago Macieira
Except for the part you quoted elsewhere in this thread: that if new succeeds,
you can access those bytes without problem. That's not the case: new may
succeed and you may still SIGBUS when accessing those bytes.
I am pretty sure you are wrong about this one. I am coding for more than 20
years -- never heard of this. I would appreciate if you provide some
information about this particular case.
I'm not.

I just tried allocating thrice 15 GB on my 16 GB RAM machine (that's three
times in the same process, without freeing the previous block). I have 16 GB
swap too, so even the maximum VM usable is 32 GB, not 45.

std::size_t n = 15ULL*1024*1024*1024;
char *ptr = new char[n];
ptr = new char[n];
ptr = new char[n];

strace shows the kernel did "allocate" memory:

mmap(NULL, 16106131456, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1,
0) = 0x7f2e1ed4b000 <0.000005>
mmap(NULL, 16106131456, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1,
0) = 0x7f2a5ed4a000 <0.000007>
mmap(NULL, 16106131456, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1,
0) = 0x7f269ed49000 <0.000004>

That number in angle brackets is the time in seconds that the system call
took. That is, BOTH 15 GB allocations took 7µs or less to run. Do you think
the kernel really allocated the memory?

No, it recorded that those two regions of memory were valid, that's it. The
pages would be faulted in when you accessed them.

And since there's no way to provide 45 GB of memory, some bytes are not
accessible.
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Michael Kilburn
2017-08-17 04:32:15 UTC
Permalink
Post by Thiago Macieira
Post by Michael Kilburn
Post by Thiago Macieira
Except for the part you quoted elsewhere in this thread: that if new succeeds,
you can access those bytes without problem. That's not the case: new
may
Post by Michael Kilburn
Post by Thiago Macieira
succeed and you may still SIGBUS when accessing those bytes.
I am pretty sure you are wrong about this one. I am coding for more than
20
Post by Michael Kilburn
years -- never heard of this. I would appreciate if you provide some
information about this particular case.
I'm not.
I just tried allocating thrice 15 GB on my 16 GB RAM machine (that's three
times in the same process, without freeing the previous block). I have 16 GB
swap too, so even the maximum VM usable is 32 GB, not 45.
std::size_t n = 15ULL*1024*1024*1024;
char *ptr = new char[n];
ptr = new char[n];
ptr = new char[n];
mmap(NULL, 16106131456, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1,
0) = 0x7f2e1ed4b000 <0.000005>
mmap(NULL, 16106131456, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1,
0) = 0x7f2a5ed4a000 <0.000007>
mmap(NULL, 16106131456, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1,
0) = 0x7f269ed49000 <0.000004>
That number in angle brackets is the time in seconds that the system call
took. That is, BOTH 15 GB allocations took 7µs or less to run. Do you
think
the kernel really allocated the memory?
No, it recorded that those two regions of memory were valid, that's it. The
pages would be faulted in when you accessed them.
And since there's no way to provide 45 GB of memory, some bytes are not
accessible.
Ahh... I see. 'Full overcommit'. Some claim it to be bad practice:
https://www.etalabs.net/overcommit.html

I personally don't care, it is just a tool. If you don't like it -- don't
use it. When setting up an environment for my app that is supposed to
handle OOMs I'll make sure it is switched off.

In last Linux environment I worked we never had it on -- because OOM-killer
always chose the most important app to kill, the one that spent last 14
hours chewing through data that needs to be available before next trading
day starts :-)

I don't know if this behavior is allowed by C++ standard. Probably is. If
yes -- then you are correct, you can't build portable C or C++ app that can
handle OOMs, but it is not the reason to deny this possibility to those
systems where OOM-killer isn't used.

Also, this is kinda irrelevant -- my biggest grind with 'crashing throw'
can be summed like this:
- exceptions and return codes are essentially the same thing -- it is a way
to pass error object up the call stack, just implemented differently
- you can always pass this error object using C technique
- why you can't to it using C++ technique?
Post by Thiago Macieira
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Thiago Macieira
2017-08-17 04:58:26 UTC
Permalink
Post by Michael Kilburn
https://www.etalabs.net/overcommit.html
I personally don't care, it is just a tool. If you don't like it -- don't
use it. When setting up an environment for my app that is supposed to
handle OOMs I'll make sure it is switched off.
*How*?

First, there's no portable way of doing that. So your question about how to
portably handle OOM falls short, since as I said you can't do it.

Second, you can't change the kernel VM settings as a regular user. You need to
do that as a privleged user. So your regular app can't do it.

Third, even if you run your application as root, little stops the admin from
shrinking the swap or turning it completely off (swapoff can fail if it can't
free the pages already swapped out to it, but I doubt it takes into
consideration reserved-but-unused space). That shrinks the total VM after it's
been "committed" to a particular process. And running without swap is poor use
of resources, since it prevents the kernel from evicting little used pages in
favour of things that really need RAM.

If you have enough control over the system to overcome the problems above, you
have enough control over the C++ runtime library implementation too.
Post by Michael Kilburn
In last Linux environment I worked we never had it on -- because OOM-killer
always chose the most important app to kill, the one that spent last 14
hours chewing through data that needs to be available before next trading
day starts :-)
You can non-portably change that behaviour by adjusting its OOM score. But,
again, non-portable solution.
Post by Michael Kilburn
I don't know if this behavior is allowed by C++ standard. Probably is. If
yes -- then you are correct, you can't build portable C or C++ app that can
handle OOMs, but it is not the reason to deny this possibility to those
systems where OOM-killer isn't used.
I'm not denying it where it is possible. I'm saying you can't require a
portable solution because it can't be done, or would cost too much.
Post by Michael Kilburn
Also, this is kinda irrelevant -- my biggest grind with 'crashing throw'
- exceptions and return codes are essentially the same thing -- it is a way
to pass error object up the call stack, just implemented differently
Except where they're not. As you've shown, throwing can fail, returning can't.
Post by Michael Kilburn
- you can always pass this error object using C technique
- why you can't to it using C++ technique?
Because it's not the same thing. It's a more complex beast and requires more
machinery.
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Michael Kilburn
2017-08-17 05:06:50 UTC
Permalink
Post by Thiago Macieira
Post by Michael Kilburn
https://www.etalabs.net/overcommit.html
I personally don't care, it is just a tool. If you don't like it --
don't
Post by Michael Kilburn
use it. When setting up an environment for my app that is supposed to
handle OOMs I'll make sure it is switched off.
*How*?
By adding smth like this into supporting documentation:
This application was designed to handle lack out-of-memory situations
on it's own. On Linux switch off over commit, please.

And, depending on actual design this can be made optional or mandatory.
Post by Thiago Macieira
First, there's no portable way of doing that. So your question about how to
portably handle OOM falls short, since as I said you can't do it.
Second, you can't change the kernel VM settings as a regular user. You need to
do that as a privleged user. So your regular app can't do it.
Third, even if you run your application as root, little stops the admin from
shrinking the swap or turning it completely off (swapoff can fail if it can't
free the pages already swapped out to it, but I doubt it takes into
consideration reserved-but-unused space). That shrinks the total VM after it's
been "committed" to a particular process. And running without swap is poor use
of resources, since it prevents the kernel from evicting little used pages in
favour of things that really need RAM.
If you have enough control over the system to overcome the problems above, you
have enough control over the C++ runtime library implementation too.
Post by Michael Kilburn
In last Linux environment I worked we never had it on -- because
OOM-killer
Post by Michael Kilburn
always chose the most important app to kill, the one that spent last 14
hours chewing through data that needs to be available before next
trading
Post by Michael Kilburn
day starts :-)
You can non-portably change that behaviour by adjusting its OOM score. But,
again, non-portable solution.
I wasn't planning to do it in the code.
Post by Thiago Macieira
I don't know if this behavior is allowed by C++ standard. Probably is. If
Post by Michael Kilburn
yes -- then you are correct, you can't build portable C or C++ app that
can
Post by Michael Kilburn
handle OOMs, but it is not the reason to deny this possibility to those
systems where OOM-killer isn't used.
I'm not denying it where it is possible. I'm saying you can't require a
portable solution because it can't be done, or would cost too much.
Post by Michael Kilburn
Also, this is kinda irrelevant -- my biggest grind with 'crashing throw'
- exceptions and return codes are essentially the same thing -- it is a
way
Post by Michael Kilburn
to pass error object up the call stack, just implemented differently
Except where they're not. As you've shown, throwing can fail, returning can't.
I'd like throwing not to fail.
Post by Thiago Macieira
Post by Michael Kilburn
- you can always pass this error object using C technique
- why you can't to it using C++ technique?
Because it's not the same thing. It's a more complex beast and requires more
machinery.
I am looking for better answer. I've been coding complex systems for years
-- complexity (where you can't avoid it) is no longer a goof reason to not
do smth (for me).
Why it can't be made no-fail?
Post by Thiago Macieira
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Thiago Macieira
2017-08-17 06:05:16 UTC
Permalink
Post by Michael Kilburn
Post by Thiago Macieira
*How*?
This application was designed to handle lack out-of-memory situations
on it's own. On Linux switch off over commit, please.
And, depending on actual design this can be made optional or mandatory.
So you can also add it to the documentation

"Please use libstdc++safe instead of libstdc++"
Post by Michael Kilburn
Post by Thiago Macieira
Post by Michael Kilburn
Also, this is kinda irrelevant -- my biggest grind with 'crashing throw'
- exceptions and return codes are essentially the same thing -- it is a
way
Post by Michael Kilburn
to pass error object up the call stack, just implemented differently
Except where they're not. As you've shown, throwing can fail, returning can't.
I'd like throwing not to fail.
That's a physical impossibilty, for an arbitrary exception object of unknown
size.

At best, we can adopt your suggestion: an unthrowable exception could be
converted to an exception of a different type (which your catch handlers won't
usually catch) to indicate the situation.

I still think it's a bad idea because you'll most likely unwind too much and
exit frames that did not expect to exit. Including noexcept frames (which will
cause std::terminate).
Post by Michael Kilburn
I am looking for better answer. I've been coding complex systems for years
-- complexity (where you can't avoid it) is no longer a goof reason to not
do smth (for me).
Why it can't be made no-fail?
That depends on what "no-fail" is. Can it guarantee throwing an arbitrary
exception of any type? No, never. That's easy to see why.

Is it physically possible to write an implementation that can guarantee that
throwing a specific exception type or types will succeed in starting the
unwind, and that unwinding will not by itself fail? Yes, I believe so.

If that runtime implementation can be written, I'd argue that it solves your
problem. You can require your application to use it.

But can we make such requirements to all implementations? Without a survey of
the current implementations, both mainstream and fringe (but not obsolete),
it's difficult to say. It might be possible. But my wild guess here is that such
a survey would find at least one implementation that cannot implement it.
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Michael Kilburn
2017-08-17 06:29:32 UTC
Permalink
Post by Michael Kilburn
Post by Michael Kilburn
Post by Thiago Macieira
*How*?
This application was designed to handle lack out-of-memory
situations
Post by Michael Kilburn
on it's own. On Linux switch off over commit, please.
And, depending on actual design this can be made optional or mandatory.
So you can also add it to the documentation
"Please use libstdc++safe instead of libstdc++"
This might be actually a good idea... I really would like it to be mandated
on a language level (if possible, of course).
Post by Michael Kilburn
Post by Michael Kilburn
Post by Thiago Macieira
Post by Michael Kilburn
Also, this is kinda irrelevant -- my biggest grind with 'crashing
throw'
Post by Michael Kilburn
Post by Thiago Macieira
Post by Michael Kilburn
- exceptions and return codes are essentially the same thing -- it
is a
Post by Michael Kilburn
Post by Thiago Macieira
way
Post by Michael Kilburn
to pass error object up the call stack, just implemented differently
Except where they're not. As you've shown, throwing can fail,
returning
Post by Michael Kilburn
Post by Thiago Macieira
can't.
I'd like throwing not to fail.
That's a physical impossibilty, for an arbitrary exception object of unknown
size.
At best, we can adopt your suggestion: an unthrowable exception could be
converted to an exception of a different type (which your catch handlers won't
usually catch) to indicate the situation.
Yes, this is what I meant. My apologies. If standard says that "throw XXX"
can also throw std::bad_alloc (or std::cant_throw) -- my handlers will be
catching it.


I still think it's a bad idea because you'll most likely unwind too much
Post by Michael Kilburn
and
exit frames that did not expect to exit. Including noexcept frames (which will
cause std::terminate).
Only if you let it slip through... But this situation isn't different from
any other exception escaping when it shouldn't.
Post by Michael Kilburn
I am looking for better answer. I've been coding complex systems for years
Post by Michael Kilburn
-- complexity (where you can't avoid it) is no longer a goof reason to
not
Post by Michael Kilburn
do smth (for me).
Why it can't be made no-fail?
That depends on what "no-fail" is. Can it guarantee throwing an arbitrary
exception of any type? No, never. That's easy to see why.
Is it physically possible to write an implementation that can guarantee that
throwing a specific exception type or types will succeed in starting the
unwind, and that unwinding will not by itself fail? Yes, I believe so.
If that runtime implementation can be written, I'd argue that it solves your
problem. You can require your application to use it.
I think most people here have an impression that I report a problem and
propose a solution. It isn't correct. I am asking questions -- is it a
problem? Why it can't be done differently?

I came to learn, not to fight. I have no solution. I may have some ideas
and I'd like to discuss them with someone who understands subject matter
better than me.

Regarding way of implementing it -- I'd aim for smth like this:
- std::bad_alloc is pre-allocated and same object is used in every "throw
std::bad_alloc"
or
- throwing std::bad_alloc is implemented as __cxx_throw(NULL) -- i.e. smth
that does not consume any additional memory and is guaranteed to succeed.
We literally need only to pass an 'out-of-memory' flag to exception
handling machinery.

- if throw fails to allocate --> throw std::bad_alloc
- ... luckily exception specs are deprecated
Post by Michael Kilburn
But can we make such requirements to all implementations? Without a survey of
the current implementations, both mainstream and fringe (but not obsolete),
it's difficult to say. It might be possible. But my wild guess here is that such
a survey would find at least one implementation that cannot implement it.
Who should I talk to to get this ball rolling? Maybe there is a better
person to discuss this with? Should I try to come up with proposal as
Patrice suggested? (it is hard to come up with proposal without knowing
precisely how stuff you want to change operates right now...)
Post by Michael Kilburn
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Thiago Macieira
2017-08-17 07:40:59 UTC
Permalink
Post by Michael Kilburn
Post by Thiago Macieira
But can we make such requirements to all implementations? Without a survey of
the current implementations, both mainstream and fringe (but not obsolete),
it's difficult to say. It might be possible. But my wild guess here is that such
a survey would find at least one implementation that cannot implement it.
Who should I talk to to get this ball rolling? Maybe there is a better
person to discuss this with? Should I try to come up with proposal as
Patrice suggested? (it is hard to come up with proposal without knowing
precisely how stuff you want to change operates right now...)
My guess is that person that looks back at you when you brush your teeth or
shave in the morning: yourself. (I suppose your dog could also be looking at
you, but the dog won't be of much help. If it's a cat, it might actually make
matters worse if the cat may be plotting the downfall of human civilisation)

I think that the part about changing types if the throw mechanism can't throw
that type can be solved with a simple paper making the proposal. The committee
and compiler writers would see it and discuss the issue.

You should also include in the paper the further problems associated with
heap-free unwinding, beyond the ability to store the exception object itself.
But unless you do the survey I mentioned above, I don't think you can propose
solutions. But you can at least get the ball rolling by getting the vendors to
survey themselves.

I'd also write "stack overflow is out of scope".
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Michael Kilburn
2017-08-17 07:54:01 UTC
Permalink
Post by Thiago Macieira
Post by Michael Kilburn
Post by Thiago Macieira
But can we make such requirements to all implementations? Without a
survey
Post by Michael Kilburn
Post by Thiago Macieira
of
the current implementations, both mainstream and fringe (but not obsolete),
it's difficult to say. It might be possible. But my wild guess here is that such
a survey would find at least one implementation that cannot implement
it.
Post by Michael Kilburn
Who should I talk to to get this ball rolling? Maybe there is a better
person to discuss this with? Should I try to come up with proposal as
Patrice suggested? (it is hard to come up with proposal without knowing
precisely how stuff you want to change operates right now...)
My guess is that person that looks back at you when you brush your teeth or
shave in the morning: yourself. (I suppose your dog could also be looking at
you, but the dog won't be of much help. If it's a cat, it might actually make
matters worse if the cat may be plotting the downfall of human
civilisation)
So many assumptions... That I am shaving... in the mornings... and own a
mirror... or brush my teeth...
I could be the cat you've mentioned and this discussion is the first step
in my elaborate and bullet-proof plot to bring doom to humans.
:-)


I think that the part about changing types if the throw mechanism can't
Post by Thiago Macieira
throw
that type can be solved with a simple paper making the proposal. The committee
and compiler writers would see it and discuss the issue.
You should also include in the paper the further problems associated with
heap-free unwinding, beyond the ability to store the exception object itself.
But unless you do the survey I mentioned above, I don't think you can propose
solutions. But you can at least get the ball rolling by getting the vendors to
survey themselves.
I'd also write "stack overflow is out of scope".
Thank you. I'll see what I can come up with.
Post by Thiago Macieira
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Michael Kilburn
2017-08-16 22:28:46 UTC
Permalink
Post by Thiago Macieira
Post by Michael Kilburn
Leaving this case unspecified prevents me from writing portable
code that handles OOM.
You can't portably handle OOM anyway.
This is one of the possible answers, yes. But I would like to understand
why I can do it in C, but can not do it in C++.
Post by Thiago Macieira
On a modern, multitasking operating system with virtual memory, OOM is
signalled by killing some processes. You don't get notified by it. Your memory
allocations succeed, but you may get SIGBUS when you try to access it.
This contradicts guarantee that C++ gives wrt 'new' -- if it succeeds, I
should be able to access memory it allocated.
Post by Thiago Macieira
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Thiago Macieira
2017-08-16 23:04:36 UTC
Permalink
Post by Michael Kilburn
Post by Thiago Macieira
You can't portably handle OOM anyway.
This is one of the possible answers, yes. But I would like to understand
why I can do it in C, but can not do it in C++.
C has the same problems I listed.

It's just that C++ introduces one more: its failure reporting mechanism can
itself fail.
Post by Michael Kilburn
Post by Thiago Macieira
On a modern, multitasking operating system with virtual memory, OOM is
signalled by killing some processes. You don't get notified by it. Your memory
allocations succeed, but you may get SIGBUS when you try to access it.
This contradicts guarantee that C++ gives wrt 'new' -- if it succeeds, I
should be able to access memory it allocated.
It does. Nothing we can do about it.
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Michael Kilburn
2017-08-17 04:36:35 UTC
Permalink
Post by Thiago Macieira
On Wednesday, August 16, 2017 at 1:37:24 AM UTC-5, Thiago Macieira
Post by Thiago Macieira
You can't portably handle OOM anyway.
This is one of the possible answers, yes. But I would like to understand
why I can do it in C, but can not do it in C++.
C has the same problems I listed.
It's just that C++ introduces one more: its failure reporting mechanism can
itself fail.
Yes, you could sum it up this way... Unfortunately this error handling
mechanism is also a default one in C++. Which means it's 'fallibility'
affects (infects?) practically everything.

Also it rather convenient. I want to understand why it can't be implemented
as no-fail and if it is possible -- fix it.
Post by Thiago Macieira
Post by Thiago Macieira
On a modern, multitasking operating system with virtual memory, OOM is
signalled by killing some processes. You don't get notified by it.
Your
Post by Thiago Macieira
memory
allocations succeed, but you may get SIGBUS when you try to access it.
This contradicts guarantee that C++ gives wrt 'new' -- if it succeeds, I
should be able to access memory it allocated.
It does. Nothing we can do about it.
Yes, you proved your point (on assumption that standard allows this
behavior). In general case. :-)
Post by Thiago Macieira
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Andrey Semashev
2017-08-17 09:18:41 UTC
Permalink
Post by Thiago Macieira
Post by Michael Kilburn
Post by Thiago Macieira
On a modern, multitasking operating system with virtual memory, OOM is
signalled by killing some processes. You don't get notified by it. Your memory
allocations succeed, but you may get SIGBUS when you try to access it.
This contradicts guarantee that C++ gives wrt 'new' -- if it succeeds, I
should be able to access memory it allocated.
It does. Nothing we can do about it.
The fact that the Operating System overcommits memory does not mean that
the overcommitted memory is returned by operator new or malloc. The
runtime library can pre-fault the allocated pages before returning,
catch SIGBUS and return NULL or throw std::bad_alloc internally.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Nicol Bolas
2017-08-16 02:12:36 UTC
Permalink
Post by Michael Kilburn
Post by Nicol Bolas
Post by Michael Kilburn
Post by Chris Hallock
Post by Michael Kilburn
I have discovered recently that "throw X(...);" terminates application
if it can't allocate memory for exception being thrown. This happens with
GCC. Standard does not specify what should happen in this situation (see
C++11 15.1.p4).
I am trying to discuss this situation with someone who is closer to
committee than me... Is it a defect? Standard does not specify behavior
here and implementations are free to terminate (and they do) -- problem is
that this makes impossibly to write a C++ program that survives
out-of-memory situation -- even throwing std::bad_alloc can terminate your
app.
This is an implementation-internal memory allocation. What could the
Standard possibly say?
"This allocation failure should result in std::bad_alloc being thrown.
Implementation should never fail to throw std::bad_alloc"
Well that would change nothing, since "should" only specifies a
recommendation ;) The word you're looking for is "must".
Naturally :-)
That being said, I don't think I would consider this a "defect" in the
Post by Nicol Bolas
standard. It seems likely that this is deliberately left as an
implementation detail. Basically, it's a quality-of-implementation issue;
an implementation can provide a guarantee that all
standard-library-generated `std::bad_alloc`s will be able to be generated.
But they aren't *required* to do so.
I believe QoI isn't an issue here -- real issue is that standard does not
allow std::bad_alloc to be thrown in this case (am I wrong?).
I'm really confused by this.

As I understood the issue, your concern was that you attempt to call an
allocation function, which fails to allocate the requested memory. So the
implementation *attempts* to throw `std::bad_alloc`, *as required by the
standard*, but because throwing exceptions may require dynamic allocations,
the implementation may fail to throw that exception.

If this happens, the standard *cannot* "allow std::bad_alloc to be thrown",
because if the implementation could throw it, you *wouldn't have this
problem*.

That's what makes this a QOI issue. The standard does not *require* that
throwing exceptions use dynamic allocations; that's merely a possible
implementation. Therefore, a C++ implementation could make sure that the
executable contains sufficient static memory to throw a single
`std::bad_alloc` exception.

Therefore implementations opt for std::terminate(). If it was allowed --
Post by Michael Kilburn
every exception specification would need to be implicitly extended with
std::bad_alloc -- and I am pretty sure Standard doesn't mention that.
That's Java, not C++. In C++, when we had exception specifications, if
nothing was listed, you throw *anything*.

it certainly may come as a surprise to those who use exception
Post by Michael Kilburn
Post by Nicol Bolas
Post by Michael Kilburn
specification, but at least I will be able to write application that
doesn't mysteriously die when it runs out of memory
Unless it runs out of memory due to a stack allocation. In which case,
you're in undefined behavior land. At least if an exception cannot be
allocated, `std::terminate` will be called. With a stack allocation OOM
error, you won't even get that.
I can control local variables and call depth in my code -- therefore I can
avoid running out of stack. I can't avoid out-of-memory including running
out of these emergency exception buffers, especially considering that now I
can have many threads and retain (and chain) exceptions indefinitely via
std::current_exception.
Nonsense. You have just as much control over your memory allocation
patterns and what exceptions get thrown as you do over local variable size
and call stack depth. If `std::bad_alloc` is thrown, it is *your choice*
how much memory to allocate during the stack unwinding process to reach the
cite where the exception is caught.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Michael Kilburn
2017-08-16 03:08:30 UTC
Permalink
Post by Nicol Bolas
That being said, I don't think I would consider this a "defect" in the
Post by Michael Kilburn
Post by Nicol Bolas
standard. It seems likely that this is deliberately left as an
implementation detail. Basically, it's a quality-of-implementation issue;
an implementation can provide a guarantee that all
standard-library-generated `std::bad_alloc`s will be able to be generated.
But they aren't *required* to do so.
I believe QoI isn't an issue here -- real issue is that standard does not
allow std::bad_alloc to be thrown in this case (am I wrong?).
I'm really confused by this.
As I understood the issue, your concern was that you attempt to call an
allocation function, which fails to allocate the requested memory. So the
implementation *attempts* to throw `std::bad_alloc`, *as required by the
standard*, but because throwing exceptions may require dynamic
allocations, the implementation may fail to throw that exception.
No. My concern is that "throw my_exception();" statement has unspecified
behavior if exception mechanism fails to allocate memory for my_exception
object. Most popular compilers seem to simply terminate application.
Post by Nicol Bolas
If this happens, the standard *cannot* "allow std::bad_alloc to be
thrown", because if the implementation could throw it, you *wouldn't have
this problem*.
That's what makes this a QOI issue. The standard does not *require* that
throwing exceptions use dynamic allocations; that's merely a possible
implementation. Therefore, a C++ implementation could make sure that the
executable contains sufficient static memory to throw a single
`std::bad_alloc` exception.
Therefore implementations opt for std::terminate(). If it was allowed --
Post by Michael Kilburn
every exception specification would need to be implicitly extended with
std::bad_alloc -- and I am pretty sure Standard doesn't mention that.
That's Java, not C++. In C++, when we had exception specifications, if
nothing was listed, you throw *anything*.
I know, but if standard mandate throwing bad_alloc in this case code like
this:

void foo() throw(int) { throw 1; }

needs to be changed to:

void foo() throw(int, std::bad_alloc) { throw 1; }

either manually in every instance or implicitly (by a decree in standard
which would say something like "any function that throws is considered to
have std::bad_alloc added to it's exception specification"). Because such
decree is nowhere to be found in the standard and no one ever bothered
adding std::bad_alloc to function exception specification -- I assume that
no one ever considered throwing std::bad_alloc from "throw my_exception()".
Post by Nicol Bolas
it certainly may come as a surprise to those who use exception
Post by Michael Kilburn
Post by Nicol Bolas
Post by Michael Kilburn
specification, but at least I will be able to write application that
doesn't mysteriously die when it runs out of memory
Unless it runs out of memory due to a stack allocation. In which case,
you're in undefined behavior land. At least if an exception cannot be
allocated, `std::terminate` will be called. With a stack allocation OOM
error, you won't even get that.
I can control local variables and call depth in my code -- therefore I
can avoid running out of stack. I can't avoid out-of-memory including
running out of these emergency exception buffers, especially considering
that now I can have many threads and retain (and chain) exceptions
indefinitely via std::current_exception.
Nonsense. You have just as much control over your memory allocation
patterns and what exceptions get thrown as you do over local variable size
and call stack depth. If `std::bad_alloc` is thrown, it is *your choice*
how much memory to allocate during the stack unwinding process to reach the
cite where the exception is caught.
I think you totally misunderstood my point.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Thiago Macieira
2017-08-16 06:45:30 UTC
Permalink
Post by Michael Kilburn
No. My concern is that "throw my_exception();" statement has unspecified
behavior if exception mechanism fails to allocate memory for my_exception
object. Most popular compilers seem to simply terminate application.
It's not unspecified behaviour. It's very specified: call std::terminate.

As others have said: when the mechanism used to report failures fails itself,
you get a last-ditch bail-out the form of std::terminate.

It's actually the same behaviour as a double-throw: if in the process of
throwing an exception, we ran out of memory, we'd throw std::bad_alloc. But
since an exception is already being thrown, we enter the double-throw
scenario. Result: call std::terminate.
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Michael Kilburn
2017-08-16 06:58:16 UTC
Permalink
Post by Michael Kilburn
Post by Michael Kilburn
No. My concern is that "throw my_exception();" statement has unspecified
behavior if exception mechanism fails to allocate memory for
my_exception
Post by Michael Kilburn
object. Most popular compilers seem to simply terminate application.
It's not unspecified behaviour. It's very specified: call std::terminate.
Can you point me to a spot in a standard that says that?
Post by Michael Kilburn
As others have said: when the mechanism used to report failures fails itself,
you get a last-ditch bail-out the form of std::terminate.
No, you don't. Implementation can certainly do that, but it isn't even
mentioned in standard. As I mentioned in original post -- standard leaves
this to implementation (according to my reading), see C++11 15.1.p4


It's actually the same behaviour as a double-throw: if in the process of
Post by Michael Kilburn
throwing an exception, we ran out of memory, we'd throw std::bad_alloc. But
since an exception is already being thrown, we enter the double-throw
scenario. Result: call std::terminate.
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Ville Voutilainen
2017-08-16 09:03:48 UTC
Permalink
Post by Michael Kilburn
Post by Thiago Macieira
Post by Michael Kilburn
No. My concern is that "throw my_exception();" statement has unspecified
behavior if exception mechanism fails to allocate memory for my_exception
object. Most popular compilers seem to simply terminate application.
It's not unspecified behaviour. It's very specified: call std::terminate.
Can you point me to a spot in a standard that says that?
There is no such spot. Throwing an exception during unwinding calls
terminate, but
failing to allocate memory when an exception is initialized before
it's thrown does not.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
T. C.
2017-08-16 22:03:30 UTC
Permalink
On Wednesday, August 16, 2017 at 5:32:13 PM UTC-4, Nevin ":-)" Liber
It's not clear to me what, if anything, the standard mandates if
allocation fails while copying the exception in the course of throwing it -
I'm pretty sure this would trigger terminate() but I couldn't find the
specifics in the standard. However, it does mandate (21.8.2) that all
standard exception classes have non-throwing copy constructors and
assignment operators, so this is a problem that could only arise with
custom exceptions.
That isn't true. For instance, std::runtime_error usually contains a
string whose copy constructor can throw.
--
That's why std::runtime_error is typically implemented as holding a
reference-counted immutable string so that its copy constructor doesn't
actually copy the string.
I'm still not seeing where that is required.
--
+1-847-691-1404
[exception]/2, which is cited in the email you quoted.

Each standard library class T that derives from class exception shall have
a publicly accessible copy constructor and a publicly accessible copy
assignment operator that do not exit with an exception.
<https://timsong-cpp.github.io/cppwp/exception#2.sentence-1>
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Michael Kilburn
2017-08-16 22:37:09 UTC
Permalink
Post by Ville Voutilainen
There is no such spot. Throwing an exception during unwinding calls
terminate, but
failing to allocate memory when an exception is initialized before
it's thrown does not.
Failing to allocate while constructing an exception is just an ordinary
allocation failure, and would be expected to throw bad_alloc just as it
would in any other circumstances. The fact that it happened inside a
throw expression isn't relevant; execution never got as far as actually
attempting to throw the original exception.
Not that I am against this notion, but where standard says that allocation
in every circumstance must produce std::bad_alloc? On the other hand
15.1.p4 clearly states that allocation mechanism is unspecified.
It's not clear to me what, if anything, the standard mandates if
allocation fails while copying the exception in the course of throwing
it - I'm pretty sure this would trigger terminate() but I couldn't find
the specifics in the standard. However, it does mandate (21.8.2) that
all standard exception classes have non-throwing copy constructors and
assignment operators, so this is a problem that could only arise with
custom exceptions. Writing your exceptions so that copying them can't
throw is generally considered good practice.
The standard also requires (21.6.3.1) that all operations on bad_alloc,
including default construction, can't throw.
throw can fail during allocation, before construction.
So this whole discussion
seems to me to be based on a fundamentally wrong premise. Failing to
allocate while constructing an exception throws bad_alloc, nothing
undefined or ambiguous about that; failing to allocate (or any other
exception-worthy error condition) while throwing bad_alloc can't happen.
Ross Smith
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Ross Smith
2017-08-16 22:59:10 UTC
Permalink
Post by Ville Voutilainen
There is no such spot. Throwing an exception during unwinding calls
terminate, but
failing to allocate memory when an exception is initialized before
it's thrown does not.
Failing to allocate while constructing an exception is just an ordinary
allocation failure, and would be expected to throw bad_alloc just as it
would in any other circumstances. The fact that it happened inside a
throw expression isn't relevant; execution never got as far as actually
attempting to throw the original exception.
Not that I am against this notion, but where standard says that
allocation in every circumstance must produce std::bad_alloc? On the
other hand 15.1.p4 clearly states that allocation mechanism is unspecified.
I'm no sure which part of the standard you're referring to there; I
can't see anything relevant in 15.1/4 in the current draft. Probably my
fault for using section numbers in the first place, we should use the
labels. Anyway, [bad.alloc] (21.6.3.1 in the current draft) seems pretty
clear: "The class bad_alloc defines the type of objects thrown as
exceptions by the implementation to report a failure to allocate storage."
The standard also requires (21.6.3.1) that all operations on bad_alloc,
including default construction, can't throw.
throw can fail during allocation, before construction.
I have no idea what you mean by that. Constructing and throwing
bad_alloc doesn't involve any allocation; constructing some other
exception type can throw, but that's just an ordinary allocation failure
and throws bad_alloc as usual, no special rules required.

(Well, it's not quite true that bad_alloc can't allocate anything in its
constructor: if it does, it has to catch and handle any failure
internally, without throwing. I can imagine an implementation where
bad_alloc's constructor tries to construct a string with a detailed
error message, but if that fails, gives up and just holds a pointer to a
static string with a generic message.)

Ross Smith
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Michael Kilburn
2017-08-16 23:07:24 UTC
Permalink
Post by Patrice Roy
Post by Ville Voutilainen
There is no such spot. Throwing an exception during unwinding
calls
Post by Ville Voutilainen
terminate, but
failing to allocate memory when an exception is initialized
before
Post by Ville Voutilainen
it's thrown does not.
Failing to allocate while constructing an exception is just an
ordinary
allocation failure, and would be expected to throw bad_alloc just as
it
would in any other circumstances. The fact that it happened inside a
throw expression isn't relevant; execution never got as far as
actually
attempting to throw the original exception.
Not that I am against this notion, but where standard says that
allocation in every circumstance must produce std::bad_alloc? On the
other hand 15.1.p4 clearly states that allocation mechanism is
unspecified.
I'm no sure which part of the standard you're referring to there; I
can't see anything relevant in 15.1/4 in the current draft. Probably my
fault for using section numbers in the first place, we should use the
labels.
Quote:
C++11 15.1/4:
The memory for the exception object is allocated in an unspecified way,
except as noted in 3.7.4.1.
Post by Patrice Roy
Anyway, [bad.alloc] (21.6.3.1 in the current draft) seems pretty
clear: "The class bad_alloc defines the type of objects thrown as
exceptions by the implementation to report a failure to allocate storage."
It doesn't say this has to be used for every allocation.
Post by Patrice Roy
The standard also requires (21.6.3.1) that all operations on
bad_alloc,
including default construction, can't throw.
throw can fail during allocation, before construction.
I have no idea what you mean by that. Constructing and throwing
bad_alloc doesn't involve any allocation; constructing some other
exception type can throw, but that's just an ordinary allocation failure
and throws bad_alloc as usual, no special rules required.
For every value to be constructed you need to allocate memory for it first.
Doesn't matter if it is local storage (stack), global or dynamic. In terms
of implementation allocation can mean as little as adding 4 to some "stack
pointer(SP)" register or calling malloc().

For exception to be constructed compiler has to decided where it will be
constructed -- this is allocation I was talking about. And 15.1/4 seems to
deliberately avoid putting any requirements on this part. I imagine to
allow both MSVC (where exception gets constructed on the stack) and GCC
(heap) implementations.
Post by Patrice Roy
(Well, it's not quite true that bad_alloc can't allocate anything in its
constructor: if it does, it has to catch and handle any failure
internally, without throwing. I can imagine an implementation where
bad_alloc's constructor tries to construct a string with a detailed
error message, but if that fails, gives up and just holds a pointer to a
static string with a generic message.)
What happens during exception object construction is irrelevant to original
problem.
Post by Patrice Roy
Ross Smith
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Nicol Bolas
2017-08-17 00:10:32 UTC
Permalink
Post by Ville Voutilainen
There is no such spot. Throwing an exception during unwinding calls
terminate, but
failing to allocate memory when an exception is initialized before
it's thrown does not.
Failing to allocate while constructing an exception is just an ordinary
allocation failure, and would be expected to throw bad_alloc just as it
would in any other circumstances.
I think you're misunderstanding the use case here. This is not failing to
allocate "while constructing an exception". This is failing to allocate the
storage for the exception object itself. This is *not* about what goes on
in a constructor.

When you do `throw expr;`, the first step is the evaluation of `expr` into
a value. Then the compiler allocates storage for the exception object,
which is of the same type as `expr`. The compiler then copies/moves from
the `expr` evaluated value into that storage.

We're talking about the failure of step 2. It has nothing to do with
user-defined code. It's more like allocating additional pages of stack
memory; it's an implementation detail which causes the program to die if it
is ever unable to be accomplished.
The fact that it happened inside a
throw expression isn't relevant; execution never got as far as actually
attempting to throw the original exception.
Creating the exception object is the beginning of the process of throwing
an exception. Everything after the evaluation of the throw expression is
part of exception handling. That's why the copy/move constructor of an
exception isn't allowed to throw, and that's why `throw` statements aren't
allowed to participate in guaranteed elision.
It's not clear to me what, if anything, the standard mandates if
allocation fails while copying the exception in the course of throwing
it - I'm pretty sure this would trigger terminate() but I couldn't find
the specifics in the standard.
If the exception handling mechanism handling an uncaught exception
(18.5.2) directly invokes a function that exits via an exception,
std::terminate is called (18.5.1).

And the example attached specifically calls out the copy constructor of the
exception type:

struct C {
C() { }
C(const C&) {
if (std::uncaught_exceptions()) {
throw 0; // throw during copy to handler’s exception-declaration
object (18.3)
}
}
};

int main() {
try {
throw C(); // calls std::terminate() if construction of the handler’s
// exception-declaration object is not elided (15.8)
} catch(C) { }
}

All that being said... I'm not as sure I'm right as I was when I started
An exception is considered uncaught after completing the initialization
of the exception object

That seems to suggest that, in the above code, the exception isn't uncaught
when the `throw` in `C`'s copy constructor is encountered. Therefore,
`std::uncaught_exceptions()` should be 0.

This looks like a genuine defect in the standard.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
T. C.
2017-08-17 00:33:51 UTC
Permalink
Post by Nicol Bolas
And the example attached specifically calls out the copy constructor of
struct C {
C() { }
C(const C&) {
if (std::uncaught_exceptions()) {
throw 0; // throw during copy to handler’s exception-declaration
object (18.3)
}
}
};
int main() {
try {
throw C(); // calls std::terminate() if construction of the handler’s
// exception-declaration object is not elided (15.8)
} catch(C) { }
}
All that being said... I'm not as sure I'm right as I was when I started
An exception is considered uncaught after completing the initialization
of the exception object
That seems to suggest that, in the above code, the exception isn't
uncaught when the `throw` in `C`'s copy constructor is encountered.
Therefore, `std::uncaught_exceptions()` should be 0.
This looks like a genuine defect in the standard.
The terminate-causing throw in that example isn't in the initialization of
the exception object; it's in the initialization of the by-value parameter
of the catch. The handler isn't active, and the exception isn't caught,
until that initialization is complete.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Nicol Bolas
2017-08-17 00:36:43 UTC
Permalink
Post by T. C.
Post by Nicol Bolas
And the example attached specifically calls out the copy constructor of
struct C {
C() { }
C(const C&) {
if (std::uncaught_exceptions()) {
throw 0; // throw during copy to handler’s exception-declaration
object (18.3)
}
}
};
int main() {
try {
throw C(); // calls std::terminate() if construction of the handler’s
// exception-declaration object is not elided (15.8)
} catch(C) { }
}
All that being said... I'm not as sure I'm right as I was when I started
An exception is considered uncaught after completing the initialization
of the exception object
That seems to suggest that, in the above code, the exception isn't
uncaught when the `throw` in `C`'s copy constructor is encountered.
Therefore, `std::uncaught_exceptions()` should be 0.
This looks like a genuine defect in the standard.
The terminate-causing throw in that example isn't in the initialization of
the exception object; it's in the initialization of the by-value parameter
of the catch. The handler isn't active, and the exception isn't caught,
until that initialization is complete.
Then the comment is wrong. It should be on the catch statement, not the
throw. I double-checked with N4659, and it appears there as I copied it.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
T. C.
2017-08-17 00:40:59 UTC
Permalink
Post by Nicol Bolas
Post by T. C.
Post by Nicol Bolas
And the example attached specifically calls out the copy constructor of
struct C {
C() { }
C(const C&) {
if (std::uncaught_exceptions()) {
throw 0; // throw during copy to handler’s exception-declaration
object (18.3)
}
}
};
int main() {
try {
throw C(); // calls std::terminate() if construction of the handler’s
// exception-declaration object is not elided (15.8)
} catch(C) { }
}
All that being said... I'm not as sure I'm right as I was when I started
An exception is considered uncaught after completing the
initialization of the exception object
That seems to suggest that, in the above code, the exception isn't
uncaught when the `throw` in `C`'s copy constructor is encountered.
Therefore, `std::uncaught_exceptions()` should be 0.
This looks like a genuine defect in the standard.
The terminate-causing throw in that example isn't in the initialization
of the exception object; it's in the initialization of the by-value
parameter of the catch. The handler isn't active, and the exception isn't
caught, until that initialization is complete.
Then the comment is wrong. It should be on the catch statement, not the
throw. I double-checked with N4659, and it appears there as I copied it.
Perhaps the placement could use some improvement (but again, the terminate
happens after the throw and before the catch, so it's not clear having it
on the catch would be better).

Nonetheless, the comment does say "the handler's exception-declaration
object", and there's only one handler and only one exception-declaration in
that example.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Chris Hallock
2017-08-16 05:25:23 UTC
Permalink
Post by Michael Kilburn
Post by Chris Hallock
Post by Michael Kilburn
I have discovered recently that "throw X(...);" terminates application
if it can't allocate memory for exception being thrown. This happens with
GCC. Standard does not specify what should happen in this situation (see
C++11 15.1.p4). [...]
This is an implementation-internal memory allocation. What could the
Standard possibly say?
"This allocation failure should result in std::bad_alloc being thrown.
Implementation should never fail to throw std::bad_alloc"
The Standard can't prohibit the existence of implementation limits nor
define the behavior of programs that exceed those limits. The statement
"Implementation should never fail to throw std::bad_alloc" can't be
absolutely guaranteed no matter how forcefully you say it, because there
are all manner of ways for programs to fail unexpectedly that are not
within the power of compiler authors (or, indeed, anyone) to prevent. For
example, the user could apply a sledgehammer to the computer running your
program.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Michael Kilburn
2017-08-16 05:37:57 UTC
Permalink
Post by Chris Hallock
Post by Michael Kilburn
Post by Chris Hallock
Post by Michael Kilburn
I have discovered recently that "throw X(...);" terminates application
if it can't allocate memory for exception being thrown. This happens with
GCC. Standard does not specify what should happen in this situation (see
C++11 15.1.p4). [...]
This is an implementation-internal memory allocation. What could the
Standard possibly say?
"This allocation failure should result in std::bad_alloc being thrown.
Implementation should never fail to throw std::bad_alloc"
The Standard can't prohibit the existence of implementation limits nor
define the behavior of programs that exceed those limits. The statement
"Implementation should never fail to throw std::bad_alloc" can't be
absolutely guaranteed no matter how forcefully you say it, because there
are all manner of ways for programs to fail unexpectedly that are not
within the power of compiler authors (or, indeed, anyone) to prevent. For
example, the user could apply a sledgehammer to the computer running your
program.
I don't think this is a good argument -- we operate on assumption that
hardware delivers promised guarantees. There are multiple places in the
language where it mandates certain outcomes and disregards "sledgehammer"
factor. In my (limited) understanding there at least one way to implement
"throw std::bad_alloc() should never fail" requirement from compiler
standpoint.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Thiago Macieira
2017-08-16 06:46:21 UTC
Permalink
Post by Michael Kilburn
I don't think this is a good argument -- we operate on assumption that
hardware delivers promised guarantees. There are multiple places in the
language where it mandates certain outcomes and disregards "sledgehammer"
factor. In my (limited) understanding there at least one way to implement
"throw std::bad_alloc() should never fail" requirement from compiler
standpoint.
That wouldn't solve your problem.
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Michael Kilburn
2017-08-16 06:55:21 UTC
Permalink
Post by Michael Kilburn
Post by Michael Kilburn
I don't think this is a good argument -- we operate on assumption that
hardware delivers promised guarantees. There are multiple places in the
language where it mandates certain outcomes and disregards
"sledgehammer"
Post by Michael Kilburn
factor. In my (limited) understanding there at least one way to
implement
Post by Michael Kilburn
"throw std::bad_alloc() should never fail" requirement from compiler
standpoint.
That wouldn't solve your problem.
Why do you think so?
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Thiago Macieira
2017-08-16 15:32:08 UTC
Permalink
Post by Michael Kilburn
Post by Thiago Macieira
Post by Michael Kilburn
"throw std::bad_alloc() should never fail" requirement from compiler
standpoint.
That wouldn't solve your problem.
Why do you think so?
Because you said your problem is that you wanted to throw something else, an
exception class possibly much bigger than std::bad_alloc. So even if the
implementation is required to be able to throw std::bad_alloc, it won't be
required to always be able to throw your type.
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Michael Kilburn
2017-08-16 22:57:07 UTC
Permalink
Post by Thiago Macieira
Post by Michael Kilburn
Post by Thiago Macieira
Post by Michael Kilburn
"throw std::bad_alloc() should never fail" requirement from compiler
standpoint.
That wouldn't solve your problem.
Why do you think so?
Because you said your problem is that you wanted to throw something else, an
exception class possibly much bigger than std::bad_alloc. So even if the
implementation is required to be able to throw std::bad_alloc, it won't be
required to always be able to throw your type.
No, I said my problem is that my application terminates even though it is
100% correct. And that requirement (along with "if throw fails to allocate
memory for exception -- throw bad_alloc" requirement) certainly would solve
my problem . That may make other things too expensive (or simply not
possible to implement) -- then I'd like to understand how and why.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Loading...