C++Talk.NET Forum Index C++Talk.NET
C++ language newsgroups
 
Archives   FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

RAII and resource release functions that can throw
Goto page 1, 2, 3, 4, 5, 6, 7, 8, 9, 10  Next
 
Post new topic   Reply to topic    C++Talk.NET Forum Index -> C++ Language (Moderated)
View previous topic :: View next topic  
Author Message
Scott Meyers
Guest





PostPosted: Sun Feb 20, 2005 1:26 am    Post subject: RAII and resource release functions that can throw Reply with quote



I have an old question that I, of all people, should know the answer to.
But I don't.

C++ uses RAII to avoid leaking resources, and destructors don't throw
because it makes things all icky if they do. No problem, I'm on board.
But then I get questions like this:

What if the destruction of the resource requires some exception handling,
such as logging the exception, translating the exception to app-specific
exception and returns to the caller, etc. For example, for a db
connection, I might want to do: (quite frequently found in similar JDBC
codes in Java world.)

void doSomething()
{
Connection* conn = NULL;
conn* = createConnection();
// ... do something
try {
if (conn != NULL) {
conn.close();
}
delete conn;
conn = NULL;
} catch (SQLException sqle) {
// 1. log the exception in some log file.
// 2. throw some app-specific exception or warning based on SQLException.
}
}

Adding RAII for the DB connection is easy, and taking care of logging in
the dtor is equally easy. But dealing with an exception arising from close
is not easy. The connection-managing object could swallow the exception,
but that doesn't seem right. It could translate the exception into a
different error-reporting strategy (e.g., errno), but that loses the
advantages of using exceptions for error reporting (e.g, they can't be
ignored). We could move the call to close out of the dtor and into a
different function that clients would be expected to call manually, but
that would lose the advantages of RAII.

There are a whole host of things we COULD do about combining RAII with
resource release functions that can throw, but I'm interested in what
people actually DO do. FWIW, I checked Herb's library, but I didn't see
any definitive advice on this topic, and some quick googling around didn't
reveal anything, either, but I'm sure this has been hashed over many times.
Feel free to respond with reference to a publication or a URL.

Yours in woeful uninformedness,

Scott



[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Back to top
Trevor L. Jackson, III
Guest





PostPosted: Sun Feb 20, 2005 4:47 am    Post subject: Re: RAII and resource release functions that can throw Reply with quote



Scott Meyers wrote:

Quote:
I have an old question that I, of all people, should know the answer to.
But I don't.

C++ uses RAII to avoid leaking resources, and destructors don't throw
because it makes things all icky if they do. No problem, I'm on board.
But then I get questions like this:

What if the destruction of the resource requires some exception handling,
such as logging the exception, translating the exception to app-specific
exception and returns to the caller, etc. For example, for a db
connection, I might want to do: (quite frequently found in similar JDBC
codes in Java world.)

void doSomething()
{
Connection* conn = NULL;
conn* = createConnection();
// ... do something
try {
if (conn != NULL) {
conn.close();
}
delete conn;
conn = NULL;
} catch (SQLException sqle) {
// 1. log the exception in some log file.
// 2. throw some app-specific exception or warning based on SQLException.
}
}

Adding RAII for the DB connection is easy, and taking care of logging in
the dtor is equally easy. But dealing with an exception arising from close
is not easy. The connection-managing object could swallow the exception,
but that doesn't seem right. It could translate the exception into a
different error-reporting strategy (e.g., errno), but that loses the
advantages of using exceptions for error reporting (e.g, they can't be
ignored). We could move the call to close out of the dtor and into a
different function that clients would be expected to call manually, but
that would lose the advantages of RAII.

There are a whole host of things we COULD do about combining RAII with
resource release functions that can throw, but I'm interested in what
people actually DO do. FWIW, I checked Herb's library, but I didn't see
any definitive advice on this topic, and some quick googling around didn't
reveal anything, either, but I'm sure this has been hashed over many times.
Feel free to respond with reference to a publication or a URL.

Yours in woeful uninformedness,

Scott

Destructors need to be able to throw. If excepetions are to be the
universal mechanism for reporting exceptional conditions that cause
lower levels of the software to bail, then they need to be uuniverally
available. Any mechanism with exceptions (pun intended) in its
availability is not suitable for use as a general purpose facility in
the language.

I would love to use exceptions in produciton code. But I can't. As
currently designed they don't work.

This particular issue is a reasonably good example of the ugly nooks and
crannies of the language. There are too many special cases that just
don't work in a straightforward manner. Much of this is due to the C
language roots of C++. But a lot of it is due to the fact that the
language is not defined as a standardaization of existing practice. It
is more of a language design exercise, and parts of that design are
simply non-functional.

/tj3


/tj3



[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
White Wolf
Guest





PostPosted: Sun Feb 20, 2005 5:01 am    Post subject: Re: RAII and resource release functions that can throw Reply with quote



Scott Meyers wrote:
Quote:
I have an old question that I, of all people, should know the answer to.
But I don't.

Then the answer must be: It depends. And as I will try to illuminate (yes,
I do read Harry Potter) in this post it later, I believe that the answer in
this case *is*: It depends.

Quote:
C++ uses RAII to avoid leaking resources, and destructors don't throw
because it makes things all icky if they do. No problem, I'm on board.
But then I get questions like this:

What if the destruction of the resource requires some exception
handling, such as logging the exception, translating the exception to
app-specific exception and returns to the caller, etc. For example, for
a db connection, I might want to do: (quite frequently found in similar
JDBC codes in Java world.)

My standard answer is: don't use Java. Smile But I guess one cannot say that
during paid consulting... ;-)

[CODE-SNIPPED]

Quote:
Adding RAII for the DB connection is easy, and taking care of logging in
the dtor is equally easy. But dealing with an exception arising from
close is not easy.

It definitely is not. The reason is, that you are facing a way too vague
question. I have met this question in two forms during my life. It wasn't
usually in C++ (as I recall) but all of such questions has lead to two
solutions, none of which is possibly acceptable for your case, if not, there
is a third one. But let's first see the two "basic" options.

Option one is the smoke-the-weed one: I ask the system to remove a record
(let's suppose I have some sort of ScopeGuard). The system says: there is
no such record. I say (with a touch of the 70ies): whatever. So let's just
forget about it! (Maybe log it, that usually means the same thing). I
wanted to delete it. It is not there. So, I have got what I wanted, didn't
I? ;-)

Option two is the fall-on-your-sword one: So someone comes to me: 'Attila,
you have said that we should check all the erors.', 'And you remembered!'
say I. 'So I have here this call to close, which can return an error. But
what should I do if it does?' This conversation actually has happened. I
have asked the person to look at the documentation (as a last reserve) and
come back tell me *when* will close return an error. The answer was: when
it has got an invalid file handle. The usual reaction is: 'But in my code
it is impossible to have an invalid file handle there.' A quick look at the
code, it is really true. It is as simple as possible, no way to get garbage
or a bad handle. So the answer is: the error should *never* happen. That
is spelled assert in C (and C++). So the answer here is the same as every
case when the impossible is detected: die fast and try to do no more damage
on the way.

However these simple answers may not be good enough for something as complex
as a database connection. First of all let me state it at the beginning
that if your database (or anything else "independent") is not designed to
protect itself (transactional behavior) you are looking at Mr. Disaster, and
driving to the server room at 3am in a snowstorm to restore everything from
backup and happy things like that.

I will continue where you see ###, because it belongs there. But before
that, it is worth to mention that one strategy may not be sufficient to
handle all possible database-close errors. If you get an error saying 'Due
to earlier errors all database connections has been closed and all open
transactions will be rolled back.', you can happily forget about the error.
But if you get a 'Internal error.' or a 'I dunno about that connection.'
error, you are better of killing the process as fast as possible. And if
you are paranoid (as I am) the text to your (operator's) pager will go out
*not* if the system dies with printing some special message, but when it
dies *without* printing that special message. I talk about servers, of
course.

Die is basically what terminate does, so we can achieve that with not
writing a catch. But terminate is a half-graceful shutdown, and it possibly
does too much, so IMNSHO it is better to just simply assert/abort. The
other case (ignoring the non-errors) is very simple (write a catch and eat
it), but IMNSHO it is a good idea to make it a rule: only specific errors
can be ignored, not every possible exception (or large groups of them).

Quote:
The connection-managing object could swallow the exception,
but that doesn't seem right.

I wholeheartedly agree with you on that, that is the reason why I do not
like the idea (which is in the original ScopeGuard sources on the CUJ FTP;
once it will work...) of catching and ignoring errors by default. Some
errors you can and should ignore, because they are not really errors. But
this has to be a conscious decision every time and for every error!

Quote:
It could translate the exception into a
different error-reporting strategy (e.g., errno), but that loses the
advantages of using exceptions for error reporting (e.g, they can't be
ignored). We could move the call to close out of the dtor and into a
different function that clients would be expected to call manually, but
that would lose the advantages of RAII.

###

You don't have to drop RAII completely. But you can delay it. ;-)

I have made code which shows what I talk about, but I guess it is too long
to post here. Anyway, I will tell the "trick" I thought of. For all of
these "special" things (where destruction may really fail outside of the
die/ignore realm) you create a trendy global array variable. Ehem.
Something like that, a simple storage where you can register for cleanup (if
normal RAII fails with an exception and you catch it). You also make your
exception base class destructor call that cleanup function. (Or you make
another, RAII type, which will be in a scope around your try-catch, all of
those which finally handle exceptions.)

Suppose you had an out of memory situation. You could not close your
database connection due to that, but later, when your exception's destructor
runs, the database connection can already be closed. So you retry then. If
it still does not work... I guess you just have to clean up as much as you
can and quit. Nothing will save you from the 3am walk-in, except if you are
the boss so you can blame someone else. ;-)

Quote:
There are a whole host of things we COULD do about combining RAII with
resource release functions that can throw, but I'm interested in what
people actually DO do.

For that, one example is Andrei's ScopeGuard article, where Andrei bets his
bucks on hiding all exceptions. I disagree. :-)

Of course what I wrote above is only my vivid imagination. I did not use
exceptions (yet) in huge quantities, and where I did use, it was simplicity
itself. So although my ideas (except for the do-nothing-and-crash Smile
aren't practice (yet), this is what I would/will do. But only in addition
to making it possible for the independent elements to re-synchronize,
providing transactional behavior - and do some magical design where bringing
one side down will not bring the other side down. But I guess that brings
us to a totally separate topic. Wink And finally we get to where we've
started: you need to have reliability in the system (somewhere) to be able
to make stable code. The requirement on destructors (for exception safety
in C++) is just a manifestation of that.

Quote:
FWIW, I checked Herb's library, but I didn't see
any definitive advice on this topic, and some quick googling around
didn't reveal anything, either, but I'm sure this has been hashed over
many times. Feel free to respond with reference to a publication or a
URL.

If you want me I can also post code (feel free to email me, anyone not only
Scott). I did not post the code because it is long but (I think) trivial
(which means that if it is not, I have many bugs in it Smile.

Quote:
Yours in woeful uninformedness,
--

Yours in woeful uninformadness,
WW aka Attila
:::
It is far more impressive when others discover your good qualities without
your help.



[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]


Back to top
Michael Pryhodko
Guest





PostPosted: Sun Feb 20, 2005 5:06 am    Post subject: Re: RAII and resource release functions that can throw Reply with quote

Quote:
What if the destruction of the resource requires some exception
handling, such as logging the exception, translating the exception
to app-specific exception and returns to the caller, etc.

You should do smth like this:

{
Auto_DB_connection db_conn = ...;

// do anything you need

// everything went fine -- try to close connection explicitly
// could throw
db_conn.close();
}

Auto_DB_connection's destructor should not throw (but can log).

Successful resource release should be the part of 'do anything you
need', if you can not guarantee its success Smile). Sometimes you do not
care -- in this case you could omit explicit release call (for example
closing CloseHandle for mutex)

Bye.
Sincerely yours, Michael.


[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]


Back to top
Roland Pibinger
Guest





PostPosted: Sun Feb 20, 2005 5:14 pm    Post subject: Re: RAII and resource release functions that can throw Reply with quote

On 20 Feb 2005 00:06:50 -0500, "Michael Pryhodko"
<mpryhodko (AT) westpac (DOT) com.au> wrote:

Quote:
What if the destruction of the resource requires some exception
handling, such as logging the exception, translating the exception
to app-specific exception and returns to the caller, etc.

You should do smth like this:

{
Auto_DB_connection db_conn = ...;

// do anything you need

// everything went fine -- try to close connection explicitly
// could throw
db_conn.close();
}

Auto_DB_connection's destructor should not throw (but can log).

Successful resource release should be the part of 'do anything you
need', if you can not guarantee its success Smile). Sometimes you do not
care -- in this case you could omit explicit release call (for example
closing CloseHandle for mutex)

Good point! If you feel the need to handle an exception the destructor
is not the right place for the 'throwing' piece of code. I'd write the
database example in the following way:

void doSomething (Database& db)
{
Auto_DB_connection db_conn (db);
// ... do something

db_conn.commitWork(); // last statment
}
// db_conn.rollbackWork() is called in the destructor of db_conn
// if db_conn.commitWork() is not reached, e.g. due to
// an exception

Best wishes,
Roland Pibinger


[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Andreas Huber
Guest





PostPosted: Sun Feb 20, 2005 5:16 pm    Post subject: Re: RAII and resource release functions that can throw Reply with quote

Michael Pryhodko wrote:
Quote:
What if the destruction of the resource requires some exception
handling, such as logging the exception, translating the exception
to app-specific exception and returns to the caller, etc.

You should do smth like this:

{
Auto_DB_connection db_conn = ...;

// do anything you need

// everything went fine -- try to close connection explicitly
// could throw
db_conn.close();
}

Shouldn't that rather read:

{
Auto_DB_connection db_conn = ...;
try
{
// do anything you need

// everything went fine -- try to close connection explicitly
// could throw
db_conn.close();
}
catch ( ... )
{
db_conn.close();
}
}

?

Regards,

--
Andreas Huber

When replying by private email, please remove the words spam and trap
from the address shown in the header.

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
White Wolf
Guest





PostPosted: Sun Feb 20, 2005 5:21 pm    Post subject: Re: RAII and resource release functions that can throw Reply with quote

Trevor L. Jackson, III wrote:
[SNIP]
Quote:
Destructors need to be able to throw.

They are able to throw.

Quote:
If excepetions are to be the
universal mechanism for reporting exceptional conditions that cause
lower levels of the software to bail, then they need to be uuniverally
available.

They are universally available. As much as they can be. But I am open to
suggestions which show us how C++ could handle more than one exception
(basicaly meaning more than one thread of execution) in one thread of
execution.

Quote:
Any mechanism with exceptions (pun intended) in its
availability is not suitable for use as a general purpose facility in
the language.

Would you please share the reasons behind this opinion (statement)? Why do
you say exceptions have exceptions in their availability? And why do you
think if they have, that stops you from using them effectively? Have you
tried? Or is this all on theoretical level? In my previous post I gave 3
ways to handle the situation Scott has asked about. Two out of which is
*very* simple, and covers most of the cases one meets, at least that is my
feeling. Even if it does not, I gave the 3rd option.

I would also be happy to see your suggestions on how can someone (in any
language) write safe code, if both the commit and the rollback operations
can fail? I believe that (regardless of the programming language of choice)
this is simply impossible. Unless the system does absolutely nothing. But
I think that can already be achieved in C++, although I think it needs to
run in freestanding environment.

Quote:
I would love to use exceptions in produciton code. But I can't. As
currently designed they don't work.

That is a statement which has been proved to be false many years ago.
Unless you have new reasons to share with us. If you do, please do share
them.

Quote:
This particular issue is a reasonably good example of the ugly nooks and
crannies of the language.

I am sorry, but this is the point where I *strongly* need to ask you, to
*please* actually tell something other than your verdict on C++. So far all
I read is flaming dislike (to be PC) towards C++ without a single technical
reasoning or fact to support it.

Quote:
There are too many special cases that just
don't work in a straightforward manner.

Again. Flaming statement, absolutely no reasoning, not even an example.

Quote:
Much of this is due to the C
language roots of C++.

Much of what? Do you mean that C++ is unable to handle two pathes of
execution (two exceptions) at the same time because of C? C has no
exceptions. I fail to see your point.

Quote:
But a lot of it is due to the fact that the
language is not defined as a standardaization of existing practice.

Again, a statement with no technical facts ot back it up. I am not saying
you don't have them, but these statements are just too one-sided to just
drop them in without proving them.

Quote:
It
is more of a language design exercise, and parts of that design are
simply non-functional.

Again: zero technical facts, but a broad statement (or should I say
judgement) on the work of many people. Even if they have done a very bad
job during the years of standardization, don't they deserve the courtesy of
being told the reason why you think what they have made is non-functional?

As for the moderators: I thought that the rule for flame was "if in doubt,
reject". I suspect that my post will be rejected based on that clause. I
just wonder how hard it is to see if a post is nothing else but an outburst
with absolutely no technical facts or reasoning of any kind? We - who work
with C++ - have enough attacks already to handle daily. I for one do not
need any more ammunition for those people who refuse to look at C++ code but
they "know" C++ is a bad choice for a language. Especially not this kind of
ammunition. Since it contains absolutely no reasoning or technical facts
(only the fallacies we have heard for years), it gives unbeatable
ammunition. Why unbeatable? Because it has been approved by the moderators
(and not taken as flame) so it must be true. But there is not a single fact
to be discussed or debated, so there is zero chance to prove that it is
false or that it is way too exaggerated.

--
WW aka Attila
:::
Intel: We put the 'um...' in Pentium.



[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]


Back to top
James Kanze
Guest





PostPosted: Sun Feb 20, 2005 6:22 pm    Post subject: Re: RAII and resource release functions that can throw Reply with quote

Scott Meyers wrote:
Quote:
I have an old question that I, of all people, should know the
answer to. But I don't.

C++ uses RAII to avoid leaking resources, and destructors
don't throw because it makes things all icky if they do. No
problem, I'm on board. But then I get questions like this:

What if the destruction of the resource requires some
exception handling, such as logging the exception,
translating the exception to app-specific exception and
returns to the caller, etc. For example, for a db
connection, I might want to do: (quite frequently found in
similar JDBC codes in Java world.)

Using RAII is quite frequent in the Java world:-)?

Quote:
void doSomething()
{
Connection* conn = NULL;
conn* = createConnection();
// ... do something
try {
if (conn != NULL) {
conn.close();
}
delete conn;
conn = NULL;
} catch (SQLException sqle) {
// 1. log the exception in some log file.
// 2. throw some app-specific exception or warning based on
SQLException.
}
}

Adding RAII for the DB connection is easy, and taking care of
logging in the dtor is equally easy. But dealing with an
exception arising from close is not easy. The
connection-managing object could swallow the exception, but
that doesn't seem right. It could translate the exception
into a different error-reporting strategy (e.g., errno), but
that loses the advantages of using exceptions for error
reporting (e.g, they can't be ignored). We could move the
call to close out of the dtor and into a different function
that clients would be expected to call manually, but that
would lose the advantages of RAII.

There is a simple rule: if an operation might fail, and you need
to react to the error condition, that operation must be done
explicitly, before destruction. Period. No exceptions.

The key, of course, is "and you need to react to the error
condition". Very often, once an error has occured, and an
exception has been thrown further down, you don't need to react
to further error conditions. In my own code, it's not rare for
a class to e.g. close a file in a destructor, ignoring any
errors, but that the "normal" control flow will pass threw an
explicitly called function which closes the file, checking for
errors. In many cases, the the class will memorize the fact
that the file has been explicitly closed, and if the destructor
is invoked without the file having been explicitly closed, it
will also take actions to ensure that the results of a probably
incomplete write are not used (delete the file, write a bad
checksum into the last record, etc.). But the destructor will
never propagate an error out. (Note that the transaction idiom
works very well here. The normal close is in commit, and at
that point, you ensure the transactional integrity, by whatever
means are necessary. Calling the destructor before commit has
finished causes a roll-back, and you'be better make sure that
you can roll-back without any errors if you want to ensure
transactional integrity.)

Quote:
There are a whole host of things we COULD do about combining
RAII with resource release functions that can throw, but I'm
interested in what people actually DO do.

Well, I can only describe what I actually do. This is one case
I can't talk too much for others -- on most of the projects I've
worked on, this sort of thing has been part of my
responsibilities, so I don't know what the others would have
done.

--
James Kanze home: www.gabi-soft.fr
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 pl. Pierre Sémard, 78210 St.-Cyr-l'École, France +33 (0)1 30 23 00 34

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Dietmar Kuehl
Guest





PostPosted: Sun Feb 20, 2005 9:33 pm    Post subject: Re: RAII and resource release functions that can throw Reply with quote

I just want to comment on the non-technical part of this article:

White Wolf wrote:
Quote:
As for the moderators: I thought that the rule for flame was "if in doubt,
reject". I suspect that my post will be rejected based on that clause. I
just wonder how hard it is to see if a post is nothing else but an
outburst
with absolutely no technical facts or reasoning of any kind?

The context set by Scott Meyers provides technical facts (albeit incomplete
and misleading ones: it can be made safe to throw an exception from a
destructor but typically it is avoided due to the necessary extra work and
the fact that normally the two simple approaches you mention are the right
way to go anyway). With this context I don't consider the article to be a
flamebait (it was not a flame at all since no entity was directly attacked,
although the Committee was criticized but also with a reason, namely that
it did not standardization but invention).

Quote:
We - who work
with C++ - have enough attacks already to handle daily. I for one do not
need any more ammunition for those people who refuse to look at C++ code
but they "know" C++ is a bad choice for a language.

We definitely agree on this part. However, the general statement that
destructors cannot throw would indeed be a strong limitation of the language
if it were true (which it is not: the key to use 'uncaught_exception()' to
detect whether it is safe to throw a new exception or whether another
exception also needs to be taken care of): it is not just a plain complaint
about something but it is rooted in a technical concern. Of course, deducing
that exceptions are unusable in C++ due to such a limitation, even if it
were true, is a little bit strong but not necessarily unreasonable.

Quote:
Especially not this kind of
ammunition. Since it contains absolutely no reasoning or technical facts
(only the fallacies we have heard for years), it gives unbeatable
ammunition. Why unbeatable? Because it has been approved by the
moderators (and not taken as flame) so it must be true.

This is not at all the case: although I detect many false statements in
articles, I don't reject them and this is definitely not only true for me
but also for the other moderators! The fact that an article is approved has
nothing to do with the correctness of the statements in the article. All we
judge is whether the article is about C++, is a flame, and a few rather
technical things (we also reject duplicates, trivial or FAQ stuff, excessive
quoting, huge code examples, non-English language, etc.). For the given
article I considered whether it is a flame but decided that there is enough
technical background (by the context given by Scott; as a stand-alone
article without the quoting it would have been rejected as flamebait).

Quote:
But there is not a single
fact to be discussed or debated, so there is zero chance to prove that it
is false or that it is way too exaggerated.

From the above discussion it should be clear that we disagree on this
assessment: there is a statement at the root (that exceptions cannot be

thrown from destructors) which is considered to be a fact. It is wrong but
this is exactly the start for the discussion (or, if it is indeed correct
and I'm wrong about the possibilities based on 'uncaught_exception()' we
might discuss what we should do about the restriction; actually, even if
'uncaught_exception()' can be used as I think it can, it may be reasonable
to discuss whether there should be some mechanism allowing exception
chaining).
--
<mailto:dietmar_kuehl (AT) yahoo (DOT) com> <http://www.dietmar-kuehl.de/>
<http://www.contendix.com> - Software Development & Consulting

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
mark
Guest





PostPosted: Sun Feb 20, 2005 9:36 pm    Post subject: Re: RAII and resource release functions that can throw Reply with quote

Scott Meyers wrote:
Quote:
I have an old question that I, of all people, should know the answer to.
But I don't.

C++ uses RAII to avoid leaking resources, and destructors don't throw
because it makes things all icky if they do. No problem, I'm on board.
But then I get questions like this:

What if the destruction of the resource requires some exception handling,
such as logging the exception, translating the exception to app-specific
exception and returns to the caller, etc. For example, for a db
connection, I might want to do: (quite frequently found in similar JDBC
codes in Java world.)

void doSomething()
{
Connection* conn = NULL;
conn* = createConnection();
// ... do something
try {
if (conn != NULL) {
conn.close();
}
delete conn;
conn = NULL;
} catch (SQLException sqle) {
// 1. log the exception in some log file.
// 2. throw some app-specific exception or warning based on SQLException.
}
}

Adding RAII for the DB connection is easy, and taking care of logging in
the dtor is equally easy. But dealing with an exception arising from close
is not easy. The connection-managing object could swallow the exception,
but that doesn't seem right. It could translate the exception into a
different error-reporting strategy (e.g., errno), but that loses the
advantages of using exceptions for error reporting (e.g, they can't be
ignored). We could move the call to close out of the dtor and into a
different function that clients would be expected to call manually, but
that would lose the advantages of RAII.

There are a whole host of things we COULD do about combining RAII with
resource release functions that can throw, but I'm interested in what
people actually DO do. FWIW, I checked Herb's library, but I didn't see
any definitive advice on this topic, and some quick googling around didn't
reveal anything, either, but I'm sure this has been hashed over many times.
Feel free to respond with reference to a publication or a URL.

If you routinely expect errors, then I wouldn't use exceptions,
instead I would check a return code.

Another example is opening a file. This commonly fails, so I check
the return code rather than throw an exception.

Cheers,
Mark.



[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
White Wolf
Guest





PostPosted: Sun Feb 20, 2005 9:37 pm    Post subject: Re: RAII and resource release functions that can throw Reply with quote

James Kanze wrote:
[SNIP]
Quote:
Using RAII is quite frequent in the Java world:-)?

Possibly as a curse-word. It is 4 letters. :-)

[SNIP]
Quote:
means are necessary. Calling the destructor before commit has
finished causes a roll-back, and you'be better make sure that
you can roll-back without any errors if you want to ensure
transactional integrity.)
[SNIP]


I think this can only be done if either your commit or your rollback is an
"unlikely to fail" operation. In case both of them can fail easily, you
will have trouble, C++ or not, exceptions or not.

--
WW aka Attila
:::
Despite the cost of living, have you noticed how it remains so popular?



[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Trevor L. Jackson, III
Guest





PostPosted: Sun Feb 20, 2005 10:46 pm    Post subject: Re: RAII and resource release functions that can throw Reply with quote

Dietmar Kuehl wrote:

Quote:
I just want to comment on the non-technical part of this article:

[snip]

Quote:
We definitely agree on this part. However, the general statement that
destructors cannot throw would indeed be a strong limitation of the language
if it were true (which it is not: the key to use 'uncaught_exception()' to
detect whether it is safe to throw a new exception or whether another
exception also needs to be taken care of): it is not just a plain complaint
about something but it is rooted in a technical concern. Of course, deducing
that exceptions are unusable in C++ due to such a limitation, even if it
were true, is a little bit strong but not necessarily unreasonable.

The paragraph above makes certain statmes that are fundamentally
technical rather than non-technical. So I am taking the libery of
taking them out of the original context set by the introductory statement.

I dispute the claim that uncaught_exception() is a sufficient foundation
on which to solve (in some sense of the term "solve") the issue of
colliding exceptions.

For example, given an exception from a low level function, say bad_alloc
or something else that is exceptional, but not fatal, the superposed
layers of software would be unwound until some layer is reached that has
declared its intention to deal with a set of exceptions including
bad_alloc (or whatever).

The problem appears when an intermediate layer of the software detects
an exceptional condition that is of higher importance than the exception
currently pending. In that situation the intervening layer can either
ignore the important exception or force termination. That is usually an
unacceptable choice.

Would anyone care to work on a system where the interrupts were arranged
such that a higher priority interrupt was faced with the choice of being
ignored to forcing a reset (and thus a reboot) of the machine? Of
course not. Analogies are slippery, but I don't think this one is a
distortion. From the perspective of the higher level software an
exception is a kind of interruption in the normal flow of control, but
it gets handled and the normal flow of control resumes.

Interrupts achieve this transparency by nesting or queueing. Exceptions
don't. In the absence of neither nesting nor queueing I suggest that
replacement based on a precedence hierarchy is the least functionality
on which a robust system could depend.

[snip]

Quote:
From the above discussion it should be clear that we disagree on this
assessment: there is a statement at the root (that exceptions cannot be
thrown from destructors) which is considered to be a fact. It is wrong but
this is exactly the start for the discussion (or, if it is indeed correct
and I'm wrong about the possibilities based on 'uncaught_exception()' we
might discuss what we should do about the restriction; actually, even if
'uncaught_exception()' can be used as I think it can, it may be reasonable
to discuss whether there should be some mechanism allowing exception
chaining).

Clearly an application could build an exception framework that would
allow chaining or queueing and even impose a priority or filtering
scheme upon the collection of pending exceptions. But the only argument
against moving that capability out of the application and into the
language (or libraries) would be an overhead issue (probably both space
and time). But in almost every case that overhead is avoidable. In the
few cases where it is unavoidable it can probably (more theory than fact
here) be justified on the basis of reliability.

/tj3

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]


Back to top
David B. Held
Guest





PostPosted: Sun Feb 20, 2005 10:47 pm    Post subject: Re: RAII and resource release functions that can throw Reply with quote

Scott Meyers wrote:
Quote:
[...]
There are a whole host of things we COULD do about combining RAII with
resource release functions that can throw, but I'm interested in what
people actually DO do. FWIW, I checked Herb's library, but I didn't see
any definitive advice on this topic, and some quick googling around didn't
reveal anything, either, but I'm sure this has been hashed over many times.
Feel free to respond with reference to a publication or a URL.
[...]

Well, the main problem is stated in this link:

http://kolpackov.net/projects/c++/eh/dtor-1.xhtml

The C++ exception-handling and destruction mechanism is 1-fault
failfast for dense faults. However, a failing close operation
that must follow a failing member operation can only be gracefully
handled by a 2-fault or better failfast system. Basically, we
would need to be allowed to have two active d'tors at once. If
the close failed after all the other operations succeeded, all is
well. You could throw from the d'tor because it would be the only
active exception. However, if the resource was dynamically allocated,
you have the little problem of its storage not being properly
reclaimed. In a first-order RAII scenario, that's not a problem,
because it is stack-allocated. In a second-order RAII, where the
resource spans multiple block scopes and is managed by a smart
pointer, you have a problem.

You probably don't want to require your RAII object to always
be stack-allocated, so any kind of d'tor-throwing scenario can
be eliminated right away. But that still doesn't address the
fundamental issue, which is that you want to have two active
exceptions at the same time. It would be interesting to contemplate
what C++ would look like if it supported such a thing, but since
it doesn't, we have to decide what behavior we want as the way
C++ is now. That means that when a connection operation fails,
causing close() to fail, we have to decide whether we want to
propagate the original exception or the close() exception, or
create a new exception that merges information about both of them.

Now, if we choose the first option, we can just fail to propagate
close() exceptions (after logging them), but that still leaves us
with the case where only close() itself fails. There you probably
want to propagate the exception, but you don't want to do it from
a d'tor. The second case is pretty much impossible to implement
in C++. The third option is as hard as the second, which means
that we can effectively eliminate it. Which means that if we
really really want an exception to be reported when close() fails
by itself, we have to provide a member function that can be invoked
manually. The d'tor can call that function and suppress the
exception, but I don't see any other logical response to this
problem.

That mean's that James Kanze's answer seems like the only sane
one, except that transactional behavior really has nothing to
do with close() failing. If you are at the end of doing a commit,
and the commit process itself fails at the very end when you close()
the connection, odds are that it is not going to be possible to
rollback anyway. The best you can do is let the close() exception
propagate up the stack and hope for the best. The interesting
thing is that a failure in close() might not represent a failure
of the operation as a whole. It could be that the operation
succeeded, and a network connection to the db was broken
unexpectedly before close() was called, and close() reports an
unexpected connection state as an exception. In that case, you
wouldn't even want to do a rollback even if you still could. So
it really depends on how close() can fail, and what that means
for your application.

Even though it seems like you lose the benefit of RAII by having
to manually call close(), I see it as merely registering that
you want the result of an operation performed by the d'tor.
Since the d'tor will call close() anyway, you still get the EH
benefits of RAII, up to the point that the system is able to
provide.

Dave

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Victoria Dassen
Guest





PostPosted: Sun Feb 20, 2005 10:47 pm    Post subject: Re: RAII and resource release functions that can throw Reply with quote

Andreas Huber wrote:
Quote:
Michael Pryhodko wrote:

What if the destruction of the resource requires some exception
handling, such as logging the exception, translating the exception
to app-specific exception and returns to the caller, etc.

You should do smth like this:

{
Auto_DB_connection db_conn = ...;

// do anything you need

// everything went fine -- try to close connection explicitly
// could throw
db_conn.close();
}


Shouldn't that rather read:

{
Auto_DB_connection db_conn = ...;
try
{
// do anything you need

// everything went fine -- try to close connection explicitly
// could throw
db_conn.close();
}
catch ( ... )
{
db_conn.close();
}
}

?

Regards,

Better yet, try ---> (Rigor Mortis Saloon "Come in and get stiff")[1]

Auto_DB_connection::~Auto_DB_connection()
{
close(); // won't throw
release(); // same idea, or rely on auto_ptr, shared_ptr
// or some other RAII class to delete or otherwise
// give back resources used by m_connection and get
// its (non-throwing) destructor to run
// do whatever else that doesn't throw, too
// my data member dtors better not throw, either Smile
}

void
Auto_DB_connection::close() throw ()
{
try
{
// preamble
m_connection.close();
// postamble ( or what-*ever*! )
}
catch ( const DBException& error )
{
// handle it
}
// ...more handlers as needed
catch ( ... )
{
// handle anything
}
}

[1] I'll be real impressed by anyone who gets *that* reference Smile,
Shania Twain not withstanding :)

Victoria

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Andreas Huber
Guest





PostPosted: Sun Feb 20, 2005 10:48 pm    Post subject: Re: RAII and resource release functions that can throw Reply with quote

Andreas Huber wrote:
[snip]
Quote:
Shouldn't that rather read:
[snip]


I hit the send button too soon, please disregard this message...

Regards,

--
Andreas Huber

When replying by private email, please remove the words spam and trap
from the address shown in the header.

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Post new topic   Reply to topic    C++Talk.NET Forum Index -> C++ Language (Moderated) All times are GMT
Goto page 1, 2, 3, 4, 5, 6, 7, 8, 9, 10  Next
Page 1 of 10

 
 


Powered by phpBB © 2001, 2006 phpBB Group