[Python-Dev] setjmp/longjmp exception handling (was: More
informative error messages)
Brett C.
bac at OCF.Berkeley.EDU
Thu Oct 23 02:48:03 EDT 2003
Tim Peters wrote:
<SNIP>
> An internal PyExc_AttributeError isn't the same as a user-visible
> AttributeError, though -- a class instance isn't created unless and until
> PyErr_NormalizeException() gets called because the exception needs to be
> made user-visible. If the latter never happens, setting and clearing
> exceptions internally is pretty cheap (a pointer to the global
> PyExc_AttributeError object is stuffed into the thread state). OTOH, almost
> every call to a C API function has to test+branch for an error-return value,
> and I've often wondered whether a setjmp/longjmp-based hack might allow for
> cleaner and more optimizable code (hand-rolled "real exception handling").
>
For some odd reason (maybe because of all the code touch-ups I did to
Python/ast.c in the AST branch), the idea of doing exception handling in
C using setjmp/longjmp really appealed to me.
So, being a programmer with an itch that needed to be scratched, I came
up with a possible solution. Even if the idea would work (which I don't
know if it will just because I am not sure how thread-safe it is nor if
the code will work; this was a mental exercise that doesn't compile
because of casting of jmp_buf and such), I doubt it will ever be
incorporated into Python just because it would require so much change to
the C code. But hey, who knows.
The basic idea is to keep a stack of jmp_buf points. They are pushed on
to the stack when a chunk of code wants to handle an exception. The
basic code is in the function try_except(); have an 'if' that calls a
function that pushes on to the stack a new jmp_buf and register it in
the conditional check. When an exception is raised a function is called
(makejmp()) that pops the stack and jumps to the jmp_buf that is popped.
Continue until the last item on the stack is reached which should be
PyErr_NormalizeException() (I think that is the function that exposes an
exception to Python code).
I have no clue how much performance benefit/loss there would be from
this, but code would be cleaner since you wouldn't have to do constant
``if (fxn() == NULL) return NULL;`` checks for raised exceptions.
But in case anyone cares, here is the *very* rough C code:
#include <stddef.h>
#include <setjmp.h>
/* Basically just a stack item */
typedef struct jmp_stack_item_struct {
jmp_buf jmp_point;
struct jmp_stack_item_struct *previous;
} jmp_stack_item;
/* Global stack of jmp points to exception handlers */
jmp_stack_item *jmp_stack;
void
try_except(void)
{
jmp_stack = NULL;
/* try: */
if (!setjmp(allocjmp())) {
;
}
/* except: */
else {
;
}
}
/* returning jmp_buf like this makes gcc unhappy since it is an array */
jmp_buf
allocjmp(void)
{
/* malloc jmp_buf and put on top of stack */
/* return malloc'ed jmp_buf */
jmp_stack_item *new_jmp = (jmp_stack_item *)
malloc(sizeof(jmp_stack_item));
if (!jmp_stack) {
new_jmp->previous = NULL;
}
else {
new_jmp->previous = jmp_stack;
}
jmp_stack = new_jmp;
return new_jmp->jmp_point;
}
void
raise(void)
{
/* Exception set; now call... */
makejmp();
}
void
makejmp(void)
{
jmp_stack_item *top_jmp = jmp_stack;
jmp_buf jmp_to;
if (!jmp_stack->previous)
longjmp(jmp_stack->jmp_point, 1);
else {
memmove(jmp_to, top_jmp->jmp_point, sizeof(jmp_to));
jmp_stack = top_jmp->previous;
free(top_jmp);
longjmp(jmp_to, 1);
}
}
More information about the Python-Dev
mailing list