Exception propagation

I

ittium

Group,

I have a situation where some of the class/non-class methodsmake many
levels of function calls, some of the function in this call hierarchy
may throw exception, what is a good idea

#1 inner function rethrow (by empty throw), such that exception is
propagated back to the top level function.

#2inner function does not throw (by empty throw), this exception will be
by **default** propagated back to the top level routine (assuming
intermediate routines are not catching the exception).

#3 When it is a good idea that function(inner) receiving the exception
or some intermediate function catch it and from there error event is
propagated back through some error code.

thanks
Ittium
 
I

Ian Collins

Group,

I have a situation where some of the class/non-class methodsmake many
levels of function calls, some of the function in this call hierarchy
may throw exception, what is a good idea

#1 inner function rethrow (by empty throw), such that exception is
propagated back to the top level function.

#2inner function does not throw (by empty throw), this exception will be
by **default** propagated back to the top level routine (assuming
intermediate routines are not catching the exception).

#3 When it is a good idea that function(inner) receiving the exception
or some intermediate function catch it and from there error event is
propagated back through some error code.


The first place where it makes sense to catch the exception catches it
and performs the appropriate recovery actions. if there isn't an
appropriate place, catch it in main, report and exit.
 
G

Goran

Group,

I have a situation where some of the  class/non-class methodsmake many
levels of function calls, some of the function in this call hierarchy
may throw exception, what is a good idea

#1 inner function rethrow (by empty throw), such that exception is
propagated back to the top level function.

#2inner function does not throw (by empty throw), this exception will be
by **default** propagated back to the top level routine (assuming
intermediate routines are not catching the exception).

#3 When it is a good idea that function(inner) receiving the exception
or some intermediate function catch it and from there error event is
propagated back through some error code.

In C++ code, there's no good reason to ever STOP exception propagation
AND replace it with return code, except e.g. in main. Or perhaps when
you are writing a module that should be exposed to a language that
can't receive C++ exceptions.

Further, there are RARE reasons to stop exception propagation. Off the
top of my head, some of those rare ones are:

* main() - you seldom want want terminate() to kick in, which is what
will happen if exception escapes main()
* for similar reason, any top-level thread function
* to re-throw existing exception, but with further info about the
error (e.g. "bad_alloc happened when fryding the swoorf"). This is
because lowest-level exceptions have no further info about the context
in which they happened, and that is useful, and C++ has no call stack
in exception objects ;-).
* there's a loop whose whole body can fail, but next iteration still
should happen, usually after reporting the error. Example: a server-
kind-of-a-program, who has a client serving loop, and serving one
client request failed.

You seem to be trying to make a rule of thumb here, but without the
context. That is seldom a good idea. It's better to ask "what should I
do" from a given context.

Goran.
 
C

Christopher

In C++ code, there's no good reason to ever STOP exception propagation
AND replace it with return code, except e.g. in main. Or perhaps when
you are writing a module that should be exposed to a language that
can't receive C++ exceptions.

Further, there are RARE reasons to stop exception propagation. Off the
top of my head, some of those rare ones are:

* main() - you seldom want want terminate() to kick in, which is what
will happen if exception escapes main()
* for similar reason, any top-level thread function
* to re-throw existing exception, but with further info about the
error (e.g. "bad_alloc happened when fryding the swoorf"). This is
because lowest-level exceptions have no further info about the context
in which they happened, and that is useful, and C++ has no call stack
in exception objects ;-).
* there's a loop whose whole body can fail, but next iteration still
should happen, usually after reporting the error. Example: a server-
kind-of-a-program, who has a client serving loop, and serving one
client request failed.

You seem to be trying to make a rule of thumb here, but without the
context. That is seldom a good idea. It's better to ask "what should I
do" from a given context.

Goran.- Hide quoted text -

- Show quoted text -


Catching an exception at the top level function of a thread is not so
rare in my opinion and very often overlooked.
I've actually made it a point to examine thread entry points now, and
make sure everything is caught and handled in some way.
 
E

Ebenezer

In C++ code, there's no good reason to ever STOP exception propagation
AND replace it with return code, except e.g. in main. Or perhaps when
you are writing a module that should be exposed to a language that
can't receive C++ exceptions.

Further, there are RARE reasons to stop exception propagation. Off the
top of my head, some of those rare ones are:

* main() - you seldom want want terminate() to kick in, which is what
will happen if exception escapes main()
* for similar reason, any top-level thread function
* to re-throw existing exception, but with further info about the
error (e.g. "bad_alloc happened when fryding the swoorf"). This is
because lowest-level exceptions have no further info about the context
in which they happened, and that is useful, and C++ has no call stack
in exception objects ;-).
* there's a loop whose whole body can fail, but next iteration still
should happen, usually after reporting the error. Example: a server-
kind-of-a-program, who has a client serving loop, and serving one
client request failed.

Well that last one is hardly rare. Most programs today are
servers, I think.


I stop the propagation in the following for performance
reasons:

cmw_request
getRequest (uint8_t byteOrder
, sock_type sock)
{
static ReceiveBuffer<SameFormat> localbuf(4096);
#ifdef ENDIAN_BIG
static ReceiveBuffer<LeastSignificantFirst> otherlocalbuf(4096);
#else
static ReceiveBuffer<MostSignificantFirst> otherlocalbuf(4096);
#endif

uint8_t clientFormat;
Read(sock, &clientFormat, 1);
try {
if (clientFormat == byteOrder) {
localbuf.sock_ = sock;
while (!localbuf.GotPacket()) ;
return cmw_request(localbuf);
} else {
otherlocalbuf.sock_ = sock;
while (!otherlocalbuf.GotPacket()) ;
return cmw_request(otherlocalbuf);
}
} catch :):std::exception const& ex) {
clientFormat == byteOrder ? localbuf.Reset() :
otherlocalbuf.Reset();
throw;
}
}

The exception catching allows me to make some variables
static that would otherwise be allocating and releasing
memory from the heap each time the function is called.
It is kind of experimental, but so far so good.

The above function is from this file --
http://webEbenezer.net/misc/cmwAmbassador.cc.


Brian Wood
Ebenezer Enterprises
http://webEbenezer.net
 
G

Goran

Well that last one is hardly rare.  Most programs today are
servers, I think.

I stop the propagation in the following for performance
reasons:

cmw_request
getRequest (uint8_t byteOrder
            , sock_type sock)
{
  static ReceiveBuffer<SameFormat> localbuf(4096);
#ifdef ENDIAN_BIG
  static ReceiveBuffer<LeastSignificantFirst> otherlocalbuf(4096);
#else
  static ReceiveBuffer<MostSignificantFirst> otherlocalbuf(4096);
#endif

  uint8_t clientFormat;
  Read(sock, &clientFormat, 1);
  try {
    if (clientFormat == byteOrder) {
      localbuf.sock_ = sock;
      while (!localbuf.GotPacket()) ;
      return cmw_request(localbuf);
    } else {
      otherlocalbuf.sock_ = sock;
      while (!otherlocalbuf.GotPacket()) ;
      return cmw_request(otherlocalbuf);
    }
  } catch :):std::exception const& ex) {
    clientFormat == byteOrder ? localbuf.Reset() :
otherlocalbuf.Reset();
    throw;
  }

}

I don't think you should count this as "stopping propagation", as you-
re-throw.

You should further consider using Alexandrescu/Martinean's excellent
scope guard for such things. That would give you e.g.

cmw_request
getRequest (uint8_t byteOrder
, sock_type sock)
{
static ReceiveBuffer<SameFormatlocalbuf(4096);
#ifdef ENDIAN_BIG
static ReceiveBuffer<LeastSignificantFirstotherlocalbuf(4096);
#else
static ReceiveBuffer<MostSignificantFirstotherlocalbuf(4096);
#endif

uint8_t clientFormat;
Read(sock, &clientFormat, 1);
ON_BLOCK_EXIT_OBJ(byteOrder ? localbuf : otherlocalbuf,
&buffer_class::Reset());
if (clientFormat == byteOrder) {
localbuf.sock_ = sock;
while (!localbuf.GotPacket()) ;
return cmw_request(localbuf);
} else {
otherlocalbuf.sock_ = sock;
while (!otherlocalbuf.GotPacket()) ;
return cmw_request(otherlocalbuf);
}

+1 for Paavo's remark. Use RAII, that gets you more time for good
things in life ;-).

Goran.
 
E

Ebenezer

This is just lack of RAII. Why don't you just put a Reset() call in the
destructor of ReceiveBuffer?

I think you may have a point(s) in the other thread and I'll
have to do some digging on that later. So it seems easier to
reply here.
I'm intentionally avoiding RAII here in order
to reuse resources. I don't think calling Reset in the dtor
would help. (I can't think of other places where I avoid
RAII.)
 
G

Goran

I think you may have a point(s) in the other thread and I'll
have to do some digging on that later.  So it seems easier to
reply here.
I'm intentionally avoiding RAII here in order
to reuse resources.  I don't think calling Reset in the dtor
would help.  (I can't think of other places where I avoid
RAII.)

Perhaps Paavo was a bit too quick with his suggestion... You don't
__need__ to destroy ReceiveBuffer. You can merely have an object whose
destruction calls Reset() on the buffer (Reset() should be a no-fail
operation, which seems a good guess). So you have best of both world:
resource reuse that you hold dear, and application of RAII for simpler
code.

That said, it looks like you could get your resource reuse by changing
ReceiveBuffer to receive the buffer it operates on from the outside.
(I am guessing that the expensive resource is said buffer that is on
the heap, and that Reset is dirt-cheap otherwise.)

Note: function you've shown isn't thread-safe due to the use of the
static. I don't know if it matters, but if it does, you should look
for thread-local storage on your platform.

Goran.
 
E

Ebenezer

Perhaps Paavo was a bit too quick with his suggestion... You don't
__need__ to destroy ReceiveBuffer. You can merely have an object whose
destruction calls Reset() on the buffer (Reset() should be a no-fail
operation, which seems a good guess). So you have best of both world:
resource reuse that you hold dear, and application of RAII for simpler
code.

That said, it looks like you could get your resource reuse by changing
ReceiveBuffer to receive the buffer it operates on from the outside.
(I am guessing that the expensive resource is said buffer that is on
the heap, and that Reset is dirt-cheap otherwise.)

That's correct. Thanks for the reminder about scope guard.
I have to think about that and your arguments here further.
Note: function you've shown isn't thread-safe due to the use of the
static. I don't know if it matters, but if it does, you should look
for thread-local storage on your platform.

Yes, I'm aware of that. The program is currently single-threaded.
I've thought about the possibility of makings it use two threads.
(It is the middle tier of a 3-tier system.) One thread would handle
requests from the front tier and the other thread would handle
responses from the back tier. I don't think changing the program in
that way would be a problem as far as the statics in this function.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,743
Messages
2,569,478
Members
44,899
Latest member
RodneyMcAu

Latest Threads

Top