The basic issue is that, from the programmer's perspective, an
exception throw is an atomic event which has a duration that can only
be indirectly controlled.
The duration of any particular throw is serially deterministic - it
depends only on the difference in call depth and the number of frame
local objects that have non-trivial destructors. These are both fixed
for any particular throw/catch pairing (assuming no recursion).
However the only way to control the duration is by manipulating those
parameters - call depth and number of non-trivial objects. This can
lead to a lot of refactoring to duplicate functions or move objects
higher into the call chain and then pass them around. It can be
particularly troublesome if the functions involved are shared heavily.
The atomicity of the throw is the more serious issue. Many real time
systems perform multiple tasks but do not use preemptive scheduling.
Many systems are designed as cooperative taskers, co-routines, or as
some kind of state machine. A lengthy throw which occurs at the wrong
moment could result in a failure by delaying execution of a more
important operation.
Before someone objects that the return from a function with many local
objects to destruct may be equally lengthy, and that the programmer
similarly has no control over it ... Yes. But the point is that
exceptions may involve multiple call frames which increases the
likelihood that more objects will be involved. In the normal return
scenario the programmer would regain control in each frame and could
decide what to do next - in the exception scenario the programmer is
locked out until all the intervening frames are destructed.
Additionally, function calls and returns are natural scheduling points
which the RT programmer is used to considering, whereas exceptions are
more easily overlooked as scheduling points because their timing
effects depend on how many call frames they traverse.
There is also the [diminishing] danger that programmers moving from C
to C++ may mentally equate exceptions with longjmp, which is normally
a constant time operation regardless of call depth.
Also certain types of programs depend on the ability to hop around at
will. When used purely for upward continuations, longjmp and
exceptions are functionally equivalent modulo destructor calls. But
setjmp/longjmp can also be used for general continuations. Exceptions
can't, and C++ doesn't provide any object safe construct or standard
library function that can.
[Yes ... I know that objects and longjmp can be safely mixed if you
are very careful.]
-
Exception atomicity is not an issue for a preemptive tasking system
and it may eventually cease to be an issue going forward when
functionality demands increase to the point where preemptive threading
is the only viable solution. However, I expect to continue to see
systems designed without it for a while yet because certified RTOSes
and tasking kernels remain expensive and most managers are
[rightfully] wary of liability arising from a home built solution.
And, of course, exceptions can be tamed through coding practices.
However, the effect of tightly controlling them results either in
programs which are written unnaturally (using TLS perhaps) or in
restricting them to protecting, at most, a single level of function
call so that their effect is no more than a normal function return.
Ultimately it does come down to the programmer knowing his tools. My
own concern, exemplified by this thread, is that a lot of people are
doing things they probably shouldn't be, in unfamiliar contexts and
with tools they don't fully understand.
George