/**
@page GRAS_tut_tour_exceptions Lesson 8: Handling errors through exceptions
\section GRAS_tut_tour_exceptions_toc Table of Contents
- \ref GRAS_tut_tour_exceptions_intro
- \ref GRAS_tut_tour_exceptions_use
- \ref GRAS_tut_tour_exceptions_recap
\section GRAS_tut_tour_exceptions_intro Introduction
Exceptions are a great mecanism to deal with error exception, everyone knows
that.
Without exceptions, you have to rely on returning a value indicating whether
the function call were right or not, and check the return values of every
single function you call. If there is one point in the calling sequence
where your forgot this check, the chain is broken and caller won't notice
the issue. In practice, dealing with error without exceptions loads user
code with *tons* of those stupid checks and you loose your functional code
in the middle of that miasm.
With them, you simply write your code. If you want to deal with errors (ie,
you actually know how to react to errors at this point of your code), you
write a catching block. If you don't, you don't. And exceptions flow through
from trowing point to catching point without bothering you.
At this point, you may be a bit surprised by the previous paragraphs.
SimGrid and GRAS are written in C, and everybody knows that there is no
exception in C but only in C++, Java and such. This is true, exceptions are
not part of the C language, but this is such a great tool that we
implemented an exception mecanism as part of the SimGrid library (with
setjmp and longjmp, for the curious).
Being "home-grown" make our exception mecanic both stronger and weaker at
the same time. First it is weaker because, well, we are more limitated
within the library as we are than if we could change the compiler itself to
add some extra checks here and specific treatment there. But it is also a
advantage for us, since the exception mecanism is perfectly fitted to the
distributed settings of GRAS processes. They can easily propagate on the
net, as we will see in the next lesson (\ref GRAS_tut_tour_rpc) and contain
information about the host on which they were thrown (#xbt_ex_t) along with
the thrown point in the source code.
The syntax of XBT exceptions should not sound unfamilliar to most of you.
You throw them using the #THROW0...#THROW7 macros. They take 2 extra
arguments in addition to the format and its self arguments: an error
category (of type #xbt_errcat_t) and an error "value" (an integer;
pratically, this is often left to 0 in my own code). So, you may have
something like the following:
\verbatim THROW3(system_error, 0, "Cannot connect to %s:%d because of %s", hostname, port, reason);\endverbatim
Then, you simply add a #TRY/#CATCH block around your code:
\verbatim TRY{
/* your code */
} CATCH(e) {
/* error handling code */
} \endverbatim
Another strange thing is that you should actually free the memory allocated
to the exception with xbt_ex_fres() if you manage to deal with them. There
is a bit more than this on the picture (#CLEANUP blocks, for example), and
you should check the section \ref XBT_ex for more details.
You should be very carfull when using the exceptions. They work great
when used correctly, but there is a few golden rules you should never break.
Moreover, the error messages and symptom can get really crude when
misusing the exceptions.
- Do not move out of a TRY block with a return, a break or any other
kind of jump. NEVER. EVER.. This is the most tempting error, and this
drives the system nuts. You will probably segfault in the next exception
raising, far away from where you incidentally typed return
(this is because there is some cleanups to do at the end of a TRY block,
you cannot just leave it).
- Play safe with variables modified in the TRY block. You may want
to mark them as volatile, which is a modifier (just like
const) indicating that the value of the denoted variable may get
changed by external parts of the program during the run. This is the case
when your data gets modified by an exception raising function, I guess.
So, as you can see, you don't want to include large sections of your program
in TRY blocks. If you do so, it's quite sure that one day, you'll do a break
or a return within this block. And believe me, finding such typos is a real
pain.
If you are suspecting this kind of error, I made a little script for you:
check tools/xbt_exception_checker from the CVS. Given a set of C
files, it extracts the TRY blocks and display them on the standard output so
that you can grep for return, break and such forbidden
words.
\section GRAS_tut_tour_exceptions_use Putting exceptions into action
Okay. I hope those little warnings didn't discourage you from using the
exceptions, because they really are a nice mecanism. We will now change a
bit our program to take advantage of them. The only issue is that when a
program run properly, it usually don't raise any exception. We could protect
the calls we already have with exception handling, but it wouldn't be really
exciting since we know this code does not throw any exception under the
condition we use (actually, most of the GRAS functions may throw exception
on problem).
Instead, we will code a little game between the client and the server. We
won't tell the client the exact port on which the server listen, and it will
have to find from itself. For this, it will try to open socket and send the
kill message to each ports of the search range. If it manage to close the
socket after sending the message without being interrupted by an exception,
it can assume that it killed the server and stop searching.
\dontinclude 08-exceptions.c
\skip port=3000
\until end_of_loop
To make the game a bit more fun (and to show you what an exception actually
look like when it's not catched), we add a potential command line argument
to the server, asking it to cheat and to not open its port within the search
range but elsewhere:
\dontinclude 08-exceptions.c
\skip strcmp
\until gras_socket_my_port
\until }
Then, when the client detects that it didn't manage to find&destroy the
server, it throws a suicide exception (sorry for the bad jokes):
\skip if(!found)
\until THROW
\section GRAS_tut_tour_exceptions_recap Recapping everything together
Here is the output produced by this new program. Note that when the program
bails out because of an uncatched exception, it displays its backtrace just
like a JVM would do (ok, it'a a bit cruder than the one of the JVM, but
anyway). For each function frame of the calling stack, it displays the
function name and its location in the source files (if it manage to retrieve
it). Don't be jalous, you can display such stacks wherever you want with
xbt_backtrace_display() ;)
Unfortunately, this feature is only offered under Linux for now since I have
no idea of how to retrieve the call stack of the current process under the
other operating systems. But help is always welcome in this area too ;)
\include 08-exceptions.output
The complete program reads:
\include 08-exceptions.c
Go to \ref GRAS_tut_tour_simpledata
*/